# OpenGL

# Basic OpenGL in Qt Creator

  1. Create a widget project.
  2. At the UI drag and drop an OpenGL Widget and adjust the layout.
  3. at CMkae file include:

set(CMAKE_CXX_STANDARD 17)

and

find_package(OpenGL)

and 

target_link_libraries(... ${OPENGL_LIBRARIES})
1
2
3
4
5
6
7
8
9
10
  1. right click on the project name and add new C++ Class. Choose and insert the class name like MyGLWidget, copy the class name; Base class select <Custom> and QOpenGLWidget. Include QWidget and Add Q_OBJECT.

OpenGL Class Setup

  1. include the files on the CMake:





 
 


  add_executable(OpenGL_BoilerPlate
    main.cpp
    widget.cpp
    widget.h
    widget.ui
    myglwidget.cpp
    myglwidget.h
  )
1
2
3
4
5
6
7
8
  1. on the myglwidget.h include










 




#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QOpenGLWidget>
#include <QWidget>

class MyGLWidget : public QOpenGLWidget
{
    Q_OBJECT
public:
    MyGLWidget(QWidget* parent=nullptr);
};

#endif // MYGLWIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  1. At the myglwidget.cpp include:


 





#include "myglwidget.h"

MyGLWidget::MyGLWidget(QWidget* parent):
    QOpenGLWidget(parent)
{

}
1
2
3
4
5
6
7
  1. include #include <QOpenGLWidget> #include <QOpenGLFunctions> at the head file.



 
 











#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>

class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
    Q_OBJECT
public:
    MyGLWidget(QWidget* parent=nullptr);
};

#endif // MYGLWIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  1. add the functions:















 


 




#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>

class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
    Q_OBJECT
public:
    MyGLWidget(QWidget* parent=nullptr);
    
protected:
    //It is a virtual function
    void initializeGL() override;
    
    //It is also a virtual function
    void paintGL() override;
};

#endif // MYGLWIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  1. complete the function with code from here. Don't forget to #include <QSurfaceFormat>
#include "myglwidget.h"


MyGLWidget::MyGLWidget(QWidget* parent):
    QOpenGLWidget(parent)
{
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 2);
    format.setProfile(QSurfaceFormat::CoreProfile);
    QSurfaceFormat::setDefaultFormat(format);
}

void MyGLWidget::initializeGL()
{
    this->initializeOpenGLFunctions();
}

void MyGLWidget::paintGL()
{
    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  1. Paste some code to paintGL():
#include "myglwidget.h"


MyGLWidget::MyGLWidget(QWidget* parent):
    QOpenGLWidget(parent)
{
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 2);
    format.setProfile(QSurfaceFormat::CoreProfile);
    QSurfaceFormat::setDefaultFormat(format);
}

void MyGLWidget::initializeGL()
{
    this->initializeOpenGLFunctions();
}

void MyGLWidget::paintGL()
{
    glClearColor(1.0f, 1.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glBegin(GL_QUADS);
    
    glColor3f(1.0f, 0.0f, 1.0f);
    
    glVertex2f(-0.5f, -0.5f);
    glVertex2f( 0.5f, -0.5f);
    glVertex2f( 0.5f,  0.5f);
    glVertex2f(-0.5f,  0.5f);
    
    glEnd();
    
    glFlush();   
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  1. Change the OpenGL Widget, right click and promote to, on Promoted Class name write MyGLWidget, add and Promote

OpenGL Class Setup

This should be the result:


OpenGL Class Setup

myglwidget.h

#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QWidget>
#include <QSurfaceFormat>

class MyGLWidget : public QOpenGLWidget, public QOpenGLFunctions
{
    Q_OBJECT
public:
    MyGLWidget(QWidget* parent=nullptr);

protected:
    //It is a virtual function
    void initializeGL() override;

    //It is also a virtual function
    void paintGL() override;
};

#endif // MYGLWIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

myglwidget.cpp

#include "myglwidget.h"


MyGLWidget::MyGLWidget(QWidget* parent):
    QOpenGLWidget(parent)
{
    QSurfaceFormat format;
    format.setDepthBufferSize(24);
    format.setStencilBufferSize(8);
    format.setVersion(3, 2);
    format.setProfile(QSurfaceFormat::CoreProfile);
    QSurfaceFormat::setDefaultFormat(format);
}

void MyGLWidget::initializeGL()
{
    this->initializeOpenGLFunctions();
}

void MyGLWidget::paintGL()
{
    glClearColor(1.0f, 1.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glBegin(GL_QUADS);

    glColor3f(1.0f, 0.0f, 1.0f);

    glVertex2f(-0.5f, -0.5f);
    glVertex2f( 0.5f, -0.5f);
    glVertex2f( 0.5f,  0.5f);
    glVertex2f(-0.5f,  0.5f);

    glEnd();

    glFlush();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

CMake

cmake_minimum_required(VERSION 3.5)

project(OpenGL_BoilerPlate LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.

#if(ANDROID)
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
#    if (ANDROID_ABI STREQUAL "armeabi-v7a")
#        set(ANDROID_EXTRA_LIBS
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
#    endif()
#endif()

find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)
find_package(Qt5OpenGL)
find_package(OpenGL)



if(ANDROID)
  add_library(OpenGL_BoilerPlate SHARED
    main.cpp
    widget.cpp
    widget.h
    widget.ui
    myglwidget.cpp
    myglwidget.h
  )
else()
  add_executable(OpenGL_BoilerPlate
    main.cpp
    widget.cpp
    widget.h
    widget.ui
    myglwidget.cpp
    myglwidget.h
  )
endif()

target_link_libraries(OpenGL_BoilerPlate PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${OPENGL_LIBRARIES})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# Introduction to GLEW, GLFW and SDL

# What is GLEW?

  • OpenGL Extension Wrangler
  • Interface for OpenGL versions above 1.1
  • Load OpenGL extensions
  • Some extensions are platform specific, GLEW can check id they exist on that platform
  • Alternatives: GL3W, glLoadGen, glad, glsdk, glbinding, libepoxy, Glee
  • Best to use GLEW. Most people do.

# Using GLEW

  • #include <GL/glew.>
  • After initialization OpenGL context: glewExperimental = GL_TRUE
  • glewInit();
  • Should return GLEW_OK. If it fails, it returns the error.
  • Can read the error with glewGetErrorString(result);
  • Check extenisions exist: if (!GLEW_EXT_framebuffer_object){}
  • wglew.h for windows only functions

# GLFW

Creates the context for GLEW to work, It's a library that handle windows.

  • OpenGL Framework
  • Handles window creation and control
  • Pick up and process input from the keyboard, mouse, joystick and gamepad
  • Even allows multiple monitor support!
  • uses OpenGL context for windows.

# SDL

  • Simple DirectMedia Layer
  • Can do almost everything GLFW can do...
  • and more (Audio, Threading, Filesystems, etc.)
  • Used in: FTL, AMnesia, Starbound and Dying light
  • Even used in level editors for Source Engine and Cryengine!

# Alternatives

  • SFML (Simple and Fast Multimedia Library): Like SDL but with even more features
  • ... but the OpenGL context is very week. Based on 2D only graphics.
  • GLUT (OpenGL Utility Toolkit) : Is no longer maintained. Try to avoid it.
  • Win32 API: For the purists. Lowest level for window creation. Only attempt if you know what you are doing!

OpenGL Class Setup

# Setting up GLEW with GLFW

Download the 32-bit libraries below:

GLEW Library

GLFW Library

Obs.: the 32-bit will work on the 64-bit.


Linking the libraries in Visual Studio:

  1. include the GLEW and GLFW libraries on External_Libraries folder outside the project folder.

GLEW with GLFW Setup

  1. Open Project properties and set Configuration to All Configurations and Platform to All Platforms.

  2. Got to C/C++ and include on Additional Include Directories:

$(SolutionDir)/../../External_Libraries/GLEW/include
$(SolutionDir)/../../External_Libraries/GLFW/include
1
2

GLEW with GLFW Setup

  1. Open project properties and go to Linker -> Additional Libraries and include:
$(SolutionDir)/../../External_Libraries/GLFW/lib-vc2019
$(SolutionDir)/../../External_Libraries/GLEW/lib/Release/Win32
1
2

GLEW with GLFW Setup

  1. At the linker -> Input -> Additional Dependencies include:
opengl32.lib
glew32.lib
glfw3.lib
1
2
3

GLEW with GLFW Setup

  1. Copy the file glew32.dll from External_Libraries/GLEW/bin/Release/Win32 and paste inside the projects folder.

DONE!!!!

# First Program

#include <iostream>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;


int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Clear window
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glfwSwapBuffers(mainWindow);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

GLEW with GLFW Setup

# Shaders and the Rendering Pipeline

It's somewhat important to understand what's going on behind the scenes.

It's not as simple as just the CPU receiving the commands and then running some function in the background.

What actually happens is it gets passed to your graphics card or the GPU in order to make things as fast as possible. There's something that we call pipeline.

So it's a set series of operations that get performed every time you go through. Everything is part of the GPU itself it runs through all of this and the rendering pipeline is the name for that. Whereas Shaders are parts of the rendering pipeline that we can actually reprogramme.


Pipeline and shaders


Pipeline and shaders

# Vertex Specification


Pipeline and shaders


Pipeline and shaders


Pipeline and shaders

Here is an example of a very simple vertex shader:

The first thing you need to do in a vertical shader is define the version of glsl you are using.

#version 330

layout (location = 0) in vec 3 pos;

void main()
{
    gl_Position = vec4(pos, 1.0);
}
1
2
3
4
5
6
7
8

# Second Program - Triangle

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;

GLuint VAO, VBO, shader;

// Vertex Shader 
static const char* vShader = "                                      \n\
#version 410                                                        \n\
                                                                    \n\
layout (location = 0) in vec3 pos;                                  \n\
                                                                    \n\
void main()                                                         \n\
{                                                                   \n\
    gl_Position = vec4(0.4 * pos.x, 0.4 * pos.y, pos.z, 1.0);       \n\
}";

static const char* fShader = "                                      \n\
#version 410                                                        \n\
                                                                    \n\
out vec4 colour;                                                    \n\
                                                                    \n\
layout (location = 0) in vec3 pos;                                  \n\
                                                                    \n\
void main()                                                         \n\
{                                                                   \n\
    colour = vec4(0.0, 1.0, 0.0, 1.0);                              \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn, 
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array. 
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array. 
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);  
    glBindVertexArray(VAO);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0); 

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO

    glBindVertexArray(0); //undoing the binding in the VAO
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);

    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };


    glLinkProgram(shader); // Create the exectables in the graphics card.

    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
}

int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader

        glBindVertexArray(VAO); // We saying now work with this VAO
        glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

Pipeline and shaders

TIP

Maybe this error can show up, so just cange the shader version for 410.

Error compiling the 35632 shader: '0(6) : error C7548: 'layout(location)' requires "#extension GL_ARB_separate_shader_objects : enable" before use
0(6) : error C0000: ... or #version 410
1
2

# Vectors, Matrices and Uniform Variables

Translating:


Translation Matrices

Scaling:


Scaling Matrices

Rotation:


Rotation Matrices


Rotation Matrices

GLM will do most of the matrix maths for us.

Combining:


Combining Matrices


Combining Matrices

GLM:


GLM Matrices

Uniform Variables


Uniform Variables


Uniform Variables


Uniform Variables

Summary


Summary

# Uniform Variables

Just a quick one down the difference between an attribute variable and an uniform variable in the context of shade is an attribute variable is distinct for each point pass through. This shader here will be running for each coordinate that is passed through each of the points on the triangle and this piece of code will be run on those points. Each time this shader runs this position he will change.

However, a uniform variable will be a variable which is constant over the whole flow. We pass on one variable and that variable will be the same on every single instance. Every single time that shader runs for that set of coordinates.




 






static const char* vShader = "                                      \n\
#version 410                                                        \n\
                                                                    \n\
layout (location = 0) in vec3 pos;                                  \n\
                                                                    \n\
void main()                                                         \n\
{                                                                   \n\
    gl_Position = vec4(0.4 * pos.x, 0.4 * pos.y, pos.z, 1.0);       \n\
}";
1
2
3
4
5
6
7
8
9

# Moving a triangle left and right in the screen




 






 

 
 
 
 








 



 


































































































































 
 























































 
 
 
 
 
 
 
 
 
 
 
 
 
 








 
 
 
 
 












#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;

GLuint VAO, VBO, shader, uniformXMove;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.005f;


// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
uniform float xMove;                                                        \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = vec4(0.4 * pos.x + xMove, 0.4 * pos.y, pos.z, 1.0);       \n\
}";

static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vec4(0.0, 1.0, 0.0, 1.0);                                      \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn, 
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array. 
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array. 
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);  
    glBindVertexArray(VAO);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0); 

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO

    glBindVertexArray(0); //undoing the binding in the VAO
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);

    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };


    glLinkProgram(shader); // Create the exectables in the graphics card.

    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }

    // Get the actual ID or location of the uniform variable
    uniformXMove = glGetUniformLocation(shader, "xMove");
}

int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader

        // Assign the value to the shader itself
        // Pass the location in the shader, everytime this goes trough triOffset will be updated, 
        // It will update the Uniform value and 
        // that means the shader value being used for xMove will be getting changed everytime this shader runs.
        glUniform1f(uniformXMove, triOffset); 

        glBindVertexArray(VAO); // We saying now work with this VAO
        glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

Moving Triangle


Moving Triangle

# GLM

To install GLM go to the github, and download the latest non alpha version.

Extract the file and delete all folder except the glm folder.

Copy the folder into the External_Libraries folder and add on the project in visual studio code, at C/C++ Additional Include Directories.

Done!

# Transforming Translation








 
 
 




 













 



 



































































































































 













































































 
 
 
 
 
 














#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;

GLuint VAO, VBO, shader, uniformModel;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;


// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = model * vec4(0.4 * pos.x, 0.4 * pos.y, pos.z, 1.0);       \n\
}";

static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vec4(0.0, 1.0, 0.0, 1.0);                                      \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn, 
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array. 
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array. 
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);  
    glBindVertexArray(VAO);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0); 

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO

    glBindVertexArray(0); //undoing the binding in the VAO
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);

    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };


    glLinkProgram(shader); // Create the exectables in the graphics card.

    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }

    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}

int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader

        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        // Now we modify the shaders to handle the new values

        glBindVertexArray(VAO); // We saying now work with this VAO
        glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

# Checking for Errors in OpenGL

To check for error we define the ASSERT macro and the functions GLClearError and GLLogCall, another macro GLCall(x) will call the function GLClearError to make sure the error comes from the function enveloped and then will ASSERT the GLLogCall. Like shown below:

#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


//Checking for errors
static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Then envelope any GL function in GLCall(), like so:

GLCall(uniformModel = glGetUniformLocation(shader, "model")); 
1
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <fstream>
#include <sstream>

#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


//Checking for errors
static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;

GLuint VAO, VBO, shader, uniformModel;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;


// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = model * vec4(0.4 * pos.x, 0.4 * pos.y, 0.4 * pos.z, 1.0); \n\
}";



static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vec4(0.0, 1.0, 0.0, 1.0);                                      \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO

    glBindVertexArray(0); //undoing the binding in the VAO
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    
    glLinkProgram(shader); // Create the exectables in the graphics card.
    
    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
    
    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}

int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader


        
        glm::mat4 model(1.0f);
        model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
        
        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        // Now we modify the shaders to handle the new values

        glBindVertexArray(VAO); // We saying now work with this VAO
        glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);        
    }    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291

# Transforming Rotation









































 








 
























































































































































































































 
 
 
 
 











 


















#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <fstream>
#include <sstream>

#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


//Checking for errors
static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;

GLuint VAO, VBO, shader, uniformModel;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;

float curAngle = 0.0f;


// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = model * vec4(0.4 * pos.x, 0.4 * pos.y, 0.4 * pos.z, 1.0); \n\
}";



static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vec4(0.0, 1.0, 0.0, 1.0);                                      \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO

    glBindVertexArray(0); //undoing the binding in the VAO
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    
    glLinkProgram(shader); // Create the exectables in the graphics card.
    
    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
    
    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}

int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        curAngle += 1.0f;
        if (curAngle >= 360)
        {
            curAngle -= 360;
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader


        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        //model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
        model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 0.0f, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        // Now we modify the shaders to handle the new values

        glBindVertexArray(VAO); // We saying now work with this VAO
        glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);      
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

Transform Rotation


Transform Rotation


Transform Rotation

# Transform Scale

For scaling just add:

model = glm::scale(model, glm::vec3(2.0f, 2.0f, 1.0f));
1

and this will substitute the 0.4 we included in the shaders:

gl_Position = model * vec4(0.4 * pos.x, 0.4 * pos.y, 0.4 * pos.z, 1.0); \n\
1

to be like this:

gl_Position = model * vec4(pos.x, pos.y, 0.4 * pos.z, 1.0); \n\
1

and simplifying even more we can do only the pos, since it's a vec3, and we have our simplified shader:

// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = model * vec4(pos, 1.0);                                   \n\
}";
1
2
3
4
5
6
7
8
9
10
11
12

Complete program with triangle scaling up and down:




















































 
 
 
 





























































































































































































































 
 
 
 
 
 
 
 
 
 
 












 


















#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <fstream>
#include <sstream>

#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


//Checking for errors
static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;

GLuint VAO, VBO, shader, uniformModel;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;

float curAngle = 0.0f;

bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;

// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = model * vec4(pos, 1.0);                                   \n\
}";



static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vec4(0.0, 1.0, 0.0, 1.0);                                      \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO

    glBindVertexArray(0); //undoing the binding in the VAO
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    
    glLinkProgram(shader); // Create the exectables in the graphics card.
    
    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
    
    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}

int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        curAngle += 1.0f;
        if (curAngle >= 360)
        {
            curAngle -= 360;
        }

        if (sizeDirection)
        {
            curSize += 0.001f;
        }
        else {
            curSize -= 0.001f;
        }

        if (curSize >= maxSize || curSize <= minSize) {
            sizeDirection = !sizeDirection;
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader


        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        //model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
        //model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 0.0f, 1.0f));
        model = glm::scale(model, glm::vec3(curSize, curSize, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        // Now we modify the shaders to handle the new values

        glBindVertexArray(VAO); // We saying now work with this VAO
        glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);      
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317

Transform Scaling


Transform Scaling


Transform Scaling

# Implementing Rotation, Translation and Scaling in QtCreator

CMakeLists.txt

cmake_minimum_required(VERSION 3.8)

project(OpenGLCourse LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)



find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)


# Unsed previous parameters from cache for the sake of dynamism
unset(KITGL_SUPPORT_GLFW CACHE)
unset(KITGL_SUPPORT_GLEW CACHE)


###
### Initialization
###
# Project parameters
set(PROJ_LINK_LIBS)
#set(PROJ_SRC)

# Add all files/folders under src folder automatically to the project
#file(GLOB_RECURSE PROJ_SRC src/*.h src/*.cpp)



###
### Options
###
# Unsed previous parameters from cache for the sake of dynamism
unset(KITGL_SUPPORT_GLFW CACHE)
unset(KITGL_SUPPORT_GLAD CACHE)
unset(KITGL_SUPPORT_GLEW CACHE)
unset(KITGL_SUPPORT_FREEGLUT CACHE)

# Enable desired libs
option(KITGL_SUPPORT_GLFW "Set ON/OFF to enable/disable GLFW support." ON)
option(KITGL_SUPPORT_GLAD "Set ON/OFF to enable/disable GLAD support." ON)
option(KITGL_SUPPORT_GLEW "Set ON/OFF to enable/disable GLEW support." ON)
option(KITGL_SUPPORT_FREEGLUT "Set ON/OFF to enable/disable FreeGLUT support." ON)



###
### OpenGL
###

find_package(OpenGL REQUIRED)
include_directories(${OpenGL_INCLUDE_DIRS})
link_directories(${OpenGL_LIBRARY_DIRS})
add_definitions(${OpenGL_DEFINITIONS})

if(NOT OpenGL_FOUND)
message(Error "OpenGL not found")
endif(NOT OpenGL_FOUND)

list(APPEND PROJ_LINK_LIBS ${OPENGL_LIBRARIES})


###
### GLFW
###
#if(KITGL_SUPPORT_GLFW)
#    add_subdirectory(libs/glfw)
#    include_directories(libs/glfw/include)
#    include_directories(libs/glfw/deps)
#    list(APPEND PROJ_LINK_LIBS glfw)
#endif(KITGL_SUPPORT_GLFW)


# Disable internal builds
unset(GLFW_BUILD_EXAMPLES CACHE)
unset(GLFW_BUILD_TESTS CACHE)
unset(GLFW_BUILD_DOCS CACHE)
option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" OFF)
option(GLFW_BUILD_TESTS "Build the GLFW test programs" OFF)
option(GLFW_BUILD_DOCS "Build the GLFW documentation" OFF)

add_subdirectory(libs/glfw)
include_directories(libs/glfw/include)
include_directories(libs/glfw/deps)
list(APPEND PROJ_LINK_LIBS glfw)


###
### GLEW
###
#if(KITGL_SUPPORT_GLEW)
#    add_subdirectory(libs/glew/build/cmake)
#    include_directories(libs/glew/include)
#    list(APPEND PROJ_LINK_LIBS glew)
#endif(KITGL_SUPPORT_GLEW)


# Disable internal builds
unset(BUILD_UTILS CACHE)
option(BUILD_UTILS "utilities" OFF)

add_subdirectory(libs/glew/build/cmake)
include_directories(libs/glew/include)
list(APPEND PROJ_LINK_LIBS glew)


###
### GLM
###

include_directories(/libs/GLM/)



add_executable(OpenGLCourse
    main.cpp
    widget.cpp
    widget.h
    widget.ui
)


target_link_libraries(OpenGLCourse PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${PROJ_LINK_LIBS})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

Main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    w.drawOpenGL();
    return a.exec();
}
1
2
3
4
5
6
7
8
9
10
11

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    const GLint WIDTH = 800, HEIGHT = 600;
    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    GLuint VAO, VBO, shader, uniformModel;
    const float toRadians = 3.14159265f / 180.0f;

    bool direction = true;
    float triOffset = 0.0f;
    float triMaxoffset = 0.7f;
    float triIncrement = 0.02f;

    float curAngle = 0.0f;

    bool sizeDirection = true;
    float curSize = 0.4f;
    float maxSize = 0.8f;
    float minSize = 0.1f;


    // Vertex Shader
    inline static const char* vShader = "                                      \n\
    #version 410                                                                \n\
                                                                                \n\
    layout (location = 0) in vec3 pos;                                          \n\
                                                                                \n\
    uniform mat4 model;                                                         \n\
                                                                                \n\
    void main()                                                                 \n\
    {                                                                           \n\
        gl_Position = model * vec4(0.4 * pos.x, 0.4 * pos.y, pos.z, 1.0);       \n\
    }";

    inline static const char* fShader = "                                      \n\
    #version 410                                                        \n\
                                                                        \n\
    out vec4 colour;                                                    \n\
                                                                        \n\
    layout (location = 0) in vec3 pos;                                  \n\
                                                                        \n\
    void main()                                                         \n\
    {                                                                   \n\
        colour = vec4(0.0, 1.0, 0.0, 1.0);                              \n\
    }";


    void CreateTriangle();
    void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType);
    void CompileShaders();
    int drawOpenGL();

private slots:
    void on_horizontalSlider_sliderMoved(int position);

    void on_horizontalSlider_2_sliderMoved(int position);

    void on_horizontalSlider_3_sliderMoved(int position);

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

Widget.cpp

#include "widget.h"
#include "./ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::CreateTriangle()
{
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO.
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO

    glBindVertexArray(0); //undoing the binding in the VAO
}

void Widget::AddShader(GLuint theProgram, const char *shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}

void Widget::CompileShaders()
{
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);

    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };


    glLinkProgram(shader); // Create the exectables in the graphics card.

    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}

int Widget::drawOpenGL()
{
    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

//        // Checking the direction
//        if (direction)
//        {
//            triOffset += triIncrement;
//        }
//        else {
//            triOffset -= triIncrement;
//        }

//        // Check if the location is between max and min offset

//        if (abs(triOffset) >= triMaxoffset) {
//            direction = !direction; // Easy way of doing a bool
//        }

//        curAngle += 1.0f;
//        if (curAngle >= 360)
//        {
//            curAngle -= 360;
//        }

//        if (sizeDirection)
//        {
//            curSize += 0.001f;
//        }
//        else {
//            curSize -= 0.001f;
//        }

//        if (curSize >= maxSize || curSize <= minSize) {
//            sizeDirection = !sizeDirection;
//        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader


        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
        model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 0.0f, 1.0f));
        model = glm::scale(model, glm::vec3(curSize, curSize, 1.0f));

        // First is the location, then quantity, then we don't want to transpose
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        // Now we modify the shaders to handle the new values

        glBindVertexArray(VAO); // We saying now work with this VAO
        glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);
    }

    glfwDestroyWindow(mainWindow);
    glfwTerminate();
    return 0;
}

void Widget::on_horizontalSlider_sliderMoved(int position)
{
    triOffset = float(position)/100;
    ui->TriOffsetlabel->setText(QString::number(triOffset));
    ui->Sliderlabel->setText(QString::number(position));
}

void Widget::on_horizontalSlider_2_sliderMoved(int position)
{
    curAngle  = position;
    ui->TriOffsetlabel_2->setText(QString::number(curAngle));
    ui->Sliderlabel_2->setText(QString::number(position));
}

void Widget::on_horizontalSlider_3_sliderMoved(int position)
{
    curSize  = float(position)/100;
    ui->TriOffsetlabel_3->setText(QString::number(curSize));
    ui->Sliderlabel_3->setText(QString::number(position));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

Transform Scaling


Transform Scaling


Transform Scaling

File Structure:


Transform Scaling


Transform Scaling

# Interpolation, Indexed Draws and Projections


Interpolation, Indexed Draws and Projections

# Interpolation

An important concept is that we can get a variable out of the shader.






































































 







 







 


























































































































































































































 


















#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <fstream>
#include <sstream>

#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


//Checking for errors
static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;

GLuint VAO, VBO, shader, uniformModel;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;

float curAngle = 0.0f;

bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;

// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
out vec4 vCol;                                                              \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = model * vec4(pos, 1.0);                                   \n\
    vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f);                              \n\
}";

// clamp make th values outside 0 and 1 to be inside 0 and 1

static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
in vec4 vCol;                                                               \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vCol;                                                          \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO

    glBindVertexArray(0); //undoing the binding in the VAO
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    
    glLinkProgram(shader); // Create the exectables in the graphics card.
    
    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
    
    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}

int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        curAngle += 1.0f;
        if (curAngle >= 360)
        {
            curAngle -= 360;
        }

        if (sizeDirection)
        {
            curSize += 0.001f;
        }
        else {
            curSize -= 0.001f;
        }

        if (curSize >= maxSize || curSize <= minSize) {
            sizeDirection = !sizeDirection;
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader


        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        //model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
        //model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 0.0f, 1.0f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        // Now we modify the shaders to handle the new values

        glBindVertexArray(VAO); // We saying now work with this VAO
        glDrawArrays(GL_TRIANGLES, 0, 3); // draw 3 points starting on the first
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);      
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322

Interpolation

# Indexed Draws

Building a pyramid with indexed indices.











































 






















































 
 
 
 
 
 

















 
 
 
 













 

















































































































































































 










 
 
 
 








#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <fstream>
#include <sstream>

#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


//Checking for errors
static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;

GLuint VAO, VBO, IBO, shader, uniformModel;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;

float curAngle = 0.0f;

bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;

// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
out vec4 vCol;                                                              \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = model * vec4(pos, 1.0);                                   \n\
    vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f);                              \n\
}";

// clamp make th values outside 0 and 1 to be inside 0 and 1

static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
in vec4 vCol;                                                               \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vCol;                                                          \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    unsigned int indices[] = {
        0, 3, 1,
        1, 3, 2,
        2, 3, 0,
        0, 1, 2
    };
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         0.0f, -1.0f, 1.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Creating another buffer for the indices;
    glGenBuffers(1, &IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0); //undoing the binding in the VAO
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    
    glLinkProgram(shader); // Create the exectables in the graphics card.
    
    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
    
    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}

int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        curAngle += 0.01f;
        if (curAngle >= 360)
        {
            curAngle -= 360;
        }

        if (sizeDirection)
        {
            curSize += 0.001f;
        }
        else {
            curSize -= 0.001f;
        }

        if (curSize >= maxSize || curSize <= minSize) {
            sizeDirection = !sizeDirection;
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader


        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        //model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
        model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        // Now we modify the shaders to handle the new values

        glBindVertexArray(VAO); // We saying now work with this VAO
        
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
        glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0); //draw by the elements IDs
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);      
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

Indexed Draws

TIP

We need to setup the Depth Buffer for the colors to be correct, now it doesn't know which triangle is in fron of which.






































































































































































































































































 















































 






























#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <fstream>
#include <sstream>

#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


//Checking for errors
static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;

GLuint VAO, VBO, IBO, shader, uniformModel;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;

float curAngle = 0.0f;

bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;

// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
out vec4 vCol;                                                              \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = model * vec4(pos, 1.0);                                   \n\
    vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f);                              \n\
}";

// clamp make th values outside 0 and 1 to be inside 0 and 1

static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
in vec4 vCol;                                                               \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vCol;                                                          \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    unsigned int indices[] = {
        0, 3, 1,
        1, 3, 2,
        2, 3, 0,
        0, 1, 2
    };
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         0.0f, -1.0f, 1.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Creating another buffer for the indices;
    glGenBuffers(1, &IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0); //undoing the binding in the VAO
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    
    glLinkProgram(shader); // Create the exectables in the graphics card.
    
    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
    
    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
}

int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    glEnable(GL_DEPTH_TEST);

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        curAngle += 0.01f;
        if (curAngle >= 360)
        {
            curAngle -= 360;
        }

        if (sizeDirection)
        {
            curSize += 0.001f;
        }
        else {
            curSize -= 0.001f;
        }

        if (curSize >= maxSize || curSize <= minSize) {
            sizeDirection = !sizeDirection;
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader


        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        //model = glm::translate(model, glm::vec3(triOffset, 0.0f, 0.0f)); // Applying a translation that will only be changing the x value.
        model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        // Now we modify the shaders to handle the new values

        glBindVertexArray(VAO); // We saying now work with this VAO
        
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
        glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0); //draw by the elements IDs
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);      
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339

Indexed Draws

# Projections

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <fstream>
#include <sstream>

#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


//Checking for errors
static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;

GLuint VAO, VBO, IBO, shader, uniformModel, uniformProjection;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;

float curAngle = 0.0f;

bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;

// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
out vec4 vCol;                                                              \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
uniform mat4 projection;                                                   \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = projection * model * vec4(pos, 1.0);                     \n\
    vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f);                              \n\
}";

// clamp make th values outside 0 and 1 to be inside 0 and 1

static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
in vec4 vCol;                                                               \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vCol;                                                          \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    unsigned int indices[] = {
        0, 3, 1,
        1, 3, 2,
        2, 3, 0,
        0, 1, 2
    };
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         0.0f, -1.0f, 1.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Creating another buffer for the indices;
    glGenBuffers(1, &IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0); //undoing the binding in the VAO
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);

}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    
    glLinkProgram(shader); // Create the exectables in the graphics card.
    
    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
    
    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
    uniformProjection = glGetUniformLocation(shader, "projection"); // Get the projection variable and place the ID of the location intp the variable uniformProjection

    
}

int main()
{

    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    glEnable(GL_DEPTH_TEST);

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    glm::mat4 projection = glm::perspective(45.0f, (GLfloat)bufferWidth / (GLfloat)bufferHeight, 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        curAngle += 1.0f;
        if (curAngle >= 360)
        {
            curAngle -= 360;
        }

        if (sizeDirection)
        {
            curSize += 0.001f;
        }
        else {
            curSize -= 0.001f;
        }

        if (curSize >= maxSize || curSize <= minSize) {
            sizeDirection = !sizeDirection;
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader


        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        model = glm::translate(model, glm::vec3(0.0f, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
        model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));

        // Now we modify the shaders to handle the new values

        glBindVertexArray(VAO); // We saying now work with this VAO
        
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
        glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0); //draw by the elements IDs
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        glBindVertexArray(0); // Unassign the VAO

        glUseProgram(0); // Unassign the shader


        glfwSwapBuffers(mainWindow);      
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346

Projection

# Cleaning up the code

# Mesh Class - Putting the VAO, VBO and IBO on its own class

Making a class will enable us to reuse the code and create multiple objects.

First let's create the class Mesh:


Mesh Class

First we create our variables private, so that nobody can access them from the outside. Then the functions headers.

#pragma once
#include <GL/glew.h>

class Mesh
{
public:
	Mesh();

	void CreateMesh(GLfloat *vertices, unsigned int *indices, unsigned int numofVertices, unsigned int numofIndices);
	void RenderMesh();
	void ClearMesh();

	~Mesh();

private:
	GLuint VAO, VBO, IBO;
	GLsizei indexCount; // to say how many indeces we have to draw like here, with 12: glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0); 

};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Now everytime we call create mesh we will create this mesh object, VAO, VBO, IBO and indexCount will be initialized to 0, it will call CreateMesh; then we will get the indexCount and we will know how much we will pass in when we are drawing; then we will create the VAO, the IBO for it, the VBO; and this will be stored locally to this one object they are all member variables of this object, and when we call Draw() it will simple reference these values. This way we can know we don't have the same object being drawn over and over.

#include "Mesh.h"

Mesh::Mesh()
{
	VAO = 0;
	VBO = 0;
	IBO = 0;
	indexCount = 0;
}

void Mesh::CreateMesh(GLfloat* vertices, unsigned int* indices, unsigned int numofVertices, unsigned int numofIndices)
{
	indexCount = numofIndices;
    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Creating another buffer for the indices;
    glGenBuffers(1, &IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * numofIndices, indices, GL_STATIC_DRAW);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * numofVertices, vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0); //undoing the binding in the VAO
}



Mesh::~Mesh()
{

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

Now we need to do the RenderMesh() function.

void Mesh::RenderMesh()
{
    glBindVertexArray(VAO); // We saying now work with this VAO

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0); //draw by the elements IDs
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0); // Unassign the VAO
}
1
2
3
4
5
6
7
8
9

Then the function ClearMesh(), to clean the buffer of the graphics card memory to make room for more space, this avoid memory overflow. There is no garbage collection like in Java.

void Mesh::ClearMesh()
{
    if (IBO != 0)
    {
        glDeleteBuffers(1, &IBO);
        IBO = 0;
    }

    if (VBO != 0)
    {
        glDeleteBuffers(1, &VBO);
        VBO = 0;
    }

    if (VAO != 0)
    {
        glDeleteVertexArrays(1, &VAO);
        VAO = 0;
    }

    indexCount = 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

And the destructor:

Mesh::~Mesh()
{
    ClearMesh();
}
1
2
3
4

The whole Class:

#include "Mesh.h"

Mesh::Mesh()
{
	VAO = 0;
	VBO = 0;
	IBO = 0;
	indexCount = 0;
}

void Mesh::CreateMesh(GLfloat *vertices, unsigned int *indices, unsigned int numofVertices, unsigned int numofIndices)
{
	indexCount = numofIndices;
    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Creating another buffer for the indices;
    glGenBuffers(1, &IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * numofIndices, indices, GL_STATIC_DRAW);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * numofVertices, vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0); //undoing the binding in the VAO
}

void Mesh::RenderMesh()
{
    glBindVertexArray(VAO); // We saying now work with this VAO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0); //draw by the elements IDs
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0); // Unassign the VAO
}


void Mesh::ClearMesh()
{
    if (IBO != 0)
    {
        glDeleteBuffers(1, &IBO);
        IBO = 0;
    }

    if (VBO != 0)
    {
        glDeleteBuffers(1, &VBO);
        VBO = 0;
    }

    if (VAO != 0)
    {
        glDeleteVertexArrays(1, &VAO);
        VAO = 0;
    }

    indexCount = 0;
}

Mesh::~Mesh()
{
    ClearMesh();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

To implement the class into the program we do:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <vector>

#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>

#include <fstream>
#include <sstream>

#include "Mesh.h"


//Checking for errors
#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;

std::vector<Mesh*> meshList;

GLuint shader, uniformModel, uniformProjection;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;

float curAngle = 0.0f;

bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;

// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
out vec4 vCol;                                                              \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
uniform mat4 projection;                                                   \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = projection * model * vec4(pos, 1.0);                     \n\
    vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f);                              \n\
}";

// clamp make th values outside 0 and 1 to be inside 0 and 1

static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
in vec4 vCol;                                                               \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vCol;                                                          \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    unsigned int indices[] = {
        0, 3, 1,
        1, 3, 2,
        2, 3, 0,
        0, 1, 2
    };
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         0.0f, -1.0f, 1.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    Mesh *obj1 = new Mesh(); // Make sure to use pointers
    obj1->CreateMesh(vertices, indices, 12, 12);
    meshList.push_back(obj1); // add to the end of the list
    std::cout << "meslist: " << meshList[0] << std::endl;
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);
}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    
    glLinkProgram(shader); // Create the exectables in the graphics card.
    
    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
    
    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
    uniformProjection = glGetUniformLocation(shader, "projection"); // Get the projection variable and place the ID of the location intp the variable uniformProjection   
}

int main()
{
    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    glEnable(GL_DEPTH_TEST);

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    glm::mat4 projection = glm::perspective(45.0f, (GLfloat)bufferWidth / (GLfloat)bufferHeight, 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        curAngle += 1.0f;
        if (curAngle >= 360)
        {
            curAngle -= 360;
        }

        if (sizeDirection)
        {
            curSize += 0.001f;
        }
        else {
            curSize -= 0.001f;
        }

        if (curSize >= maxSize || curSize <= minSize) {
            sizeDirection = !sizeDirection;
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader


        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        model = glm::translate(model, glm::vec3(triOffset, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
        model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));

        // Rendering using the class
        GLCall(meshList[0]->RenderMesh());

        glUseProgram(0); // Unassign the shader

        glfwSwapBuffers(mainWindow);      
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319

Mesh Class

# Multiple Objects

To include a second object we can do:



























































































































 
 
 
































































































































































































 
 
 
 
 
 











#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <vector>

#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>

#include <fstream>
#include <sstream>

#include "Mesh.h"


//Checking for errors
#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;

std::vector<Mesh*> meshList;

GLuint shader, uniformModel, uniformProjection;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;

float curAngle = 0.0f;

bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;

// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
out vec4 vCol;                                                              \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
uniform mat4 projection;                                                    \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = projection * model * vec4(pos, 1.0);                      \n\
    vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f);                              \n\
}";

// clamp make th values outside 0 and 1 to be inside 0 and 1

static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
in vec4 vCol;                                                               \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vCol;                                                          \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateTriangle()
{
    unsigned int indices[] = {
        0, 3, 1,
        1, 3, 2,
        2, 3, 0,
        0, 1, 2
    };
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         0.0f, -1.0f, 1.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    Mesh *obj1 = new Mesh(); // Make sure to use pointers
    obj1->CreateMesh(vertices, indices, 12, 12);
    meshList.push_back(obj1); // add to the end of the list

    Mesh* obj2 = new Mesh(); // Make sure to use pointers
    obj2->CreateMesh(vertices, indices, 12, 12);
    meshList.push_back(obj2); // add to the end of the list
}


/*!
Add the shaders to the program funtion
*/
void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);
}


/*!

*/
void CompileShaders() {
    shader = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

    // Make sure the shader was created correctly
    if (!shader) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shader, vShader, GL_VERTEX_SHADER);
    AddShader(shader, fShader, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    
    glLinkProgram(shader); // Create the exectables in the graphics card.
    
    // Make sure the program is linked properlly
    glGetProgramiv(shader, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shader);
    glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }
    
    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shader, "model"); // uniformModel will be the location of the model matrix
    uniformProjection = glGetUniformLocation(shader, "projection"); // Get the projection variable and place the ID of the location intp the variable uniformProjection   
}

int main()
{
    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    glEnable(GL_DEPTH_TEST);

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateTriangle();
    CompileShaders();

    glm::mat4 projection = glm::perspective(45.0f, (GLfloat)bufferWidth / (GLfloat)bufferHeight, 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        curAngle += 1.0f;
        if (curAngle >= 360)
        {
            curAngle -= 360;
        }

        if (sizeDirection)
        {
            curSize += 0.001f;
        }
        else {
            curSize -= 0.001f;
        }

        if (curSize >= maxSize || curSize <= minSize) {
            sizeDirection = !sizeDirection;
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.

        // Draw the triangle
        glUseProgram(shader); // This will now grab that ID, It will go through the graphics card and tell it to use the one with the ID of shader


        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        
        model = glm::translate(model, glm::vec3(triOffset, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
        //model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));

        // Rendering using the class
        meshList[0]->RenderMesh();


        model = glm::mat4(1.0f);
        model = glm::translate(model, glm::vec3(-triOffset, 1.0f, -2.5f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        meshList[1]->RenderMesh();
        


        glUseProgram(0); // Unassign the shader

        glfwSwapBuffers(mainWindow);      
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333

Multiple Objects

# Shaders Class - Moving Shaders to External Files

First we make the Shaders Class.

Shader.h

#pragma once

#include <stdio.h>
#include <string>
#include <iostream>
#include <fstream>

#include <GL/glew.h>

class Shader
{
public:
	Shader();

	void CreateFromString(const char* vertexCode, const char* fragmentCode);

	GLuint GetProjectionLocation();
	GLuint GetModelLocation();

	void UseShader();
	void clearShader();

	~Shader();

private:
	GLuint shaderID, uniformProjection, unifomModel;

	void CompileShader(const char* vertexCode, const char* fragmentCode);
	void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

Shader.cpp

#include "Shader.h"

Shader::Shader()
{
	shaderID = 0;
	uniformModel = 0;
	uniformProjection = 0;
}


void Shader::CreateFromString(const char* vertexCode, const char* fragmentCode)
{
	CompileShader(vertexCode, fragmentCode);
}


void Shader::CompileShader(const char* vertexCode, const char* fragmentCode)
{
    shaderID = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

        // Make sure the shader was created correctly
    if (!shaderID) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shaderID, vertexCode, GL_VERTEX_SHADER);
    AddShader(shaderID, fragmentCode, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };


    glLinkProgram(shaderID); // Create the exectables in the graphics card.

    // Make sure the program is linked properlly
    glGetProgramiv(shaderID, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shaderID, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shaderID);
    glGetProgramiv(shaderID, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shaderID, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }

    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shaderID, "model"); // uniformModel will be the location of the model matrix
    uniformProjection = glGetUniformLocation(shaderID, "projection"); // Get the projection variable and place the ID of the location intp the variable uniformProjection   
}

//Getters of uniformProjectin and uniformModel
GLuint Shader::GetProjectionLocation()
{
    return uniformProjection;
}


GLuint Shader::GetModelLocation()
{
    return uniformModel;
}



void Shader::UseShader()
{
    glUseProgram(shaderID);

}


void Shader::clearShader()
{
    if (shaderID != 0)
    {
        glDeleteProgram(shaderID); //This is deleting the program of the graphics card
        shaderID = 0;
    }

    uniformModel = 0;
    uniformProjection = 0;
}


void Shader::AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);
}


Shader::~Shader()
{
    clearShader();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

Using it on the program

















 































 





















































 

























 
 
 
 
 
 















































 
 
 
 














































 
 
 



































#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <vector>

#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>

#include <fstream>
#include <sstream>

#include "Mesh.h"
#include "Shader.h"


//Checking for errors
#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;

std::vector<Mesh*> meshList;
std::vector<Shader*> shaderList;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;

float curAngle = 0.0f;

bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;

// Vertex Shader 
static const char* vShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
out vec4 vCol;                                                              \n\
                                                                            \n\
uniform mat4 model;                                                         \n\
uniform mat4 projection;                                                    \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    gl_Position = projection * model * vec4(pos, 1.0);                      \n\
    vCol = vec4(clamp(pos, 0.0f, 1.0f), 1.0f);                              \n\
}";

// clamp make th values outside 0 and 1 to be inside 0 and 1

static const char* fShader = "                                              \n\
#version 410                                                                \n\
                                                                            \n\
in vec4 vCol;                                                               \n\
                                                                            \n\
out vec4 colour;                                                            \n\
                                                                            \n\
layout (location = 0) in vec3 pos;                                          \n\
                                                                            \n\
void main()                                                                 \n\
{                                                                           \n\
    colour = vCol;                                                          \n\
}";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateObjects()
{
    unsigned int indices[] = {
        0, 3, 1,
        1, 3, 2,
        2, 3, 0,
        0, 1, 2
    };
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         0.0f, -1.0f, 1.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    Mesh *obj1 = new Mesh(); // Make sure to use pointers
    obj1->CreateMesh(vertices, indices, 12, 12);
    meshList.push_back(obj1); // add to the end of the list

    Mesh* obj2 = new Mesh(); // Make sure to use pointers
    obj2->CreateMesh(vertices, indices, 12, 12);
    meshList.push_back(obj2); // add to the end of the list
}


void CreateShaders()
{
    Shader *shader1 = new Shader();
    shader1->CreateFromString(vShader, fShader);
    shaderList.push_back(shader1);
}

int main()
{
    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "Test Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
    }

    // Get Buffer size information
    int bufferWidth, bufferHeight;
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;

    if (glewInit() != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
    }

    glEnable(GL_DEPTH_TEST);

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);

    CreateObjects();
    CreateShaders();

    GLuint uniformProjection = 0, uniformModel = 0;

    glm::mat4 projection = glm::perspective(45.0f, (GLfloat)bufferWidth / (GLfloat)bufferHeight, 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar

    // LLop until window close
    while (!glfwWindowShouldClose(mainWindow)) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        curAngle += 1.0f;
        if (curAngle >= 360)
        {
            curAngle -= 360;
        }

        if (sizeDirection)
        {
            curSize += 0.001f;
        }
        else {
            curSize -= 0.001f;
        }

        if (curSize >= maxSize || curSize <= minSize) {
            sizeDirection = !sizeDirection;
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.

        // Draw the triangle
        shaderList[0]->UseShader();
        uniformModel = shaderList[0]->GetModelLocation();
        uniformProjection = shaderList[0]->GetProjectionLocation();



        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        
        model = glm::translate(model, glm::vec3(triOffset, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
        //model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));

        // Rendering using the class
        meshList[0]->RenderMesh();


        model = glm::mat4(1.0f);
        model = glm::translate(model, glm::vec3(-triOffset, 1.0f, -2.5f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        meshList[1]->RenderMesh();
        


        glUseProgram(0); // Unassign the shader

        glfwSwapBuffers(mainWindow);      
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

Multiple Objects using Shaders Class

Now Let's put the Shaders in a folder and adjust a little bit of the code:


First we now point to the location of the files.

static const char* vShader = "Shaders/shader.vert";
static const char* fShader = "Shaders/shader.frag";
1
2

Then we change the Shader.h to include new functions CreateFromFiles and ReadFile.















 
 
 
 
















#pragma once

#include <stdio.h>
#include <string>
#include <iostream>
#include <fstream>

#include <GL/glew.h>

class Shader
{
public:
	Shader();

	void CreateFromString(const char* vertexCode, const char* fragmentCode);
	void CreateFromFiles(const char* vertexLocation, const char* fragmentLocation);

	std::string ReadFile(const char* fileLocation);

	GLuint GetProjectionLocation();
	GLuint GetModelLocation();

	void UseShader();
	void clearShader();

	~Shader();

private:
	GLuint shaderID, uniformProjection, uniformModel;

	void CompileShader(const char* vertexCode, const char* fragmentCode);
	void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

And now we implement the functions

void Shader::CreateFromString(const char* vertexCode, const char* fragmentCode)
{
	CompileShader(vertexCode, fragmentCode);
}

void Shader::CreateFromFiles(const char* vertexLocation, const char* fragmentLocation)
{
    std::string vertexString = ReadFile(vertexLocation);
    std::string fragmentString = ReadFile(fragmentLocation);

    const char* vertexCode = vertexString.c_str();
    const char* fragmentCode = fragmentString.c_str();

    CompileShader(vertexCode, fragmentCode);
}

std::string Shader::ReadFile(const char* fileLocation)
{
    std::string content;
    std::ifstream fileStream(fileLocation, std::ios::in);

    if (!fileStream.is_open()) {
        printf("Failed to read %s File doesn't exist.", fileLocation);
        return "";
    }

    std::string line = "";
    while (!fileStream.eof())
    {
        std::getline(fileStream, line);
        content.append(line + "\n");
    }

    fileStream.close();
    return content;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

And a little change on the main program at the CreateShaders function:

void CreateShaders()
{
    Shader *shader1 = new Shader();
    shader1->CreateFromFiles(vShader, fShader);
    shaderList.push_back(shader1);
}
1
2
3
4
5
6

Multiple Objects using Shaders Class

# Create Window Class

First we create the Header:

Window.h

#pragma once

#include <stdio.h>

#include <GL/glew.h>
#include <GLFW/glfw3.h>

class Window
{
public:
	Window();
	Window(GLint windowWidth, GLint windowHeight);

	int Initialize();

	GLfloat getBufferWidth() { return bufferWidth; }
	GLfloat getBufferHeight() { return bufferHeight; }

	bool getShouldClose() { return glfwWindowShouldClose(mainWindow); }

	void swapBuffers() { glfwSwapBuffers(mainWindow); }

	~Window();

private:
	GLFWwindow *mainWindow;
	GLint width, height;
	GLint bufferWidth, bufferHeight;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Window.cpp

#include "Window.h"
#include <iostream>


Window::Window()
{
    width = 800;
    height = 600;
}


Window::Window(GLint windowWidth, GLint windowHeight)
{
	width = windowWidth;
	height = windowHeight;
}

int Window::Initialize()
{
    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    mainWindow = glfwCreateWindow(width, height, "OpenGL Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
        return 1;
    }

    // Get Buffer size information
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;


    GLenum error = glewInit();
    if (error != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
        return 1;
    }

    glEnable(GL_DEPTH_TEST);

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);
}


Window::~Window()
{
    glfwDestroyWindow(mainWindow);
    glfwTerminate();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

and change the main file:
















 

































 






























































 
 



















































































 





#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <vector>

#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>

#include <fstream>
#include <sstream>

#include "Window.h"
#include "Mesh.h"
#include "Shader.h"



//Checking for errors
#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


//Window dimensions
const GLint WIDTH = 800, HEIGHT = 600;
const float toRadians = 3.14159265f / 180.0f;

Window mainWindow;
std::vector<Mesh*> meshList;
std::vector<Shader*> shaderList;

bool direction = true;
float triOffset = 0.0f;
float triMaxoffset = 0.7f;
float triIncrement = 0.02f;

float curAngle = 0.0f;

bool sizeDirection = true;
float curSize = 0.4f;
float maxSize = 0.8f;
float minSize = 0.1f;


static const char* vShader = "Shaders/shader.vert";
static const char* fShader = "Shaders/shader.frag";



/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateObjects()
{
    unsigned int indices[] = {
        0, 3, 1,
        1, 3, 2,
        2, 3, 0,
        0, 1, 2
    };
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         0.0f, -1.0f, 1.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    Mesh *obj1 = new Mesh(); // Make sure to use pointers
    obj1->CreateMesh(vertices, indices, 12, 12);
    meshList.push_back(obj1); // add to the end of the list

    Mesh* obj2 = new Mesh(); // Make sure to use pointers
    obj2->CreateMesh(vertices, indices, 12, 12);
    meshList.push_back(obj2); // add to the end of the list
}


void CreateShaders()
{
    Shader *shader1 = new Shader();
    shader1->CreateFromFiles(vShader, fShader);
    shaderList.push_back(shader1);
}


int main()
{
    mainWindow = Window(800, 600);
    mainWindow.Initialize();

    CreateObjects();
    CreateShaders();

    GLuint uniformProjection = 0, uniformModel = 0;

    glm::mat4 projection = glm::perspective(45.0f, mainWindow.getBufferWidth() / mainWindow.getBufferHeight(), 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar

    // LLop until window close
    while (!mainWindow.getShouldClose()) {
        // Get + Handle user input events
        glfwPollEvents();

        // Checking the direction
        if (direction)
        {
            triOffset += triIncrement;
        }
        else {
            triOffset -= triIncrement;
        }

        // Check if the location is between max and min offset

        if (abs(triOffset) >= triMaxoffset) {
            direction = !direction; // Easy way of doing a bool
        }

        curAngle += 1.0f;
        if (curAngle >= 360)
        {
            curAngle -= 360;
        }

        if (sizeDirection)
        {
            curSize += 0.001f;
        }
        else {
            curSize -= 0.001f;
        }

        if (curSize >= maxSize || curSize <= minSize) {
            sizeDirection = !sizeDirection;
        }

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.

        // Draw the triangle
        shaderList[0]->UseShader();
        uniformModel = shaderList[0]->GetModelLocation();
        uniformProjection = shaderList[0]->GetProjectionLocation();



        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        
        model = glm::translate(model, glm::vec3(triOffset, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
        //model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));

        // Rendering using the class
        meshList[0]->RenderMesh();


        model = glm::mat4(1.0f);
        model = glm::translate(model, glm::vec3(-triOffset, 1.0f, -2.5f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        meshList[1]->RenderMesh();
        


        glUseProgram(0); // Unassign the shader

        mainWindow.swapBuffers();
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

# Cleanup final code

Finish the cleanup and excluding the moving features, final code:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <vector>

#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>

#include "Window.h"
#include "Mesh.h"
#include "Shader.h"


//Checking for errors
#define ASSERT(x) if (!(x))  __debugbreak();
#define GLCall(x)  GLClearError();\
    x;\
    ASSERT(GLLogCall(#x, __FILE__, __LINE__))


static void GLClearError()
{
    while (glGetError() != GL_NO_ERROR);
}

static bool GLLogCall(const char* function, const char* file, int line)
{
    while (GLenum error = glGetError())
    {
        std::cout << "[OpenGL Error] (" << error << ")" << function <<
            " " << file << ":" << line << std::endl;
        return false;
    }
    return true;
}


const float toRadians = 3.14159265f / 180.0f;

Window mainWindow;
std::vector<Mesh*> meshList;
std::vector<Shader*> shaderList;

static const char* vShader = "Shaders/shader.vert";
static const char* fShader = "Shaders/shader.frag";


/*!
Here we are going to create a VAO (Vertex Array Object) and a VBO (Vertex Buffer Object)
The VAO will hold multiples VBOs and other types of buffer to define how a triangle should be drawn,
In this funciton is just the vertices of the triangles.
*/
void CreateObjects()
{
    unsigned int indices[] = {
        0, 3, 1,
        1, 3, 2,
        2, 3, 0,
        0, 1, 2
    };
    // Triangle Points in an array of floats (The center of the screen is 0,0)
    GLfloat vertices[] = {
        -1.0f, -1.0f, 0.0f,
         0.0f, -1.0f, 1.0f,
         1.0f, -1.0f, 0.0f,
         0.0f,  1.0f, 0.0f
    };

    Mesh *obj1 = new Mesh(); // Make sure to use pointers
    obj1->CreateMesh(vertices, indices, 12, 12);
    meshList.push_back(obj1); // add to the end of the list

    Mesh* obj2 = new Mesh(); // Make sure to use pointers
    obj2->CreateMesh(vertices, indices, 12, 12);
    meshList.push_back(obj2); // add to the end of the list
}


void CreateShaders()
{
    Shader *shader1 = new Shader();
    shader1->CreateFromFiles(vShader, fShader);
    shaderList.push_back(shader1);
}


int main()
{
    mainWindow = Window(800, 600);
    mainWindow.Initialize();

    CreateObjects();
    CreateShaders();

    GLuint uniformProjection = 0, uniformModel = 0;

    glm::mat4 projection = glm::perspective(45.0f, mainWindow.getBufferWidth() / mainWindow.getBufferHeight(), 0.1f, 100.0f); //(field of view, aspect(width/height), zNear, ZFar

    // LLop until window close
    while (!mainWindow.getShouldClose()) {
        // Get + Handle user input events
        glfwPollEvents();

        // Clear window
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color bubber bit and also the depth buffer bit, now the colors willbe correct.

        // Draw the triangle
        shaderList[0]->UseShader();
        uniformModel = shaderList[0]->GetModelLocation();
        uniformProjection = shaderList[0]->GetProjectionLocation();



        glm::mat4 model(1.0f); // Remenber, You have to initialize the model matrix variable glm::mat4 model with (1.0f) for a identity matrix
        
        model = glm::translate(model, glm::vec3(0.0f, 0.0f, -2.5f)); // Applying a translation that will only be changing the x value.
        //model = glm::rotate(model, curAngle * toRadians, glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));

        // First is the location, then quantity, then we don't want to transpose 
        // Last we pass the value pointer, because the model is not exactly in a format that will work with the shader
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(uniformProjection, 1, GL_FALSE, glm::value_ptr(projection));

        // Rendering using the class
        meshList[0]->RenderMesh();


        model = glm::mat4(1.0f);
        model = glm::translate(model, glm::vec3(0.0f, 1.0f, -2.5f));
        model = glm::scale(model, glm::vec3(0.4f, 0.4f, 1.0f));
        glUniformMatrix4fv(uniformModel, 1, GL_FALSE, glm::value_ptr(model));

        meshList[1]->RenderMesh();

        glUseProgram(0); // Unassign the shader

        mainWindow.swapBuffers();
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

Window.h

#pragma once

#include <stdio.h>

#include <GL/glew.h>
#include <GLFW/glfw3.h>

class Window
{
public:
	Window();
	Window(GLint windowWidth, GLint windowHeight);

	int Initialize();

	GLfloat getBufferWidth() { return bufferWidth; }
	GLfloat getBufferHeight() { return bufferHeight; }

	bool getShouldClose() { return glfwWindowShouldClose(mainWindow); }

	void swapBuffers() { glfwSwapBuffers(mainWindow); }

	~Window();

private:
	GLFWwindow *mainWindow;
	GLint width, height;
	GLint bufferWidth, bufferHeight;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Window.cpp

#include "Window.h"
#include <iostream>


Window::Window()
{
    width = 800;
    height = 600;
}


Window::Window(GLint windowWidth, GLint windowHeight)
{
	width = windowWidth;
	height = windowHeight;
}

int Window::Initialize()
{
    //Initialize GLFW
    if (!glfwInit()) {
        std::cout << "GLFW Initialization failed!";
        glfwTerminate();
    }

    //Setup GLFW window properties
    //OpenGL version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    //Core Profile = No Backwards Compatibility
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //Allow forward compatibility
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    mainWindow = glfwCreateWindow(width, height, "OpenGL Window", NULL, NULL);

    if (!mainWindow) {
        std::cout << " GLFW window creation failed!";
        glfwTerminate();
        return 1;
    }

    // Get Buffer size information
    glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);


    //Set the context for GLEW to use
    glfwMakeContextCurrent(mainWindow);

    //Allow modern extension features
    glewExperimental = GL_TRUE;


    GLenum error = glewInit();
    if (error != GLEW_OK) {
        std::cout << "GLEW Initialization failed!";
        glfwDestroyWindow(mainWindow);
        glfwTerminate();
        return 1;
    }

    glEnable(GL_DEPTH_TEST);

    //Setup viewport size
    glViewport(0, 0, bufferWidth, bufferHeight);
}


Window::~Window()
{
    glfwDestroyWindow(mainWindow);
    glfwTerminate();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

Mesh.h

#pragma once

#include <GL\glew.h>

class Mesh
{
public:
	Mesh();

	void CreateMesh(GLfloat *vertices, unsigned int *indices, unsigned int numofVertices, unsigned int numofIndices);
	void RenderMesh();
	void ClearMesh();

	~Mesh();

private:
	GLuint VAO, VBO, IBO;
	GLsizei indexCount; // to say how many indeces we have to draw like here, with 12: glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0); 
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Mesh.cpp

#include "Mesh.h"

Mesh::Mesh()
{
	VAO = 0;
	VBO = 0;
	IBO = 0;
	indexCount = 0;
}

void Mesh::CreateMesh(GLfloat *vertices, unsigned int *indices, unsigned int numofVertices, unsigned int numofIndices)
{
	indexCount = numofIndices;
    /*
    Creating a VAO
    Takes the amount of arrays we want to create and where to store the ID of the array.
    When we are calling this function on the graphics card is now creating a vertex array, its defining some space in the memory for some vertex array.
    So if you want to access that space in the memory you just pass the ID(VAO)).
    */
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    // Creating another buffer for the indices;
    glGenBuffers(1, &IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * numofIndices, indices, GL_STATIC_DRAW);

    // Now that we have a VAO we need to create a buffer object to actually go inside of that.
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO); //We have to enter which buffer we want to bind, as VBO has multiple targets it can bind to.
    // Now we have to connect the buffer data, the vertices that we created to the GL_BUFFER for this VBO. 
    // GL_STATIC_DRAW means we are not going to be changing the values we are putting in there, GL_DYNAMIC_DRAW is more complex.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * numofVertices, vertices, GL_STATIC_DRAW);


    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // First attribute is location of the attribute, then amount of values(x,y,z), then type, then if normalizing(we are not normalizing), then striding value(to jump 3 or whatever to read next value), then offset)
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0); // undoing the binding in the VBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0); //undoing the binding in the VAO
}

void Mesh::RenderMesh()
{
    glBindVertexArray(VAO); // We saying now work with this VAO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0); //draw by the elements IDs
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0); // Unassign the VAO
}


void Mesh::ClearMesh()
{
    if (IBO != 0)
    {
        glDeleteBuffers(1, &IBO);
        IBO = 0;
    }

    if (VBO != 0)
    {
        glDeleteBuffers(1, &VBO);
        VBO = 0;
    }

    if (VAO != 0)
    {
        glDeleteVertexArrays(1, &VAO);
        VAO = 0;
    }

    indexCount = 0;
}

Mesh::~Mesh()
{
    ClearMesh();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

Shader.h

#pragma once

#include <stdio.h>
#include <string>
#include <iostream>
#include <fstream>

#include <GL/glew.h>

class Shader
{
public:
	Shader();

	void CreateFromString(const char* vertexCode, const char* fragmentCode);
	void CreateFromFiles(const char* vertexLocation, const char* fragmentLocation);

	std::string ReadFile(const char* fileLocation);

	GLuint GetProjectionLocation();
	GLuint GetModelLocation();

	void UseShader();
	void clearShader();

	~Shader();

private:
	GLuint shaderID, uniformProjection, uniformModel;

	void CompileShader(const char* vertexCode, const char* fragmentCode);
	void AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

Shader.cpp

#include "Shader.h"

Shader::Shader()
{
	shaderID = 0;
	uniformModel = 0;
	uniformProjection = 0;
}


void Shader::CreateFromString(const char* vertexCode, const char* fragmentCode)
{
	CompileShader(vertexCode, fragmentCode);
}

void Shader::CreateFromFiles(const char* vertexLocation, const char* fragmentLocation)
{
    std::string vertexString = ReadFile(vertexLocation);
    std::string fragmentString = ReadFile(fragmentLocation);

    const char* vertexCode = vertexString.c_str();
    const char* fragmentCode = fragmentString.c_str();

    CompileShader(vertexCode, fragmentCode);
}

std::string Shader::ReadFile(const char* fileLocation)
{
    std::string content;
    std::ifstream fileStream(fileLocation, std::ios::in);

    if (!fileStream.is_open()) {
        printf("Failed to read %s File doesn't exist.", fileLocation);
        return "";
    }

    std::string line = "";
    while (!fileStream.eof())
    {
        std::getline(fileStream, line);
        content.append(line + "\n");
    }

    fileStream.close();
    return content;
}

void Shader::CompileShader(const char* vertexCode, const char* fragmentCode)
{
    shaderID = glCreateProgram(); // Create the program and give shader the ID, then we can modify the program and do something with it

        // Make sure the shader was created correctly
    if (!shaderID) {
        printf("Error creating shader program!\n");
        return;
    }
    //Add the shaders to the program using the function
    AddShader(shaderID, vertexCode, GL_VERTEX_SHADER);
    AddShader(shaderID, fragmentCode, GL_FRAGMENT_SHADER);
    // Getting the error codes, with the results of the two functions we are about to perform and a place to put the errors
    GLint result = 0;
    GLchar eLog[1024] = { 0 };


    glLinkProgram(shaderID); // Create the exectables in the graphics card.

    // Make sure the program is linked properlly
    glGetProgramiv(shaderID, GL_LINK_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shaderID, sizeof(eLog), NULL, eLog); // Getting the InfoLog for this program, passing the eLog char array and then print 
        printf("Error linking program: '%s'\n", eLog);
        return;
    }

    // Validating if the program is valid on the current context that OpenGL is working
    glValidateProgram(shaderID);
    glGetProgramiv(shaderID, GL_VALIDATE_STATUS, &result);
    if (!result)
    {
        glGetProgramInfoLog(shaderID, sizeof(eLog), NULL, eLog);
        printf("Error validating program: '%s'\n", eLog);
        return;
    }

    // Get the actual ID or location of the uniform variable
    uniformModel = glGetUniformLocation(shaderID, "model"); // uniformModel will be the location of the model matrix
    uniformProjection = glGetUniformLocation(shaderID, "projection"); // Get the projection variable and place the ID of the location intp the variable uniformProjection   
}

//Getters of uniformProjectin and uniformModel
GLuint Shader::GetProjectionLocation()
{
    return uniformProjection;
}


GLuint Shader::GetModelLocation()
{
    return uniformModel;
}



void Shader::UseShader()
{
    glUseProgram(shaderID);

}


void Shader::clearShader()
{
    if (shaderID != 0)
    {
        glDeleteProgram(shaderID); //This is deleting the program of the graphics card
        shaderID = 0;
    }

    uniformModel = 0;
    uniformProjection = 0;
}


void Shader::AddShader(GLuint theProgram, const char* shaderCode, GLenum shaderType)
{
    GLuint theShader = glCreateShader(shaderType); // Creating an empty shader for that type

    const GLchar* theCode[1]; // Passing a pointer to the first element of GLchar, so we can pass a pointer directly to it
    theCode[0] = shaderCode;

    GLint codeLength[1]; // Hold the length of the code
    codeLength[0] = strlen(shaderCode);

    glShaderSource(theShader, 1, theCode, codeLength); // Now the empty shader is modified to have this code
    glCompileShader(theShader); // compile the shader

    GLint result = 0;
    GLchar eLog[1024] = { 0 };

    // Error check before attach the shader to the program
    glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
    if (!result)
    {
        glGetShaderInfoLog(theShader, sizeof(eLog), NULL, eLog);
        printf("Error compiling the %d shader: '%s'\n", shaderType, eLog);
        return;
    }

    glAttachShader(theProgram, theShader);
}


Shader::~Shader()
{
    clearShader();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

Multiple Objects using Shaders Class