# data[].attributes.attachment_link.readable_text
1) Prepare your PC
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

2) Go to microsoft store
install Linux

3) Set up Linux
sudo apt-get update
sudo apt-get upgrade

4) Install GUI
Download and install Xming from https://sourceforge.net/projects/xming/

A better package is VcXsrv Windows X Server
https://sourceforge.net/projects/vcxsrv/

5) Tweak Ubuntu
echo "export DISPLAY=:0.0" >> ~/.bashrc
echo "export DISPLAY=localhost:0" >> ~/.bashrc
source ~/.bashrc

6) Install a graphical program
sudo apt-get install gedit

7) invoke gedit from terminal
It should work. Now you are ready to install other graphical programs

8)[Accessing Windows Directories]
C: Drive location /mnt/c do cd /mnt/c and we can access the files in window.

9)In case you want to install a lightweight one called Xfce4. 
Run each command here in turn:

sudo apt install xfce4
sudo apt install gnome-themes-standard
sudo apt install conky
then run the command
nano ~/.xinitrc
In the text editor, enter the lines of code that follow. When done, 
press Ctrl-O and hit Return to save it, and Ctrl-x to exit the text editor.
#!/usr/bin/env bash
export LANG="en_US.UTF-8
export LC_ALL="en_US.UTF-8
exec startxfce4

Run:
chmod +x ~/.xinitrc

This will make the file weve just created executable. Then:
ln -s ~/.xinitrc ~/.xsession

This will link that file with another standard X configuration file. 
Start your newly installed Linux desktop with the following command: 

dbus-launch --exit-with-session ~/.xsession

On the first run, a dialog pops upselect to use the default. 
The Xfce4 desktop appears over the Windows 10 one, so you might want to 
ensure the Ubuntu window isnt full-screen, because the window bar can be blocked. 
You should spot an Application menu top-left, theres a dock at the bottom, 
with a taskbar running along the top. To quit, use the Log out option in the 
top-left menu (see image below).

You wont find too much to play with at this point, because its a Linux desktop 
running on a largely sparse distro. You can add programs, such as Firefox, via sudo 
apt-get install firefox and this then appears under the Internet menu. Have fun!

creating a virtual machine
https://www.windowscentral.com/how-run-linux-distros-windows-10-using-hyper-v

copying files from windows to Linux installd under windows.
cp /mnt/c/Users/Rcitro/CST-310-summer/Project1-Sierpinski-Gasket/Dennis\ Canar/* .

# data[].attributes.attachment_link.readable_text
#ifndef CAMERA_H
#define CAMERA_H

#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <vector>

// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {
	FORWARD,
	BACKWARD,
	LEFT,
	RIGHT,
	UP,
	DOWN
};

// Default camera values
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
//const float SENSITIVITY = 0.1f;
const float SENSITIVITY = 0.001f;
const float ZOOM = 45.0f;


// An abstract camera class that processes input and calculates the corresponding Euler Angles, Vectors and Matrices for use in OpenGL
class Camera
{
public:
	// Camera Attributes
	glm::vec3 Position;
	glm::vec3 Front;
	glm::vec3 Up;
	glm::vec3 Right;
	glm::vec3 WorldUp;
	// Euler Angles
	float Yaw;
	float Pitch;
	// Camera options
	float MovementSpeed;
	float MouseSensitivity;
	float Zoom;

	// Constructor with vectors
	Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
	{
		Position = position;
		WorldUp = up;
		Yaw = yaw;
		Pitch = pitch;
		updateCameraVectors();
	}
	// Constructor with scalar values
	Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
	{
		Position = glm::vec3(posX, posY, posZ);
		WorldUp = glm::vec3(upX, upY, upZ);
		Yaw = yaw;
		Pitch = pitch;
		updateCameraVectors();
	}

	// Returns the view matrix calculated using Euler Angles and the LookAt Matrix
	glm::mat4 GetViewMatrix()
	{
		return glm::lookAt(Position, Position + Front, Up);
	}

	// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
	void ProcessKeyboard(Camera_Movement direction, float deltaTime)
	{
		float velocity = MovementSpeed * deltaTime;
		if (direction == FORWARD)
			Position += Front * velocity;
		if (direction == BACKWARD)
			Position -= Front * velocity;
		if (direction == LEFT)
			Position -= Right * velocity;
		if (direction == RIGHT)
			Position += Right * velocity;
		if (direction == UP)
			Position -= Up * velocity;
		if (direction == DOWN)
			Position += Up * velocity;
	}

	// Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
	void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
	{
		xoffset *= MouseSensitivity;
		yoffset *= MouseSensitivity;

		Yaw += xoffset;
		Pitch += yoffset;

		// Make sure that when pitch is out of bounds, screen doesn't get flipped
		if (constrainPitch)
		{
			if (Pitch > 89.0f)
				Pitch = 89.0f;
			if (Pitch < -89.0f)
				Pitch = -89.0f;
		}

		// Update Front, Right and Up Vectors using the updated Euler angles
		updateCameraVectors();
	}

	// Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
	void ProcessMouseScroll(float yoffset)
	{
		if (Zoom >= 1.0f && Zoom <= 45.0f)
			Zoom -= yoffset;
		if (Zoom <= 1.0f)
			Zoom = 1.0f;
		if (Zoom >= 45.0f)
			Zoom = 45.0f;
	}

private:
	// Calculates the front vector from the Camera's (updated) Euler Angles
	void updateCameraVectors()
	{
		// Calculate the new Front vector
		glm::vec3 front;
		front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
		front.y = sin(glm::radians(Pitch));
		front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
		Front = glm::normalize(front);
		// Also re-calculate the Right and Up vector
		Right = glm::normalize(glm::cross(Front, WorldUp));  // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
		Up = glm::normalize(glm::cross(Right, Front));
	}
};
#endif

# data[].attributes.attachment_link.readable_text
#include <iostream>
#include <cmath>
#include <string>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL/SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"
#include "Camera_Emilee.h"

using namespace std;


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void do_movement();
void move_cube();

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



// Camera
Camera  camera(glm::vec3(0.0f, 0.0f, 3.0f));
GLfloat lastX  =  WIDTH  / 2.0;
GLfloat lastY  =  HEIGHT / 2.0;
bool    keys[1024];

// Light attributes
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

// Deltatime
GLfloat deltaTime = 0.0f;	// Time between current frame and last frame
GLfloat lastFrame = 0.0f;  	// Time of last frame

// The MAIN function, from here we start the application and run the game loop
int main()
{
    
    float r = 0.0;
    float g = 0.0;
    float b = 0.0;
    cout << "R:"<<endl;
    cin >> r;
    cout << "G:"<<endl;
    cin >> g;
    cout<<"B:"<<endl;
    cin >> b;


    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Lighting and Colors", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // GLFW Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // OpenGL options
    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader lampShader("lamp.vs", "lamp1.frag");
    Shader lightingShader("lighting.vs", "lighting.frag");
    

    // Set up vertex data (and buffer(s)) and attribute pointers
 
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,

        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,

         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,

        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f
    };
    
 
    // First, set the container's VAO (and VBO)
    GLuint VBO, containerVAO;
    glGenVertexArrays(1, &containerVAO);
    glGenBuffers(1, &VBO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindVertexArray(containerVAO);
    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    
    // Then, we set the light's VAO (VBO stays the same. After all, the vertices are the same for the light object (also a 3D cube))
    GLuint lightVAO;
    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);
    // We only need to bind to the VBO (to link it with glVertexAttribPointer), no need to fill it; the VBO's data already contains all we need.
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // Set the vertex attributes (only position data for the lamp))
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    

    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Calculate deltatime of current frame
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();
        do_movement();

        // Clear the colorbuffer
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        //glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Use cooresponding shader when setting uniforms/drawing objects
        lightingShader.Use();
        GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor");
        GLint lightColorLoc  = glGetUniformLocation(lightingShader.Program, "lightColor");
        
        GLfloat red = r;
        GLfloat green = g;
        GLfloat blue = b;
        glUniform3f(objectColorLoc, red, green, blue);
        glUniform3f(lightColorLoc,  1.0f, 1.0f, 1.0f);

        // Create camera transformations
        glm::mat4 view = glm::mat4(1.0f);
        view = camera.GetViewMatrix();
        glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");
        GLint viewLoc  = glGetUniformLocation(lightingShader.Program,  "view");
        GLint projLoc  = glGetUniformLocation(lightingShader.Program,  "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // Draw the container (using container's vertex attributes)
        
        glm::mat4 model = glm::mat4(1.0f);
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
       
        glBindVertexArray(containerVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        // glBindVertexArray(0);

        // Also draw the lamp object, again binding the appropriate shader
        lampShader.Use();
        // Get location objects for the matrices on the lamp shader (these could be different on a different shader)
        modelLoc = glGetUniformLocation(lampShader.Program, "model");
        viewLoc  = glGetUniformLocation(lampShader.Program, "view");
        projLoc  = glGetUniformLocation(lampShader.Program, "projection");
        // Set matrices
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
        // model = glm::mat4(1.0f);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        // Draw the light object (using light's vertex attributes)
        glBindVertexArray(lightVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }

    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

void do_movement()
{
    // Camera controls
    if (keys[GLFW_KEY_W])
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (keys[GLFW_KEY_S])
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (keys[GLFW_KEY_A])
        camera.ProcessKeyboard(LEFT, deltaTime);
    //if (keys[GLFW_KEY_D])
        //camera.ProcessKeyboard(RIGHT, deltaTime);
    if (keys[GLFW_KEY_E])
        exit(0);
    if (keys[GLFW_KEY_U])
        camera.ProcessKeyboard(UP, deltaTime);
    if (keys[GLFW_KEY_L])
        camera.ProcessKeyboard(DOWN, deltaTime);
}


bool firstMouse = true;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;  // Reversed since y-coordinates go from bottom to left

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);

}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}

# data[].attributes.body
What are 3D primitives, and how do they work?

How do you render a scene in OpenGL, and is it similar to rendering scenes in Unity?

How do you perform rotations in OpenGL?

# data[].attributes.attachment_link.readable_text
include <GL/glut.h>
void renderScene(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_TRIANGLES);
        glVertex3f(-0.5,-0.5,0.0);
        glVertex3f(0.5,0.0,0.0);
        glVertex3f(0.0,0.5,0.0);
    glEnd();
    glFlush();
}

int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);
    glutInitWindowPosition(100,100);
    glutInitWindowSize(320,320);
    glutCreateWindow(�My Triangle");
    glutDisplayFunc(renderScene);
    glutMainLoop();
}

# data[].attributes.attachment_link.readable_text
include <GL/glut.h>
void renderScene(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_TRIANGLES);
        glVertex3f(-0.5,-0.5,0.0);
        glVertex3f(0.5,0.0,0.0);
        glVertex3f(0.0,0.5,0.0);
    glEnd();
    glFlush();
}

int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);
    glutInitWindowPosition(100,100);
    glutInitWindowSize(320,320);
    glutCreateWindow(� My Triangle");
    glutDisplayFunc(renderScene);
    glutMainLoop();
}

# data[].attributes.attachment_link.readable_text
#include <GL/glut.h>
#include <SOIL/SOIL.h>
#include <cstdio>

GLuint texture; // Texture ID

int view = 0;

void init() {
    glEnable(GL_DEPTH_TEST);  
    glClearColor(0.2118f, 0.4745f, 0.7490f, 1.0f);
    glMatrixMode(GL_PROJECTION);
    gluPerspective(45.0, 1.0, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);

    // Load the texture
    texture = SOIL_load_OGL_texture("brickwallimages.jpeg", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
    if (texture == 0) {
        printf("SOIL loading error: '%s'\n", SOIL_last_result());
    }

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

void drawWindow(float x, float y, float z, float rotationY = 0.0f) {// x and y are the where the windows are on the x and y plane, z is the translate value and rotationY is their rotation
    glColor3f(0.1f, 0.1f, 0.1f);  // Dark color for the window
    glPushMatrix();
    glTranslatef(x, y, z);  // Windows are slightly extruded outward
    glRotatef(rotationY, 0.0f, 1.0f, 0.0f);
    glScalef(4.5, 5, 0.15);  // Window size and slight extrusion. 2.4, 3.6, 0.15
    if (rotationY == 90.0) {
        glScalef(0.15, 0.15, 0.015);  // Window size and slight extrusion. 2.4, 3.6, 0.15
    }
    glutSolidCube(0.2);  // Draw the window
    glPopMatrix();
}

void drawDesign(float x, float y, float z, float rotationY = 0.0f) {// x and y are the where the windows are on the x and y plane, z is the translate value and rotationY is their rotation
    glColor3f(0.5156f, 0.5898f, 0.6484f); // grey color for the design
    glPushMatrix();
    glTranslatef(x, y, z);
    glRotatef(rotationY, 0.0f, 1.0f, 0.0f);
    if(rotationY == 0 && x > -7) { //close left building
    glScalef(4.5, 35, 0.15);
    }
    else if (rotationY == 0 && x < -7) { //far left building
    glScalef(11.25, 42, 0.15);
    }
    else { //right buildings
    glScalef(17, 31, 0.15);
    }
    glutSolidCube(0.2);
    glPopMatrix();
}

void drawBrickWall(float yTranslation) {
    glPushMatrix();
    glTranslatef(0.0, yTranslation, 0.0);  // Translate the floor up
    glScalef(1.5, 1.0, 2.0);  // Scale the cube to form a floor shape

    glBindTexture(GL_TEXTURE_2D, texture); // Bind the texture

    // Cube with texture coordinates
    glBegin(GL_QUADS);

    // Front face
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -0.5, 0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -0.5, 0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  0.5, 0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  0.5, 0.5);

    // Back face
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  0.5, -0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  0.5, -0.5);

    // Left face
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -0.5,  0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f(-1.0,  0.5,  0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  0.5, -0.5);

    // Right face
    glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -0.5,  0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  0.5,  0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f( 1.0,  0.5, -0.5);

    // Top face
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,  0.5, -0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,  0.5, -0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  0.5,  0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  0.5,  0.5);

    // Bottom face
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, -0.5,  0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, -0.5,  0.5);

    glEnd();
    
    glPopMatrix();
    glBindTexture(GL_TEXTURE_2D, 0);  // Unbind the texture

}

void drawBuildings(float yTranslation) {
    glColor3f(0.9569f, 0.9647f, 0.9216f);  // Beige color

    glPushMatrix();
    glTranslatef(0.0, yTranslation, 0.0);  // Translate the building up

    // We'll adjust the height of each floor of the building, so the total 
    // height of the building is 4.0 (since we want the 5th floor's top at 80% 
    // of the brick wall's total height of 10.0)
    glScalef(1.0, 4.95, 1.0);

    // FIRST RIGHT BUILDING
    
    // Cube with texture coordinates
    glBegin(GL_QUADS);

     // Front face
    glVertex3f(-2.0, -0.5, -10.5);
    glVertex3f( 1.0, -0.5, -10.5);
    glVertex3f( 1.0, 0.7, -10.5);
    glVertex3f(-2.0, 0.7, -10.5);

    // Back face
    glVertex3f(-2.0, -0.5, -2.5);
    glVertex3f( 1.0, -0.5, -2.5);
    glVertex3f( 1.0, 0.7, -2.5);
    glVertex3f(-2.0, 0.7, -2.5);

    // Left face
    glVertex3f(-2.0, -0.5, -2.5);
    glVertex3f(-2.0, -0.5, -10.5);
    glVertex3f(-2.0, 0.7, -10.5);
    glVertex3f(-2.0, 0.7, -2.5);

    // Right face
    glVertex3f( 1.0, -0.5, -2.5);
    glVertex3f( 1.0, -0.5, -10.5);
    glVertex3f( 1.0, 0.7, -10.5);
    glVertex3f( 1.0, 0.7, -2.5);

    // Top face
    glVertex3f(-2.0, 0.7, -2.5);
    glVertex3f( 1.0, 0.7, -2.5);
    glVertex3f( 1.0, 0.7, -10.5);
    glVertex3f(-2.0, 0.7, -10.5);

    // Bottom face
    glVertex3f(-2.0, -0.5, -2.5);
    glVertex3f( 1.0, -0.5, -2.5);
    glVertex3f( 1.0, -0.5, -10.5);
    glVertex3f(-2.0, -0.5, -10.5);

    glEnd();

    // SECOND RIGHT BUILDING
    // Cube with texture coordinates
    glBegin(GL_QUADS);

     // Front face
    glVertex3f(-1.0, -0.5, -22.0);
    glVertex3f( 1.0, -0.5, -22.0);
    glVertex3f( 1.0, 0.7, -22.0);
    glVertex3f(-1.0, 0.7, -22.0);

    // Back face
    glVertex3f(-1.0, -0.5, -12.0);
    glVertex3f( 1.0, -0.5, -12.0);
    glVertex3f( 1.0, 0.7, -12.0);
    glVertex3f(-1.0, 0.7, -12.0);

    // Left face
    glVertex3f(-1.0, -0.5, -12.0);
    glVertex3f(-1.0, -0.5, -22.0);
    glVertex3f(-1.0, 0.7, -22.0);
    glVertex3f(-1.0, 0.7, -12.0);

    // Right face
    glVertex3f( 1.0, -0.5, -12.0);
    glVertex3f( 1.0, -0.5, -22.0);
    glVertex3f( 1.0, 0.7, -22.0);
    glVertex3f( 1.0, 0.7, -12.0);

    // Top face
    glVertex3f(-1.0, 0.7, -12.0);
    glVertex3f( 1.0, 0.7, -12.0);
    glVertex3f( 1.0, 0.7, -22.0);
    glVertex3f(-1.0, 0.7, -22.0);

    // Bottom face
    glVertex3f(-1.0, -0.5, -12.0);
    glVertex3f( 1.0, -0.5, -12.0);
    glVertex3f( 1.0, -0.5, -22.0);
    glVertex3f(-1.0, -0.5, -22.0);

    glEnd();
    //======Currently working building
        // Third Building behind 2 right buildings
        //color needs to be a bit grey
        //move behind the building and a little more up
    // Cube with texture coordinates
    glBegin(GL_QUADS);
    glColor3f(0.3f, 0.3f, 0.3f);  // grey color
     // Front face
    glVertex3f(-3.0, -0.5, -23);
    glVertex3f( -2.0, -0.5,-23);
    glVertex3f( -2.0,  1.0,-23);
    glVertex3f(-3.0,  1.0, -23);

    // Back face
    glVertex3f(-3.0, -0.5, -3.0);
    glVertex3f( -2.0, -0.5, -3.0);
    glVertex3f( -2.0,  1.0, -3.0);
    glVertex3f(-3.0,  1.0, -3.0);

    // Left face
    glVertex3f(-3.0, -0.5, -3.0);
    glVertex3f(-3.0, -0.5, -23);
    glVertex3f(-3.0,  1.0, -23);
    glVertex3f(-3.0,  1.0, -3.0);

    // Right face
    glVertex3f( -2.0, -0.5, -3.0);
    glVertex3f( -2.0, -0.5, -23);
    glVertex3f( -2.0,  1.0, -23);
    glVertex3f( -2.0,  1.0, -3.0);

    // Top face
    glVertex3f(-3.0,  1.0, -3.0);
    glVertex3f( -2.0,  1.0, -3.0);
    glVertex3f( -2.0,  1.0, -23);
    glVertex3f(-3.0,  1.0, -23);

    // Bottom face
    glVertex3f(-3.0, -0.5, -3.0);
    glVertex3f( -2.0, -0.5, -3.0);
    glVertex3f( -2.0, -0.5, -23);
    glVertex3f(-3.0, -0.5, -23);

    glEnd();
    
    glColor3f(0.9569f, 0.9647f, 0.9216f);  // Beige color
    // FIRST LEFT BUILDING
    // Cube with texture coordinates
    glBegin(GL_QUADS);

    // Front face
    glVertex3f(-7.0, -0.5, 0.5);
    glVertex3f( -3.0, -0.5, 0.5);
    glVertex3f( -3.0,  0.5, 0.5);
    glVertex3f(-7.0,  0.5, 0.5);

    // Back face
    glVertex3f(-7.0, -0.5, -0.5);
    glVertex3f( -3.0, -0.5, -0.5);
    glVertex3f( -3.0,  0.5, -0.5);
    glVertex3f(-7.0,  0.5, -0.5);

    // Left face
    glVertex3f(-7.0, -0.5, -0.5);
    glVertex3f(-7.0, -0.5,  0.5);
    glVertex3f(-7.0,  0.5,  0.5);
    glVertex3f(-7.0,  0.5, -0.5);

    // Right face
    glVertex3f( -3.0, -0.5, -0.5);
    glVertex3f( -3.0, -0.5,  0.5);
    glVertex3f( -3.0,  0.5,  0.5);
    glVertex3f( -3.0,  0.5, -0.5);

    // Top face
    glVertex3f(-7.0,  0.5, -0.5);
    glVertex3f( -3.0,  0.5, -0.5);
    glVertex3f( -3.0,  0.5,  0.5);
    glVertex3f(-7.0,  0.5,  0.5);

    // Bottom face
    glVertex3f(-7.0, -0.5, -0.5);
    glVertex3f( -3.0, -0.5, -0.5);
    glVertex3f( -3.0, -0.5,  0.5);
    glVertex3f(-7.0, -0.5,  0.5);

    glEnd();

    // SECOND LEFT BUILDING
    // Cube with texture coordinates
    glBegin(GL_QUADS);

    // Front face
    glVertex3f(-12.0, -0.5, 0.5);
    glVertex3f( -8.0, -0.5, 0.5);
    glVertex3f( -8.0,  0.5, 0.5);
    glVertex3f(-12.0,  0.5, 0.5);

    // Back face
    glVertex3f(-12.0, -0.5, -0.5);
    glVertex3f( -8.0, -0.5, -0.5);
    glVertex3f( -8.0,  0.5, -0.5);
    glVertex3f(-12.0,  0.5, -0.5);

    // Left face
    glVertex3f(-12.0, -0.5, -0.5);
    glVertex3f(-12.0, -0.5,  0.5);
    glVertex3f(-12.0,  0.5,  0.5);
    glVertex3f(-12.0,  0.5, -0.5);

    // Right face
    glVertex3f( -8.0, -0.5, -0.5);
    glVertex3f( -8.0, -0.5,  0.5);
    glVertex3f( -8.0,  0.5,  0.5);
    glVertex3f( -8.0,  0.5, -0.5);

    // Top face
    glVertex3f(-12.0,  0.5, -0.5);
    glVertex3f( -8.0,  0.5, -0.5);
    glVertex3f( -8.0,  0.5,  0.5);
    glVertex3f(-12.0,  0.5,  0.5);

    // Bottom face
    glVertex3f(-12.0, -0.5, -0.5);
    glVertex3f( -8.0, -0.5, -0.5);
    glVertex3f( -8.0, -0.5,  0.5);
    glVertex3f(-12.0, -0.5,  0.5);

    glEnd();


    // Draw lines for edges without changing to the texture
    glColor3f(0.0f, 0.0f, 0.0f); // Set color to black for lines
    glLineWidth(1.0f); // Thin lines
    
    // Drawing the lines for the edges
    glBegin(GL_LINES);

    // Front and back edges
    glVertex3f(-1.0, -0.5, -22.0); glVertex3f(-1.0, 0.7, -22.0);
    glVertex3f( 1.0, -0.5, -22.0); glVertex3f( 1.0, 0.7, -22.0);
    glVertex3f(-1.0, -0.5, -12.0); glVertex3f(-1.0, 0.7, -12.0);
    glVertex3f( 1.0, -0.5, -12.0); glVertex3f( 1.0, 0.7, -12.0);

    // Left and right edges
    glVertex3f(-1.0, -0.5, -22.0); glVertex3f(-1.0, -0.5, -12.0);
    glVertex3f( 1.0, -0.5, -22.0); glVertex3f( 1.0, -0.5, -12.0);
    glVertex3f(-1.0, 0.7, -22.0); glVertex3f(-1.0, 0.7, -12.0);
    glVertex3f( 1.0, 0.7, -22.0); glVertex3f( 1.0, 0.7, -12.0);

    // Bottom and top edges
    glVertex3f(-1.0, -0.5, -22.0); glVertex3f( 1.0, -0.5, -22.0);
    glVertex3f(-1.0, 0.7, -22.0); glVertex3f( 1.0, 0.7, -22.0);
    glVertex3f(-1.0, -0.5, -12.0); glVertex3f( 1.0, -0.5, -12.0);
    glVertex3f(-1.0, 0.7, -12.0); glVertex3f( 1.0, 0.7, -12.0);

    glEnd();

    // Drawing the lines for the edges
    glBegin(GL_LINES);

    // Front and back edges
    glVertex3f(-1.0, -0.5, -10.5); glVertex3f(-1.0, 0.7, -10.5);
    glVertex3f( 1.0, -0.5, -10.5); glVertex3f( 1.0, 0.7, -10.5);
    glVertex3f(-1.0, -0.5, -2.5); glVertex3f(-1.0, 0.7, -2.5);
    glVertex3f( 1.0, -0.5, -2.5); glVertex3f( 1.0, 0.7, -2.5);

    // Left and right edges
    glVertex3f(-1.0, -0.5, -10.5); glVertex3f(-1.0, -0.5, -2.5);
    glVertex3f( 1.0, -0.5, -10.5); glVertex3f( 1.0, -0.5, -2.5);
    glVertex3f(-1.0, 0.7, -10.5); glVertex3f(-1.0, 0.7, -2.5);
    glVertex3f( 1.0, 0.7, -10.5); glVertex3f( 1.0, 0.7, -2.5);

    // Bottom and top edges
    glVertex3f(-1.0, -0.5, -10.5); glVertex3f( 1.0, -0.5, -10.5);
    glVertex3f(-1.0, 0.7, -10.5); glVertex3f( 1.0, 0.7, -10.5);
    glVertex3f(-1.0, -0.5, -2.5); glVertex3f( 1.0, -0.5, -2.5);
    glVertex3f(-1.0, 0.7, -2.5); glVertex3f( 1.0, 0.7, -2.5);

    glEnd();

    glBegin(GL_LINES);

    // Front and back edges
    glVertex3f(-7.0, -0.5, -0.5); glVertex3f(-7.0,  0.5, -0.5);
    glVertex3f( -3.0, -0.5, -0.5); glVertex3f( -3.0,  0.5, -0.5);
    glVertex3f(-7.0, -0.5, 0.5); glVertex3f(-7.0,  0.5, 0.5);
    glVertex3f( -3.0, -0.5, 0.5); glVertex3f( -3.0,  0.5, 0.5);

    // Left and right edges
    glVertex3f(-7.0, -0.5, -0.5); glVertex3f(-7.0, -0.5, 0.5);
    glVertex3f( -3.0, -0.5, -0.5); glVertex3f( -3.0, -0.5, 0.5);
    glVertex3f(-7.0,  0.5, 0.5); glVertex3f(-7.0,  0.5, 0.5);
    glVertex3f( -3.0,  0.5, 0.5); glVertex3f( -3.0,  0.5, 0.5);

    // Bottom and top edges
    glVertex3f(-7.0, -0.5, -0.5); glVertex3f( -3.0, -0.5, -0.5);
    glVertex3f(-7.0,  0.5, -0.5); glVertex3f( -3.0,  0.5, -0.5);
    glVertex3f(-7.0, -0.5, 0.5); glVertex3f( -3.0, -0.5, 0.5);
    glVertex3f(-7.0,  0.5, 0.5); glVertex3f( -3.0,  0.5, 0.5);

    glEnd();

    glBegin(GL_LINES);

    // Front and back edges
    glVertex3f(-12.0, -0.5, -0.5); glVertex3f(-12.0,  0.5, -0.5);
    glVertex3f( -8.0, -0.5, -0.5); glVertex3f( -8.0,  0.5, -0.5);
    glVertex3f(-12.0, -0.5, 0.5); glVertex3f(-12.0,  0.5, 0.5);
    glVertex3f( -8.0, -0.5, 0.5); glVertex3f( -8.0,  0.5, 0.5);

    // Left and right edges
    glVertex3f(-12.0, -0.5, -0.5); glVertex3f(-12.0, -0.5, 0.5);
    glVertex3f( -8.0, -0.5, -0.5); glVertex3f( -8.0, -0.5, 0.5);
    glVertex3f(-12.0,  0.5, 0.5); glVertex3f(-12.0,  0.5, 0.5);
    glVertex3f( -8.0,  0.5, 0.5); glVertex3f( -8.0,  0.5, 0.5);

    // Bottom and top edges
    glVertex3f(-12.0, -0.5, -0.5); glVertex3f( -8.0, -0.5, -0.5);
    glVertex3f(-12.0,  0.5, -0.5); glVertex3f( -8.0,  0.5, -0.5);
    glVertex3f(-12.0, -0.5, 0.5); glVertex3f( -8.0, -0.5, 0.5);
    glVertex3f(-12.0,  0.5, 0.5); glVertex3f( -8.0,  0.5, 0.5);
    
    //windows
    drawWindow(-2, -2, .6); //close left
    for (int i = -2; i <= 2; i+=2) {
        for (int j = -10; j < 3; j+=2.8) {
            drawWindow(i * .7 - 5, j * 1.2 - .2, .61);
        }
    }
    
    for (int i = -2; i <= 2; i+=2) { //far left
        for (int j = -10; j < 3; j+=2.8) {
            drawWindow(i * .7 - 10, j * 1.2 - .2, .61);
        }
    }
    
    for (int i = -2; i <= 2; i+=2) { //close right
        for (int j = -6; j < 3; j+=2) {
            drawWindow(1.1 , j * 1.3 -0.7, i * 1.2 - 6, -90.0f);
        }
    }
    
    for (int i = -2; i <= 2; i+=2) { //far right
        for (int j = -6; j < 3; j+=2) {
            drawWindow(1.1 , j * 1.3 - 0.7, i * 1.2 - 16, -90.0f);
        }
    }
    
    // Brickwall "window like" design
    for (int j = -14; j < 3.5; j+=1) {
        drawWindow(2.5 , j * 0.5, 1.2, 90.0f);
        drawWindow(2.5 , j * 0.5, 0.6, 90.0f);
    }
    for (float j = -12.5; j < 3; j+=1) {
        drawWindow(2.5 , j * 0.5, 0.9, 90.0f);
    }


    //Design
    drawDesign(-6.5, -2, .6); //close left first windows from brick
    drawDesign(-3.7, -9, .6); //close left second windows from brick
    drawDesign(-10.75, -8.7, .6); //far right
    drawDesign(1, -0.7, -4.9, -90.0f); //close right
    drawDesign(1, -8.5, -15, -90.0f); //far right
    
 	   

    glEnd();

    glColor3f(1.0f, 1.0f, 1.0f); // Set color to black for lines

    glPopMatrix();
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    // Adjust the camera view based on the current view index
    switch (view) {
    case 0:
        gluLookAt(5.0, -1.2, 12, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // Main view same as picture
        break;
    case 1:
        gluLookAt(5, 20, 25, 0, 0, 0, 0, 1, 0); // Left side view
        break;
    case 2:
        gluLookAt(6, 30, 6, 0, 0, 0, 0, 1, 0); // Right side view
        break;
    }

    glRotatef(-30.0, 0.0f, 1.0f, 0.0f);
    glTranslatef(-5.5, 1.0, -0.20);
    
    // Draw the building floors
    for (int i = 0; i < 4; i++) {
        drawBuildings(-13 + i * 0.9 * 4.95);
    }

    for (int i = 0; i < 15; i++) {
        glBindTexture(GL_TEXTURE_2D, texture);
        drawBrickWall(-10 + i * 1.0);
    }

    

    glutSwapBuffers();
}


// Keyboard callback function for camera view
void keyboard(unsigned char key, int x, int y) {
    if (key == '1') { // If 1 is pressed
        view = (view + 1) % 3; // Cycle through the three views
        glutPostRedisplay(); // Request to refresh the display
    }
}

void timer(int value) {
    glutPostRedisplay();
    glutTimerFunc(16, timer, 0);
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Simple 4-Floor Building with Windows");

    init();
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard); //for changing view
    glutTimerFunc(0, timer, 0);
    glutMainLoop();
    return 0;
}

# data[].attributes.attachment_link.readable_text
#include <GL/glut.h>
#include <SOIL/SOIL.h>
#include <cstdio>

GLuint texture; // Texture ID

int view = 0;

void init() {
    glEnable(GL_DEPTH_TEST);  
    glClearColor(0.2118f, 0.4745f, 0.7490f, 1.0f);
    glMatrixMode(GL_PROJECTION);
    gluPerspective(45.0, 1.0, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);

    // Load the texture
    texture = SOIL_load_OGL_texture("brickwallimages.jpeg", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
    if (texture == 0) {
        printf("SOIL loading error: '%s'\n", SOIL_last_result());
    }

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

void drawWindow(float x, float y, float z, float rotationY = 0.0f) {// x and y are the where the windows are on the x and y plane, z is the translate value and rotationY is their rotation
    glColor3f(0.1f, 0.1f, 0.1f);  // Dark color for the window
    glPushMatrix();
    glTranslatef(x, y, z);  // Windows are slightly extruded outward
    glRotatef(rotationY, 0.0f, 1.0f, 0.0f);
    glScalef(4.5, 5, 0.15);  // Window size and slight extrusion. 2.4, 3.6, 0.15
    if (rotationY == 90.0) {
        glScalef(0.15, 0.15, 0.015);  // Window size and slight extrusion. 2.4, 3.6, 0.15
    }
    glutSolidCube(0.2);  // Draw the window
    glPopMatrix();
}

void drawDesign(float x, float y, float z, float rotationY = 0.0f) {// x and y are the where the windows are on the x and y plane, z is the translate value and rotationY is their rotation
    glColor3f(0.5156f, 0.5898f, 0.6484f); // grey color for the design
    glPushMatrix();
    glTranslatef(x, y, z);
    glRotatef(rotationY, 0.0f, 1.0f, 0.0f);
    if(rotationY == 0 && x > -7) { //close left building
    glScalef(4.5, 35, 0.15);
    }
    else if (rotationY == 0 && x < -7) { //far left building
    glScalef(11.25, 42, 0.15);
    }
    else { //right buildings
    glScalef(17, 31, 0.15);
    }
    glutSolidCube(0.2);
    glPopMatrix();
}

void drawBrickWall(float yTranslation) {
    glPushMatrix();
    glTranslatef(0.0, yTranslation, 0.0);  // Translate the floor up
    glScalef(1.5, 1.0, 2.0);  // Scale the cube to form a floor shape

    glBindTexture(GL_TEXTURE_2D, texture); // Bind the texture

    // Cube with texture coordinates
    glBegin(GL_QUADS);

    // Front face
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -0.5, 0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -0.5, 0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  0.5, 0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  0.5, 0.5);

    // Back face
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  0.5, -0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  0.5, -0.5);

    // Left face
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -0.5,  0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f(-1.0,  0.5,  0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  0.5, -0.5);

    // Right face
    glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -0.5,  0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  0.5,  0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f( 1.0,  0.5, -0.5);

    // Top face
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,  0.5, -0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,  0.5, -0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  0.5,  0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  0.5,  0.5);

    // Bottom face
    glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -0.5, -0.5);
    glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, -0.5,  0.5);
    glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, -0.5,  0.5);

    glEnd();
    
    glPopMatrix();
    glBindTexture(GL_TEXTURE_2D, 0);  // Unbind the texture

}

void drawBuildings(float yTranslation) {
    glColor3f(0.9569f, 0.9647f, 0.9216f);  // Beige color

    glPushMatrix();
    glTranslatef(0.0, yTranslation, 0.0);  // Translate the building up

    // We'll adjust the height of each floor of the building, so the total 
    // height of the building is 4.0 (since we want the 5th floor's top at 80% 
    // of the brick wall's total height of 10.0)
    glScalef(1.0, 4.95, 1.0);

    // FIRST RIGHT BUILDING
    
    // Cube with texture coordinates
    glBegin(GL_QUADS);

     // Front face
    glVertex3f(-2.0, -0.5, -10.5);
    glVertex3f( 1.0, -0.5, -10.5);
    glVertex3f( 1.0, 0.7, -10.5);
    glVertex3f(-2.0, 0.7, -10.5);

    // Back face
    glVertex3f(-2.0, -0.5, -2.5);
    glVertex3f( 1.0, -0.5, -2.5);
    glVertex3f( 1.0, 0.7, -2.5);
    glVertex3f(-2.0, 0.7, -2.5);

    // Left face
    glVertex3f(-2.0, -0.5, -2.5);
    glVertex3f(-2.0, -0.5, -10.5);
    glVertex3f(-2.0, 0.7, -10.5);
    glVertex3f(-2.0, 0.7, -2.5);

    // Right face
    glVertex3f( 1.0, -0.5, -2.5);
    glVertex3f( 1.0, -0.5, -10.5);
    glVertex3f( 1.0, 0.7, -10.5);
    glVertex3f( 1.0, 0.7, -2.5);

    // Top face
    glVertex3f(-2.0, 0.7, -2.5);
    glVertex3f( 1.0, 0.7, -2.5);
    glVertex3f( 1.0, 0.7, -10.5);
    glVertex3f(-2.0, 0.7, -10.5);

    // Bottom face
    glVertex3f(-2.0, -0.5, -2.5);
    glVertex3f( 1.0, -0.5, -2.5);
    glVertex3f( 1.0, -0.5, -10.5);
    glVertex3f(-2.0, -0.5, -10.5);

    glEnd();

    // SECOND RIGHT BUILDING
    // Cube with texture coordinates
    glBegin(GL_QUADS);

     // Front face
    glVertex3f(-1.0, -0.5, -22.0);
    glVertex3f( 1.0, -0.5, -22.0);
    glVertex3f( 1.0, 0.7, -22.0);
    glVertex3f(-1.0, 0.7, -22.0);

    // Back face
    glVertex3f(-1.0, -0.5, -12.0);
    glVertex3f( 1.0, -0.5, -12.0);
    glVertex3f( 1.0, 0.7, -12.0);
    glVertex3f(-1.0, 0.7, -12.0);

    // Left face
    glVertex3f(-1.0, -0.5, -12.0);
    glVertex3f(-1.0, -0.5, -22.0);
    glVertex3f(-1.0, 0.7, -22.0);
    glVertex3f(-1.0, 0.7, -12.0);

    // Right face
    glVertex3f( 1.0, -0.5, -12.0);
    glVertex3f( 1.0, -0.5, -22.0);
    glVertex3f( 1.0, 0.7, -22.0);
    glVertex3f( 1.0, 0.7, -12.0);

    // Top face
    glVertex3f(-1.0, 0.7, -12.0);
    glVertex3f( 1.0, 0.7, -12.0);
    glVertex3f( 1.0, 0.7, -22.0);
    glVertex3f(-1.0, 0.7, -22.0);

    // Bottom face
    glVertex3f(-1.0, -0.5, -12.0);
    glVertex3f( 1.0, -0.5, -12.0);
    glVertex3f( 1.0, -0.5, -22.0);
    glVertex3f(-1.0, -0.5, -22.0);

    glEnd();
    //======Currently working building
        // Third Building behind 2 right buildings
        //color needs to be a bit grey
        //move behind the building and a little more up
    // Cube with texture coordinates
    glBegin(GL_QUADS);
    glColor3f(0.3f, 0.3f, 0.3f);  // grey color
     // Front face
    glVertex3f(-3.0, -0.5, -23);
    glVertex3f( -2.0, -0.5,-23);
    glVertex3f( -2.0,  1.0,-23);
    glVertex3f(-3.0,  1.0, -23);

    // Back face
    glVertex3f(-3.0, -0.5, -3.0);
    glVertex3f( -2.0, -0.5, -3.0);
    glVertex3f( -2.0,  1.0, -3.0);
    glVertex3f(-3.0,  1.0, -3.0);

    // Left face
    glVertex3f(-3.0, -0.5, -3.0);
    glVertex3f(-3.0, -0.5, -23);
    glVertex3f(-3.0,  1.0, -23);
    glVertex3f(-3.0,  1.0, -3.0);

    // Right face
    glVertex3f( -2.0, -0.5, -3.0);
    glVertex3f( -2.0, -0.5, -23);
    glVertex3f( -2.0,  1.0, -23);
    glVertex3f( -2.0,  1.0, -3.0);

    // Top face
    glVertex3f(-3.0,  1.0, -3.0);
    glVertex3f( -2.0,  1.0, -3.0);
    glVertex3f( -2.0,  1.0, -23);
    glVertex3f(-3.0,  1.0, -23);

    // Bottom face
    glVertex3f(-3.0, -0.5, -3.0);
    glVertex3f( -2.0, -0.5, -3.0);
    glVertex3f( -2.0, -0.5, -23);
    glVertex3f(-3.0, -0.5, -23);

    glEnd();
    
    glColor3f(0.9569f, 0.9647f, 0.9216f);  // Beige color
    // FIRST LEFT BUILDING
    // Cube with texture coordinates
    glBegin(GL_QUADS);

    // Front face
    glVertex3f(-7.0, -0.5, 0.5);
    glVertex3f( -3.0, -0.5, 0.5);
    glVertex3f( -3.0,  0.5, 0.5);
    glVertex3f(-7.0,  0.5, 0.5);

    // Back face
    glVertex3f(-7.0, -0.5, -0.5);
    glVertex3f( -3.0, -0.5, -0.5);
    glVertex3f( -3.0,  0.5, -0.5);
    glVertex3f(-7.0,  0.5, -0.5);

    // Left face
    glVertex3f(-7.0, -0.5, -0.5);
    glVertex3f(-7.0, -0.5,  0.5);
    glVertex3f(-7.0,  0.5,  0.5);
    glVertex3f(-7.0,  0.5, -0.5);

    // Right face
    glVertex3f( -3.0, -0.5, -0.5);
    glVertex3f( -3.0, -0.5,  0.5);
    glVertex3f( -3.0,  0.5,  0.5);
    glVertex3f( -3.0,  0.5, -0.5);

    // Top face
    glVertex3f(-7.0,  0.5, -0.5);
    glVertex3f( -3.0,  0.5, -0.5);
    glVertex3f( -3.0,  0.5,  0.5);
    glVertex3f(-7.0,  0.5,  0.5);

    // Bottom face
    glVertex3f(-7.0, -0.5, -0.5);
    glVertex3f( -3.0, -0.5, -0.5);
    glVertex3f( -3.0, -0.5,  0.5);
    glVertex3f(-7.0, -0.5,  0.5);

    glEnd();

    // SECOND LEFT BUILDING
    // Cube with texture coordinates
    glBegin(GL_QUADS);

    // Front face
    glVertex3f(-12.0, -0.5, 0.5);
    glVertex3f( -8.0, -0.5, 0.5);
    glVertex3f( -8.0,  0.5, 0.5);
    glVertex3f(-12.0,  0.5, 0.5);

    // Back face
    glVertex3f(-12.0, -0.5, -0.5);
    glVertex3f( -8.0, -0.5, -0.5);
    glVertex3f( -8.0,  0.5, -0.5);
    glVertex3f(-12.0,  0.5, -0.5);

    // Left face
    glVertex3f(-12.0, -0.5, -0.5);
    glVertex3f(-12.0, -0.5,  0.5);
    glVertex3f(-12.0,  0.5,  0.5);
    glVertex3f(-12.0,  0.5, -0.5);

    // Right face
    glVertex3f( -8.0, -0.5, -0.5);
    glVertex3f( -8.0, -0.5,  0.5);
    glVertex3f( -8.0,  0.5,  0.5);
    glVertex3f( -8.0,  0.5, -0.5);

    // Top face
    glVertex3f(-12.0,  0.5, -0.5);
    glVertex3f( -8.0,  0.5, -0.5);
    glVertex3f( -8.0,  0.5,  0.5);
    glVertex3f(-12.0,  0.5,  0.5);

    // Bottom face
    glVertex3f(-12.0, -0.5, -0.5);
    glVertex3f( -8.0, -0.5, -0.5);
    glVertex3f( -8.0, -0.5,  0.5);
    glVertex3f(-12.0, -0.5,  0.5);

    glEnd();


    // Draw lines for edges without changing to the texture
    glColor3f(0.0f, 0.0f, 0.0f); // Set color to black for lines
    glLineWidth(1.0f); // Thin lines
    
    // Drawing the lines for the edges
    glBegin(GL_LINES);

    // Front and back edges
    glVertex3f(-1.0, -0.5, -22.0); glVertex3f(-1.0, 0.7, -22.0);
    glVertex3f( 1.0, -0.5, -22.0); glVertex3f( 1.0, 0.7, -22.0);
    glVertex3f(-1.0, -0.5, -12.0); glVertex3f(-1.0, 0.7, -12.0);
    glVertex3f( 1.0, -0.5, -12.0); glVertex3f( 1.0, 0.7, -12.0);

    // Left and right edges
    glVertex3f(-1.0, -0.5, -22.0); glVertex3f(-1.0, -0.5, -12.0);
    glVertex3f( 1.0, -0.5, -22.0); glVertex3f( 1.0, -0.5, -12.0);
    glVertex3f(-1.0, 0.7, -22.0); glVertex3f(-1.0, 0.7, -12.0);
    glVertex3f( 1.0, 0.7, -22.0); glVertex3f( 1.0, 0.7, -12.0);

    // Bottom and top edges
    glVertex3f(-1.0, -0.5, -22.0); glVertex3f( 1.0, -0.5, -22.0);
    glVertex3f(-1.0, 0.7, -22.0); glVertex3f( 1.0, 0.7, -22.0);
    glVertex3f(-1.0, -0.5, -12.0); glVertex3f( 1.0, -0.5, -12.0);
    glVertex3f(-1.0, 0.7, -12.0); glVertex3f( 1.0, 0.7, -12.0);

    glEnd();

    // Drawing the lines for the edges
    glBegin(GL_LINES);

    // Front and back edges
    glVertex3f(-1.0, -0.5, -10.5); glVertex3f(-1.0, 0.7, -10.5);
    glVertex3f( 1.0, -0.5, -10.5); glVertex3f( 1.0, 0.7, -10.5);
    glVertex3f(-1.0, -0.5, -2.5); glVertex3f(-1.0, 0.7, -2.5);
    glVertex3f( 1.0, -0.5, -2.5); glVertex3f( 1.0, 0.7, -2.5);

    // Left and right edges
    glVertex3f(-1.0, -0.5, -10.5); glVertex3f(-1.0, -0.5, -2.5);
    glVertex3f( 1.0, -0.5, -10.5); glVertex3f( 1.0, -0.5, -2.5);
    glVertex3f(-1.0, 0.7, -10.5); glVertex3f(-1.0, 0.7, -2.5);
    glVertex3f( 1.0, 0.7, -10.5); glVertex3f( 1.0, 0.7, -2.5);

    // Bottom and top edges
    glVertex3f(-1.0, -0.5, -10.5); glVertex3f( 1.0, -0.5, -10.5);
    glVertex3f(-1.0, 0.7, -10.5); glVertex3f( 1.0, 0.7, -10.5);
    glVertex3f(-1.0, -0.5, -2.5); glVertex3f( 1.0, -0.5, -2.5);
    glVertex3f(-1.0, 0.7, -2.5); glVertex3f( 1.0, 0.7, -2.5);

    glEnd();

    glBegin(GL_LINES);

    // Front and back edges
    glVertex3f(-7.0, -0.5, -0.5); glVertex3f(-7.0,  0.5, -0.5);
    glVertex3f( -3.0, -0.5, -0.5); glVertex3f( -3.0,  0.5, -0.5);
    glVertex3f(-7.0, -0.5, 0.5); glVertex3f(-7.0,  0.5, 0.5);
    glVertex3f( -3.0, -0.5, 0.5); glVertex3f( -3.0,  0.5, 0.5);

    // Left and right edges
    glVertex3f(-7.0, -0.5, -0.5); glVertex3f(-7.0, -0.5, 0.5);
    glVertex3f( -3.0, -0.5, -0.5); glVertex3f( -3.0, -0.5, 0.5);
    glVertex3f(-7.0,  0.5, 0.5); glVertex3f(-7.0,  0.5, 0.5);
    glVertex3f( -3.0,  0.5, 0.5); glVertex3f( -3.0,  0.5, 0.5);

    // Bottom and top edges
    glVertex3f(-7.0, -0.5, -0.5); glVertex3f( -3.0, -0.5, -0.5);
    glVertex3f(-7.0,  0.5, -0.5); glVertex3f( -3.0,  0.5, -0.5);
    glVertex3f(-7.0, -0.5, 0.5); glVertex3f( -3.0, -0.5, 0.5);
    glVertex3f(-7.0,  0.5, 0.5); glVertex3f( -3.0,  0.5, 0.5);

    glEnd();

    glBegin(GL_LINES);

    // Front and back edges
    glVertex3f(-12.0, -0.5, -0.5); glVertex3f(-12.0,  0.5, -0.5);
    glVertex3f( -8.0, -0.5, -0.5); glVertex3f( -8.0,  0.5, -0.5);
    glVertex3f(-12.0, -0.5, 0.5); glVertex3f(-12.0,  0.5, 0.5);
    glVertex3f( -8.0, -0.5, 0.5); glVertex3f( -8.0,  0.5, 0.5);

    // Left and right edges
    glVertex3f(-12.0, -0.5, -0.5); glVertex3f(-12.0, -0.5, 0.5);
    glVertex3f( -8.0, -0.5, -0.5); glVertex3f( -8.0, -0.5, 0.5);
    glVertex3f(-12.0,  0.5, 0.5); glVertex3f(-12.0,  0.5, 0.5);
    glVertex3f( -8.0,  0.5, 0.5); glVertex3f( -8.0,  0.5, 0.5);

    // Bottom and top edges
    glVertex3f(-12.0, -0.5, -0.5); glVertex3f( -8.0, -0.5, -0.5);
    glVertex3f(-12.0,  0.5, -0.5); glVertex3f( -8.0,  0.5, -0.5);
    glVertex3f(-12.0, -0.5, 0.5); glVertex3f( -8.0, -0.5, 0.5);
    glVertex3f(-12.0,  0.5, 0.5); glVertex3f( -8.0,  0.5, 0.5);
    
    //windows
    drawWindow(-2, -2, .6); //close left
    for (int i = -2; i <= 2; i+=2) {
        for (int j = -10; j < 3; j+=2.8) {
            drawWindow(i * .7 - 5, j * 1.2 - .2, .61);
        }
    }
    
    for (int i = -2; i <= 2; i+=2) { //far left
        for (int j = -10; j < 3; j+=2.8) {
            drawWindow(i * .7 - 10, j * 1.2 - .2, .61);
        }
    }
    
    for (int i = -2; i <= 2; i+=2) { //close right
        for (int j = -6; j < 3; j+=2) {
            drawWindow(1.1 , j * 1.3 -0.7, i * 1.2 - 6, -90.0f);
        }
    }
    
    for (int i = -2; i <= 2; i+=2) { //far right
        for (int j = -6; j < 3; j+=2) {
            drawWindow(1.1 , j * 1.3 - 0.7, i * 1.2 - 16, -90.0f);
        }
    }
    
    // Brickwall "window like" design
    for (int j = -14; j < 3.5; j+=1) {
        drawWindow(2.5 , j * 0.5, 1.2, 90.0f);
        drawWindow(2.5 , j * 0.5, 0.6, 90.0f);
    }
    for (float j = -12.5; j < 3; j+=1) {
        drawWindow(2.5 , j * 0.5, 0.9, 90.0f);
    }


    //Design
    drawDesign(-6.5, -2, .6); //close left first windows from brick
    drawDesign(-3.7, -9, .6); //close left second windows from brick
    drawDesign(-10.75, -8.7, .6); //far right
    drawDesign(1, -0.7, -4.9, -90.0f); //close right
    drawDesign(1, -8.5, -15, -90.0f); //far right
    
 	   

    glEnd();

    glColor3f(1.0f, 1.0f, 1.0f); // Set color to black for lines

    glPopMatrix();
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    // Adjust the camera view based on the current view index
    switch (view) {
    case 0:
        gluLookAt(5.0, -1.2, 12, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // Main view same as picture
        break;
    case 1:
        gluLookAt(5, 20, 25, 0, 0, 0, 0, 1, 0); // Left side view
        break;
    case 2:
        gluLookAt(6, 30, 6, 0, 0, 0, 0, 1, 0); // Right side view
        break;
    }

    glRotatef(-30.0, 0.0f, 1.0f, 0.0f);
    glTranslatef(-5.5, 1.0, -0.20);
    
    // Draw the building floors
    for (int i = 0; i < 4; i++) {
        drawBuildings(-13 + i * 0.9 * 4.95);
    }

    for (int i = 0; i < 15; i++) {
        glBindTexture(GL_TEXTURE_2D, texture);
        drawBrickWall(-10 + i * 1.0);
    }

    

    glutSwapBuffers();
}


// Keyboard callback function for camera view
void keyboard(unsigned char key, int x, int y) {
    if (key == '1') { // If 1 is pressed
        view = (view + 1) % 3; // Cycle through the three views
        glutPostRedisplay(); // Request to refresh the display
    }
}

void timer(int value) {
    glutPostRedisplay();
    glutTimerFunc(16, timer, 0);
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Simple 4-Floor Building with Windows");

    init();
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard); //for changing view
    glutTimerFunc(0, timer, 0);
    glutMainLoop();
    return 0;
}

# data[].attributes.attachment_link.readable_text
#include <iostream>
#include <cmath>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL/SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"
#include "Camera.h"

// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void do_movement();

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

// Camera
Camera  camera(glm::vec3(0.0f, 0.0f, 3.0f));
GLfloat lastX  =  WIDTH  / 2.0;
GLfloat lastY  =  HEIGHT / 2.0;
bool    keys[1024];

// Light attributes
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

// Deltatime
GLfloat deltaTime = 0.0f;	// Time between current frame and last frame
GLfloat lastFrame = 0.0f;  	// Time of last frame

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Lighting and Colors", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // GLFW Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // OpenGL options
    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader lampShader("lamp.vs", "lamp1.fs");
    Shader lightingShader("lighting.vs", "lighting.frag");
    

    // Set up vertex data (and buffer(s)) and attribute pointers
 
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,

        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,

         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,

        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f
    };
    
 
    // First, set the container's VAO (and VBO)
    GLuint VBO, containerVAO;
    glGenVertexArrays(1, &containerVAO);
    glGenBuffers(1, &VBO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindVertexArray(containerVAO);
    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    
    // Then, we set the light's VAO (VBO stays the same. After all, the vertices are the same for the light object (also a 3D cube))
    GLuint lightVAO;
    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);
    // We only need to bind to the VBO (to link it with glVertexAttribPointer), no need to fill it; the VBO's data already contains all we need.
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // Set the vertex attributes (only position data for the lamp))
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    

    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Calculate deltatime of current frame
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();
        do_movement();

        // Clear the colorbuffer
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        //glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Use cooresponding shader when setting uniforms/drawing objects
        lightingShader.Use();
        GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor");
        GLint lightColorLoc  = glGetUniformLocation(lightingShader.Program, "lightColor");
        glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);
        glUniform3f(lightColorLoc,  1.0f, 1.0f, 1.0f);

        // Create camera transformations
        glm::mat4 view = glm::mat4(1.0f);
        view = camera.GetViewMatrix();
        glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");
        GLint viewLoc  = glGetUniformLocation(lightingShader.Program,  "view");
        GLint projLoc  = glGetUniformLocation(lightingShader.Program,  "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // Draw the container (using container's vertex attributes)
        glBindVertexArray(containerVAO);
        glm::mat4 model = glm::mat4(1.0f);
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // Also draw the lamp object, again binding the appropriate shader
        lampShader.Use();
        // Get location objects for the matrices on the lamp shader (these could be different on a different shader)
        modelLoc = glGetUniformLocation(lampShader.Program, "model");
        viewLoc  = glGetUniformLocation(lampShader.Program, "view");
        projLoc  = glGetUniformLocation(lampShader.Program, "projection");
        // Set matrices
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
        model = glm::mat4(1.0f);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        // Draw the light object (using light's vertex attributes)
        glBindVertexArray(lightVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }

    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

void do_movement()
{
    // Camera controls
    if (keys[GLFW_KEY_F])
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (keys[GLFW_KEY_B])
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (keys[GLFW_KEY_R])
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (keys[GLFW_KEY_L])
        camera.ProcessKeyboard(RIGHT, deltaTime);
    if (keys[GLFW_KEY_D])
        camera.ProcessKeyboard(UP, deltaTime);
    if (keys[GLFW_KEY_U])
        camera.ProcessKeyboard(DOWN, deltaTime);
    if (keys[GLFW_KEY_E])
        exit(0);
    
}

bool firstMouse = true;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;  // Reversed since y-coordinates go from bottom to left

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);

}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}

# data[].attributes.body
sudo apt-get install libglfw3 libglfw3-dev

# data[].attributes.body
The dat file needed to run the moving neck dino

# data[].attributes.body
Updated Moving Neck dino

# data[].attributes.attachment_link.readable_text
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <fstream>
#include <vector>

std::vector<std::vector<std::pair<GLfloat, GLfloat>>> polylines;
GLfloat dinosaurPosition = 0.0f;  
GLfloat legAngle = 0.0f;          
GLfloat direction = 1.0f;         
GLfloat walkDirection = 2.0f;  
GLfloat headNeckAngle = 0.0f;   

void drawPolyLine(const std::vector<std::pair<GLfloat, GLfloat>>& polyline) {
    glBegin(GL_LINE_STRIP);
    for (const auto& point : polyline) {
        glVertex2f(point.first, point.second);
    }
    glEnd();
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT);

    glPushMatrix();
    glTranslatef(dinosaurPosition, 0.0f, 0.0f);

    for (int iPoly = 0; iPoly < polylines.size(); iPoly++) {
        if (iPoly == 2) {  // Front Left leg
            glPushMatrix();
            glTranslatef(110, 150, 0);  // Adjust these values for the pivot point
            glRotatef(legAngle, 0.0f, 0.0f, 1.0f);
            glTranslatef(-110, -150, 0);
        }
        if (iPoly == 5) {  // Back Left leg
            glPushMatrix();
            glTranslatef(50, 100, 0);  // Adjust these values for the pivot point
            glRotatef(legAngle * 0.3, 0.0f, 0.0f, 1.0f);  // Reduced rotation for leg 5
            glTranslatef(-50, -100, 0);
        }
        if (iPoly == 9 || iPoly == 6 || iPoly == 7) {  // Back Right leg and associated parts
            glPushMatrix();
            glTranslatef(350, 100, 0);  // Adjust these values for the pivot point
            glRotatef(-legAngle * 0.3, 0.0f, 0.0f, 1.0f);  // Reduced rotation for leg 9
            glTranslatef(-350, -100, 0);
        } 
         if (iPoly == 10) {  // Front Right leg
            glPushMatrix();
            glTranslatef(300, 150, 0);  // Adjust these values for the pivot point
            glRotatef(-legAngle * 0.3, 0.0f, 0.0f, 1.0f);  // Reduced rotation for leg 10
            glTranslatef(-300, -150, 0);
        } 
        /*if (iPoly == 16) {
         glPushMatrix();
            glTranslatef(150, 250, 0);  // Adjust these values for the pivot point of the head
            glRotatef(headNeckAngle, 0.0f, 0.0f, 1.0f);  // Apply head and neck rotation
            glTranslatef(-150, -250, 0);
        } */
        if (iPoly == 0) {
         glPushMatrix();
            glTranslatef(150, 250, 0);  // Adjust these values for the pivot point of the head 
            glRotatef(headNeckAngle, 0.0f, 0.0f, 1.0f);  // Apply head and neck rotation
            glTranslatef(-150, -250, 0);
            } 
        if (iPoly == 11) {
         glPushMatrix();
            glTranslatef(150, 250, 0);  // Adjust these values for the pivot point of the head
            glRotatef(headNeckAngle, 0.0f, 0.0f, 1.0f);  // Apply head and neck rotation
            glTranslatef(-150, -250, 0);
        }
        if (iPoly == 12) {
         glPushMatrix();
            glTranslatef(150, 250, 0);  // Adjust these values for the pivot point of the head
            glRotatef(headNeckAngle, 0.0f, 0.0f, 1.0f);  // Apply head and neck rotation
            glTranslatef(-150, -250, 0);
        }
        if (iPoly == 13) {
         glPushMatrix();
            glTranslatef(150, 250, 0);  // Adjust these values for the pivot point of the head
            glRotatef(headNeckAngle, 0.0f, 0.0f, 1.0f);  // Apply head and neck rotation
            glTranslatef(-150, -250, 0);
        } 
        if (iPoly == 14) {
         glPushMatrix();
            glTranslatef(150, 250, 0);  // Adjust these values for the pivot point of the head
            glRotatef(headNeckAngle, 0.0f, 0.0f, 1.0f);  // Apply head and neck rotation
            glTranslatef(-150, -250, 0);
        }

        drawPolyLine(polylines[iPoly]);

        if (iPoly == 0 || /* iPoly == 16 ||*/ iPoly == 2 || iPoly == 5 || iPoly == 9 || iPoly == 6 || iPoly == 7 || iPoly == 10 || iPoly == 11 || iPoly == 12 || iPoly == 13 || iPoly == 14) {
            glPopMatrix();
        }
    }

    glPopMatrix();
    glFlush();
}

void animate(int value) {
    dinosaurPosition += walkDirection;

    legAngle += 3.0f * direction;
    if (legAngle > 10.0f || legAngle < -10.0f) {
        direction = -direction;
    }
    
    headNeckAngle += 1.0f * direction;
    if (headNeckAngle > 5.0f || headNeckAngle < -5.0f) {
        direction = -direction;
        }


    // Check boundaries for dinosaur movement
    if (dinosaurPosition > 300.0f || dinosaurPosition < -300.0f) {  // Adjust these values as needed
        walkDirection = -walkDirection;
    }

    glutPostRedisplay();
    glutTimerFunc(100, animate, 0);
}
void loadPolylines(const char* fileName) {
    std::ifstream inStream;
    inStream.open(fileName);
    if (inStream.fail()) return;

    GLint numpolys, numLines;
    GLfloat x, y;
    inStream >> numpolys;

    for (int iPoly = 0; iPoly < numpolys; iPoly++) {
        inStream >> numLines;
        std::vector<std::pair<GLfloat, GLfloat>> polyline;
        for (int i = 0; i < numLines; i++) {
            inStream >> x >> y;
            polyline.push_back({x, y});
        }
        polylines.push_back(polyline);
    }
    inStream.close();
}

int main(int argc, char** argv) {
    loadPolylines("dino.dat");

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(640, 480);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Dinosaur Walking");

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, 1024.0, 0, 768.0);

    glutDisplayFunc(display);
    glutTimerFunc(100, animate, 0);
    glutMainLoop();

    return(0);
}

# data[].attributes.attachment_link.readable_text
#include <math.h>
#include <GL/gl.h>
#include <GL/glut.h>
const int screenWidth = 640;	   // width of screen window in pixels 
const int screenHeight = 480;	   // height of screen window in pixels
GLdouble A, B, C, D;               // values used for scaling and shifting
//<<<<<<<<<<<<<<<<<<<<<<< myInit >>>>>>>>>>>>>>>>>>>>
 void myInit(void)
 {
    glClearColor(1.0,1.0,1.0,0.0);       // background color is white
    glColor3f(0.0f, 0.0f, 0.0f);         // drawing color is black 
    glPointSize(2.0);		         // a 'dot' is 2 by 2 pixels
    glMatrixMode(GL_PROJECTION); 	 // set "camera shape"
    glLoadIdentity();
    gluOrtho2D(0.0, (GLdouble)screenWidth, 0.0, (GLdouble)screenHeight);
		 A = screenWidth / 4.0; // set values used for scaling and shifting
		 B = 0.0;
		 C = D = screenHeight / 2.0;
}
//<<<<<<<<<<<<<<<<<<<<<<<< myDisplay >>>>>>>>>>>>>>>>>
void myDisplay(void)
{
	glClear(GL_COLOR_BUFFER_BIT);     // clear the screen 
	glBegin(GL_POINTS);
	for(GLdouble x = 0; x < 4.0 ; x += 0.005)
  {	
	GLdouble   func = exp(-x) * cos(2 * 3.14159265 * x); 
     glVertex2d(A * x + B, C * func + D);
 		}
	glEnd();	
	glFlush();		   // send all output to display 
}
//<<<<<<<<<<<<<<<<<<<<<<<< main >>>>>>>>>>>>>>>>>>>>>>
int main(int argc, char** argv)
{
	glutInit(&argc, argv);          // initialize the toolkit
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // set display mode
	glutInitWindowSize(screenWidth, screenHeight); // set window size
	glutInitWindowPosition(100, 150); // set window position on screen
	glutCreateWindow("Dot Plot of a Function"); // open the screen window
	glutDisplayFunc(myDisplay);     // register redraw function
	myInit();                   
	glutMainLoop(); 		     // go into a perpetual loop
}

# data[].attributes.attachment_link.readable_text
// Erick Lagunas
#include <GL/glut.h>

class GLintPoint {
public:
    GLint x, y;
};

GLintPoint CP;

void moveTo(GLint x, GLint y) {
    CP.x = x; CP.y = y;
}

void lineTo(GLint x, GLint y){
    glBegin(GL_LINES);
        glVertex2i(CP.x, CP.y);
        glVertex2i(x,y);
    glEnd();
    glFlush();
    CP.x = x;
    CP.y = y;
}

void renderScene(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    moveTo(4,4);
    lineTo(-2,4);
    lineTo(-2,-2);
    lineTo(4, -2);
    lineTo(4,4);
}

void resize(int w, int h) {
    glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-10.0, 10.0, -10.0, 10.0, -1.0, 1.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_SINGLE | GLUT_RGBA);
    glutInitWindowPosition(200,200);
    glutInitWindowSize(320,320);
    glutCreateWindow("GCU My Rectangle");
    glutDisplayFunc(renderScene);
    glutReshapeFunc(resize);
    glutMainLoop();
    return 0;
}

# data[].attributes.attachment_link.readable_text
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <fstream>
#include <vector>

std::vector<std::vector<std::pair<GLfloat, GLfloat>>> polylines;
GLfloat dinosaurPosition = 0.0f;  
GLfloat legAngle = 0.0f;          
GLfloat direction = 1.0f;         
GLfloat walkDirection = 2.0f;     

void drawPolyLine(const std::vector<std::pair<GLfloat, GLfloat>>& polyline) {
    glBegin(GL_LINE_STRIP);
    for (const auto& point : polyline) {
        glVertex2f(point.first, point.second);
    }
    glEnd();
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT);

    glPushMatrix();
    glTranslatef(dinosaurPosition, 0.0f, 0.0f);

    for (int iPoly = 0; iPoly < polylines.size(); iPoly++) {
        if (iPoly == 2) {  // Front Left leg
            glPushMatrix();
            glTranslatef(110, 150, 0);  // Adjust these values for the pivot point
            glRotatef(legAngle, 0.0f, 0.0f, 1.0f);
            glTranslatef(-110, -150, 0);
        }
        if (iPoly == 5) {  // Back Left leg
            glPushMatrix();
            glTranslatef(50, 100, 0);  // Adjust these values for the pivot point
            glRotatef(legAngle * 0.3, 0.0f, 0.0f, 1.0f);  // Reduced rotation for leg 5
            glTranslatef(-50, -100, 0);
        }
        if (iPoly == 9 || iPoly == 6 || iPoly == 7) {  // Back Right leg and associated parts
            glPushMatrix();
            glTranslatef(350, 100, 0);  // Adjust these values for the pivot point
            glRotatef(-legAngle * 0.3, 0.0f, 0.0f, 1.0f);  // Reduced rotation for leg 9
            glTranslatef(-350, -100, 0);
        }
        if (iPoly == 10) {  // Front Right leg
            glPushMatrix();
            glTranslatef(300, 150, 0);  // Adjust these values for the pivot point
            glRotatef(-legAngle * 0.3, 0.0f, 0.0f, 1.0f);  // Reduced rotation for leg 10
            glTranslatef(-300, -150, 0);
        }

        drawPolyLine(polylines[iPoly]);

        if (iPoly == 2 || iPoly == 5 || iPoly == 9 || iPoly == 6 || iPoly == 7 || iPoly == 10) {
            glPopMatrix();
        }
    }

    glPopMatrix();
    glFlush();
}

void animate(int value) {
    dinosaurPosition += walkDirection;

    legAngle += 3.0f * direction;
    if (legAngle > 10.0f || legAngle < -10.0f) {
        direction = -direction;
    }

    // Check boundaries for dinosaur movement
    if (dinosaurPosition > 300.0f || dinosaurPosition < -300.0f) {  // Adjust these values as needed
        walkDirection = -walkDirection;
    }

    glutPostRedisplay();
    glutTimerFunc(100, animate, 0);
}
void loadPolylines(const char* fileName) {
    std::ifstream inStream;
    inStream.open(fileName);
    if (inStream.fail()) return;

    GLint numpolys, numLines;
    GLfloat x, y;
    inStream >> numpolys;

    for (int iPoly = 0; iPoly < numpolys; iPoly++) {
        inStream >> numLines;
        std::vector<std::pair<GLfloat, GLfloat>> polyline;
        for (int i = 0; i < numLines; i++) {
            inStream >> x >> y;
            polyline.push_back({x, y});
        }
        polylines.push_back(polyline);
    }
    inStream.close();
}

int main(int argc, char** argv) {
    loadPolylines("dino.dat");

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(640, 480);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Dinosaur Walking");

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, 1024.0, 0, 768.0);

    glutDisplayFunc(display);
    glutTimerFunc(100, animate, 0);
    glutMainLoop();

    return(0);
}

# data[].attributes.attachment_link.readable_text
1) Prepare your PC
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

2) Go to microsoft store
install Linux

3) Set up Linux
sudo apt-get update
sudo apt-get upgrade

4) Install GUI
Download and install Xming from https://sourceforge.net/projects/xming/

A better package is VcXsrv Windows X Server
https://sourceforge.net/projects/vcxsrv/

5) Tweak Ubuntu
echo export DISPLAY=:0.0 >> ~/.bashrc
echo "export DISPLAY=localhost:0" >> ~/.bashrc
source ~/.bashrc

6) Install a graphical program
sudo apt-get install gedit

7) invoke gedit from terminal
It should work. Now you are ready to install other graphical programs

8)[Accessing Windows Directories]
C: Drive location /mnt/c do cd /mnt/c and we can access the files in window.

9)In case you want to install a lightweight one called Xfce4. 
Run each command here in turn:

sudo apt install xfce4
sudo apt install gnome-themes-standard
sudo apt install conky
then run the command
nano ~/.xinitrc
In the text editor, enter the lines of code that follow. When done, 
press Ctrl-O and hit Return to save it, and Ctrl-x to exit the text editor.
#!/usr/bin/env bash
export LANG="en_US.UTF-8
export LC_ALL="en_US.UTF-8
exec startxfce4

Run:
chmod +x ~/.xinitrc

This will make the file weve just created executable. Then:
ln -s ~/.xinitrc ~/.xsession

This will link that file with another standard X configuration file. 
Start your newly installed Linux desktop with the following command: 

dbus-launch --exit-with-session ~/.xsession

On the first run, a dialog pops upselect to use the default. 
The Xfce4 desktop appears over the Windows 10 one, so you might want to 
ensure the Ubuntu window isnt full-screen, because the window bar can be blocked. 
You should spot an Application menu top-left, theres a dock at the bottom, 
with a taskbar running along the top. To quit, use the Log out option in the 
top-left menu (see image below).

You wont find too much to play with at this point, because its a Linux desktop 
running on a largely sparse distro. You can add programs, such as Firefox, via sudo 
apt-get install firefox and this then appears under the Internet menu. Have fun!

creating a virtual machine
https://www.windowscentral.com/how-run-linux-distros-windows-10-using-hyper-v

copying files from windows to Linux installd under windows.
cp /mnt/c/Users/Rcitro/CST-310-summer/Project1-Sierpinski-Gasket/Dennis\ Canar/* .

# data[].attributes.headline
I found this link for linux, worked well

# data[].attributes.subject
I found this link for linux, worked well

# data[].attributes.body
https://ubuntu.com/tutorials/install-ubuntu-on-wsl2-on-windows-11-with-gui-support#4-configure-ubuntu

# data[].attributes.attachment_link.readable_text
Windows Subsystem for Linux (WSL) allows you to install a complete Ubuntu terminal environment in minutes on your Windows machine, allowing you to develop cross-platform applications without leaving windows.

What you will learn:

How to enable and install WSL on Windows 10 and Windows 11

How to install and run a simple graphical application that uses WSLg

How to install and run a much more advanced application that uses WSLg

What you will need:

A Windows 10 or Windows 11 physical or virtual machine with all the updates installed

WSL can be installed from the command line. Open a powershell prompt as an Administrator (we recommend using Windows Terminal) and run:
wsl --install
This command will enable the features necessary to run WSL and also install the default Ubuntu distribution of Linux available in the Microsoft Store. It is recommended to reboot your machine after this initial installation to complete the setup.

You can also install WSL from the Microsoft Store.

Installation of WSL from the Microsoft Store

Duration: 3:00

The WSL app is availble to install directly from the Microsoft Store like other Windows applications.

To install the WSL application from the Microsoft Store, open it and search for Windows subsystem.

Click on the item Windows Subsystem for Linux to open the corresponding application page.

Click on Get to download and install the application.

Upon installation, you can click on Open, but it will not do much since there is no Linux distribution installed.

However, if you really want to open the WSL application without installing a distribution, you'll see a nice and short help message that you must follow in order to make something useful with WSL:

You can now proceed with the installation of Ubuntu.

WSL supports a variety of Linux distributions including the latest Ubuntu release, Ubuntu 20.04 LTS and Ubuntu 18.04 LTS. You can find them by opening the Microsoft Store app and searching for Ubuntu.

Choose the distribution you prefer and then select Get.

Ubuntu will then install on your machine.

Once installed, you can either launch the application directly from the store or search for Ubuntu in your Windows search bar.

Install Ubuntu from the command line

It is possible to install the same Ubuntu applications available on the Windows Store directly from the command line.

In a Powershell terminal you can run:

wsl --list --online to see all available distros.

You can install a distro using the NAME by running:
wsl --install -d Ubuntu-20.04
Use wsl -l -v to see all your currently installed distros and which version of WSL they are using:

Congratulations, you now have an Ubuntu terminal running on your Windows machine!

Once it has finished its initial setup, you will need to create a username and password (this does not need to match your Windows user credentials).

Finally, it's always good practice to install the latest updates with the following commands, entering your password when prompted.
sudo apt update
Then
sudo apt full-upgrade
Press Y when prompted.

(Optional) Enable systemd

In September 2022, Microsoft announced support for systemd in WSL. This long-awaited upgrade to WSL unlocks a huge number of quality of life features for managing processes and services. This includes snapd support, which enables users to take advantage of all of the tools and apps available on snapcraft.io.

To enable systemd you will need make a small modification to /etc/wsl.conf in your Ubuntu distribution.

Run `sudo nano /etc/wsl.conf' to open the file and insert the following lines:

[boot] systemd=true

Then restart your distro by running wsl --shutdown in powershell and relaunching.

WSL2 comes with WSLg enabled by default. WSLg allows you to run graphical linux applications.

To check that you have the latest package lists, type:
sudo apt update
Then, start with some basic X11 applications:
sudo apt install x11-apps
To run the xeyes, a "follow the mouse" application, type:

The & at the end of the line will execute the command asynchronously. In other words, the shell will run the command in the background and return to the command prompt immediately.

The first launch of a GUI application takes a few seconds while WSL is initializing the graphics stack. Next executions of GUI applications are much faster.

Leave xeyes opened and run the calculator xcalc with:

When you move the cursor over the calculator, xeyes follows the cursor. This shows that several GUI applications can interact together.

Note that applications running under WSLg display a little penguin at the bottom right corner of their icons in the Windows taskbar. That's one way you can distinguish applications running on Windows or Ubuntu (besides the window decoration and styling).

Close xeyes and xcalc by pressing the cross icon on the top right corner of each X application window.

Xcalc and xeyes are very basic X Windows applications but there are plenty of choices in the Linux ecosystem corresponding to your needs and available out of the box on Ubuntu.

In the following example, we will use GNU Octave to perform numerical computation.

We will use it to calculate and draw a beautiful Julia fractal. The goal here is to use Octave to demonstrate how WSLg works, not to go through the theory of fractals.

First thing is to install the software like we did for x11-apps, from the terminal prompt run:
sudo apt install octave
Then start the application:

Do not forget the ampersand & at the end of the line, so the application is started in the background and we can continue using the same terminal window.

In Octave, click on the New script icon to open a new editor window and copy/paste the following code:

#{ Inspired by the work of Bruno Girin ([Geek Thoughts: Fractals with Octave: Classic Mandelbrot and Julia](http://brunogirin.blogspot.com/2008/12/fractals-with-octave-classic-mandelbrot.html)) Calculate a Julia set zmin: Minimum value of c zmax: Maximum value of c hpx: Number of horizontal pixels niter: Number of iterations c: A complex number #} function M = julia(zmin, zmax, hpx, niter, c) %% Number of vertical pixels vpx=round(hpx*abs(imag(zmax-zmin)/real(zmax-zmin))); %% Prepare the complex plane [zRe,zIm]=meshgrid(linspace(real(zmin),real(zmax),hpx), linspace(imag(zmin),imag(zmax),vpx)); z=zRe+i*zIm; M=zeros(vpx,hpx); %% Generate Julia for s=1:niter mask=abs(z)<2; M(mask)=M(mask)+1; z(mask)=z(mask).^2+c; end M(mask)=0; end

This code is the function that will calculate the Julia set.

Save it to a file named julia.m. Since it is a function definition, the name of the file must match the name of the function.

Open a second editor window with the New Script button and copy and paste the following code:

Jc1=julia(-1.6+1.2i, 1.6-1.2i, 640, 128, -0.75+0.2i); imagesc(Jc1) axis off colormap('default');

This code calls the function defined in julia.m. You can later change the parameters if you want to explore the Julia fractal.

Save it to a file named juliatest.m.

And finally, press the button Save File and Run.

After a few seconds, depending on your hardware and the parameters, a Julia fractal is displayed.

Like Octave, this window is displayed using WSLg completely transparently to the user.

Enjoy!

# data[].attributes.attachment_link.title
Install Ubuntu on WSL2 and get started with graphical applications | Ubuntu

# data[].attributes.attachment_link.description
Ubuntu is an open source software operating system that runs from the desktop, to the cloud, to all your internet connected things.

# data[].attributes.attachment_link.description
“Blessed are the young, for they shall inherit the national debt.” - Herbert Hoover

# data[].attributes.body
g++ main.cpp -o run -lglfw -lGL -lGLEW -lSOIL -lassimp

# data[].attributes.attachment_link.readable_text
// Shader Class

#pragma once

#include <string> // Include string
#include <fstream> // Include fstream
#include <sstream> // Include sstream
#include <iostream> // Include iostream

#include <GL/glew.h> // Include glew

using namespace std; // Use namespace std

class Shader {
public:
    GLuint Program; // Initialize GLuint 
    Shader(const GLchar* vertexPath, const GLchar* fragmentPath) { // Shader constructor
        // Retrieve vertex/fragment code from path
        string vertexCode; // Initialize vertexCode string
        string fragmentCode; // Initialize fragmentCode string
        ifstream vShaderFile; // Initialize file for vertex shader
        ifstream fShaderFile; // Initialize file for fragment shader
        // Allows for exceptions
        vShaderFile.exceptions(ifstream::badbit); // Allow vs file exception
        fShaderFile.exceptions(ifstream::badbit); // Allow frag file exception
        // Read Shaders
        try {
            // Open Files
            vShaderFile.open(vertexPath); // Try opening .vs using path
            fShaderFile.open(fragmentPath); // Try opening .frag using path
            stringstream vShaderStream, fShaderStream; // Initalize stringstream for both
            // Read file's buffer contents into streams
            vShaderStream << vShaderFile.rdbuf(); // Read in .vs
            fShaderStream << fShaderFile.rdbuf(); // Read in .frag
            // Close file handlers
            vShaderFile.close(); // Close .vs file
            fShaderFile.close(); // Close .frag file
            // Convert stream into string
            vertexCode = vShaderStream.str(); // Convert and store contents of .vs as string
            fragmentCode = fShaderStream.str(); // Convert and store contents of .frag as string
        }
        catch (ifstream::failure e) {
            cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ" << endl; // Error message if catch
        }
        // Compilation
        const GLchar* vShaderCode = vertexCode.c_str(); // Initialize GLchar* for vs
        const GLchar* fShaderCode = fragmentCode.c_str(); // Initalize GLchar* for frag
        GLuint vertex, fragment; // Initalize GLuint for vs and frag
        GLint success; // Initalize GLint for success
        GLchar infoLog[512]; // Initialize infoLog
        // Vertex Shader
        vertex = glCreateShader(GL_VERTEX_SHADER); // Set vertex = shader
        glShaderSource(vertex, 1, &vShaderCode, NULL); // Get source
        glCompileShader(vertex); // Compile vertex shader
        // Print compile errors if necessary
        glGetShaderiv(vertex, GL_COMPILE_STATUS, &success); // Get status
        if (!success) { // If failure
            glGetShaderInfoLog(vertex, 512, NULL, infoLog); // Get info on failure
            cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl; // Print error message if catch
        }
        // Fragment Shader
        fragment = glCreateShader(GL_FRAGMENT_SHADER); // Initalize gragment as shader
        glShaderSource(fragment, 1, &fShaderCode, NULL); // Get source
        glCompileShader(fragment); // Compile fragment shader
        // Print compile errors if necessary
        glGetShaderiv(fragment, GL_COMPILE_STATUS, &success); // Get status
        if (!success) { // If failure
            glGetShaderInfoLog(fragment, 512, NULL, infoLog); // Get info on failure
            cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl; // Print error message if catch
        }
        // Linking Shader Program
        this->Program = glCreateProgram(); // Set program to createProgram output
        glAttachShader(this->Program, vertex); // Attach vertex shader
        glAttachShader(this->Program, fragment); // Attach frag shader
        glLinkProgram(this->Program); // Link program
        // Print linking errors if necessary
        glGetProgramiv(this->Program, GL_LINK_STATUS, &success); // Get linking status
        if (!success) { // If failure
            glGetProgramInfoLog(this->Program, 512, NULL, infoLog); // Get info on failure
            cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << endl; // Print error message if catch
        }
        // Delete shaders
        glDeleteShader(vertex); // Delete vertex shader
        glDeleteShader(fragment); // Delete fragment shader
    }
    // Uses the current shader
    void Use() {
        glUseProgram(this->Program); // Use program with shaders from method above
    }
};

# data[].attributes.attachment_link.readable_text
#pragma once
// Std. Includes
#include <string> // Include string
#include <fstream> // include fstream
#include <sstream> // Include sstream
#include <iostream> // Include iostream
#include <map> // Include map
#include <vector> // Include vector
using namespace std; // Use namespace std
// GL Includes
#include <GL/glew.h> // Contains all the necessery OpenGL includes
#include <glm/glm.hpp> // Include glm
#include <glm/gtc/matrix_transform.hpp> // Include matrix transform
#include <SOIL/SOIL.h> // Include SOIL
#include <assimp/Importer.hpp> // Include assimp importer
#include <assimp/scene.h> // Include assimp scene
#include <assimp/postprocess.h> // Include assimp postprocess

#include "Mesh.h" // Include Mesh.h

GLint TextureFromFile(const char* path, string directory); // Texture from file

class Model  // Provided in class
{
public:
	/*  Functions   */
	// Constructor, expects a filepath to a 3D model.
	Model(GLchar* path) // Model constructor using path
	{
		this->loadModel(path); // Load model with callback and path
	}
	
	// Draws the model, and thus all its meshes
	void Draw(Shader shader)
	{
		for(GLuint i = 0; i < this->meshes.size(); i++) // Iterate over mesh
			this->meshes[i].Draw(shader); // Draw
	}
	
private:
	/*  Model Data  */
	vector<Mesh> meshes; // Vector of meshes
	string directory; // String for directory
	
	/*  Functions   */
	// Loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector.
	void loadModel(string path)
	{
		// Read file via ASSIMP
		Assimp::Importer importer; // Initialize importer
		const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); // Read model
		// Check for errors
		if(!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
		{
			cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl; // Write error message
			return;
		}
		// Retrieve the directory path of the filepath
		this->directory = path.substr(0, path.find_last_of('/')); // Get directory
		
		// Process ASSIMP's root node recursively
		this->processNode(scene->mRootNode, scene); // Process nodes using callback
	}
	
	// Processes a node in a recursive fashion. Processes each individual mesh located at the node and repeats this process on its children nodes (if any).
	void processNode(aiNode* node, const aiScene* scene)
	{
		// Process each mesh located at the current node
		for(GLuint i = 0; i < node->mNumMeshes; i++) // Iterate over mNumMeshes
		{
			// The node object only contains indices to index the actual objects in the scene. 
			// The scene contains all the data, node is just to keep stuff organized (like relations between nodes).
			aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; // Assign mesh
			this->meshes.push_back(this->processMesh(mesh, scene)); // Push mesh back to meshes using processMesh method
		}
		// After we've processed all of the meshes (if any) we then recursively process each of the children nodes
		for(GLuint i = 0; i < node->mNumChildren; i++) // Iterate over children
		{
			this->processNode(node->mChildren[i], scene); // Process child nodes
		}
		
	}
	
	Mesh processMesh(aiMesh* mesh, const aiScene* scene)
	{
		// Data to fill
		vector<Vertex> vertices; // Vector for vertices
		vector<GLuint> indices; // Vector for indices
		vector<Texture> textures; // Vector for textures
		
		// Walk through each of the mesh's vertices
		for(GLuint i = 0; i < mesh->mNumVertices; i++) // Iterate over vertices
		{
			Vertex vertex; // Initialize vertex
			glm::vec3 vector; // We declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.
			// Positions
			vector.x = mesh->mVertices[i].x; // Set x based on mesh
			vector.y = mesh->mVertices[i].y; // Set y based on mesh
			vector.z = mesh->mVertices[i].z; // Set z based on mesh
			vertex.Position = vector; // Set position = vector
			// Normals
			vector.x = mesh->mNormals[i].x; // Set normal x based on mesh
			vector.y = mesh->mNormals[i].y; // Set normal y based on mesh
			vector.z = mesh->mNormals[i].z; // Set normal z based on mesh
			vertex.Normal = vector; // Set vertex.Normal based on vector
			// Texture Coordinates
			if(mesh->mTextureCoords[0]) // Does the mesh contain texture coordinates?
			{
				glm::vec2 vec; // Initialize vec2
				// A vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't 
				// use models where a vertex can have multiple texture coordinates so we always take the first set (0).
				vec.x = mesh->mTextureCoords[0][i].x; // Set x for texcoords
				vec.y = mesh->mTextureCoords[0][i].y; // Set y for texcoords
				vertex.TexCoords = vec; // Set texcoords
			}
			else
				vertex.TexCoords = glm::vec2(0.0f, 0.0f); // Set texcoords as 0.0
			vertices.push_back(vertex); // Push vertex back to vertices
		}
		// Now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
		for(GLuint i = 0; i < mesh->mNumFaces; i++) // Iterate over faces
		{
			aiFace face = mesh->mFaces[i]; // Set face
			// Retrieve all indices of the face and store them in the indices vector
			for(GLuint j = 0; j < face.mNumIndices; j++) // Iterate over face indices
				indices.push_back(face.mIndices[j]); // Push back face indices
		}
		// Process materials
		if(mesh->mMaterialIndex >= 0)
		{
			aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; // Set material
			// We assume a convention for sampler names in the shaders. Each diffuse texture should be named
			// as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER. 
			// Same applies to other texture as the following list summarizes:
			// Diffuse: texture_diffuseN
			// Specular: texture_specularN
			// Normal: texture_normalN
			
			// 1. Diffuse maps
			vector<Texture> diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"); // Set diffuseMaps
			textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); // Insert textures
			// 2. Specular maps
			vector<Texture> specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"); // Set specularMaps
			textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); // Insert textures
		}
		
		// Return a mesh object created from the extracted mesh data
		return Mesh(vertices, indices, textures); // Return Mesh object from vertices, indices, textures defined above
	}
	
	// Checks all material textures of a given type and loads the textures if they're not loaded yet.
	// The required info is returned as a Texture struct.
	vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName)
	{
		vector<Texture> textures; // Initialize textures
		for(GLuint i = 0; i < mat->GetTextureCount(type); i++) // Iterate over textures
		{
			aiString str; // Initialize aiString
			mat->GetTexture(type, i, &str); // Get texture using type and string
			// Check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
			Texture texture; // Initialize texture
			texture.id = TextureFromFile(str.C_Str(), this->directory); // Assign id
			texture.type = typeName; // Assign type
			texture.path = str; // Assign path
			textures.push_back(texture); // Push back texture
		}
		return textures; // Return vector of textures
	}
};



GLint TextureFromFile(const char* path, string directory)
{
	//Generate texture ID and load texture data 
	string filename = string(path); // Get filename
	filename = directory + '/' + filename; // Get filename with directory
	GLuint textureID; // Initialize textureID
	glGenTextures(1, &textureID); // Gen textures with textureID
	int width,height; // Initialize width and height
	unsigned char* image = SOIL_load_image(filename.c_str(), &width, &height, 0, SOIL_LOAD_RGB); // Get SOIL image
	// Assign texture to ID
	glBindTexture(GL_TEXTURE_2D, textureID); // Bind texture
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); // Bind image
	glGenerateMipmap(GL_TEXTURE_2D); // Generate mip maps
	
	// Parameters
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); // Set texture wrap s
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); // Set texture wrap t
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); // Set min filter
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Set mag filter
	glBindTexture(GL_TEXTURE_2D, 0); // Bind texture
	SOIL_free_image_data(image); // Use image
	return textureID; // Return textureID
}

# data[].attributes.attachment_link.readable_text
#pragma once
// Std. Includes
#include <string> // Include string
#include <fstream> // Include fstream
#include <sstream> // Include sstream
#include <iostream> // Include iostream
#include <vector> // Include vector
using namespace std; // Use namespace std
// GL Includes
#include <GL/glew.h> // Contains all the necessery OpenGL includes
#include <glm/glm.hpp> // Include glm
#include <glm/gtc/matrix_transform.hpp> // Include matrix transform


// Define vertex structure
struct Vertex {
    // Position
    glm::vec3 Position; // Vec3 position
    // Normal
    glm::vec3 Normal; // Vec3 normal
    // TexCoords
    glm::vec2 TexCoords; // Vec2 texture coordinates
};

// Define texture structure
struct Texture {
    GLuint id; // GLuint for id
    string type; // String for type
    aiString path; // aiString for path
};

class Mesh {  // Provided in class
public:
    /*  Mesh Data  */
    vector<Vertex> vertices; // vector of vertices
    vector<GLuint> indices; // vector of indices
    vector<Texture> textures; // vector of textures

    /*  Functions  */
    // Constructor
    Mesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures) // Input constructor
    {
        this->vertices = vertices; // Set vertices equal to input
        this->indices = indices; // Set indices equal to input
        this->textures = textures; // Set textures equal to input

        // Now that we have all the required data, set the vertex buffers and its attribute pointers.
        this->setupMesh(); // Call class setupMesh() method
    }

    // Render the mesh
    void Draw(Shader shader) 
    {
        // Bind appropriate textures
        GLuint diffuseNr = 1; // Set diffuseNr
        GLuint specularNr = 1; // Set specularNr
        for(GLuint i = 0; i < this->textures.size(); i++) // Iterate over textures
        {
            glActiveTexture(GL_TEXTURE0 + i); // Active proper texture unit before binding
            // Retrieve texture number (the N in diffuse_textureN)
            stringstream ss; // initialize stringstream
            string number; // Initalize number
            string name = this->textures[i].type; // Set name to type
            if(name == "texture_diffuse") // If diffuse
                ss << diffuseNr++; // Transfer GLuint to stream
            else if(name == "texture_specular") // If specular
                ss << specularNr++; // Transfer GLuint to stream
            number = ss.str(); // Set number equal to string of ss
            // Now set the sampler to the correct texture unit
            glUniform1i(glGetUniformLocation(shader.Program, (name + number).c_str()), i); // Set sampler to texture unit
            // And finally bind the texture
            glBindTexture(GL_TEXTURE_2D, this->textures[i].id); // Bind
        }
        
        // Also set each mesh's shininess property to a default value (if you want you could extend this to another mesh property and possibly change this value)
        glUniform1f(glGetUniformLocation(shader.Program, "material.shininess"), 16.0f);

        // Draw mesh
        glBindVertexArray(this->VAO); // Bind VAO
        glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0); // Draw GL_TRIANGLES
        glBindVertexArray(0); // Bind 0

        // Always good practice to set everything back to defaults once configured.
        for (GLuint i = 0; i < this->textures.size(); i++)
        {
            glActiveTexture(GL_TEXTURE0 + i); // Reset active texture
            glBindTexture(GL_TEXTURE_2D, 0); // Bind GL_TEXTURE_2D, 0
        }
    }

private:
    /*  Render data  */
    GLuint VAO, VBO, EBO; // Initialize VAO, VBO, EBO

    /*  Functions    */
    // Initializes all the buffer objects/arrays
    void setupMesh()
    {
        // Create buffers/arrays
        glGenVertexArrays(1, &this->VAO); // Create VAO array
        glGenBuffers(1, &this->VBO); // Create VBO buffer
        glGenBuffers(1, &this->EBO); // Create EBO buffer

        glBindVertexArray(this->VAO); // Bind vertex array
        // Load data into vertex buffers
        glBindBuffer(GL_ARRAY_BUFFER, this->VBO); // Bind buffer
        // A great thing about structs is that their memory layout is sequential for all its items.
        // The effect is that we can simply pass a pointer to the struct and it translates perfectly to a glm::vec3/2 array which
        // again translates to 3/2 floats which translates to a byte array.
        glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(Vertex), &this->vertices[0], GL_STATIC_DRAW);   // Set buffer data

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO); // Bind EBO buffer
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), &this->indices[0], GL_STATIC_DRAW); // Set buffer data

        // Set the vertex attribute pointers
        // Vertex Positions
        glEnableVertexAttribArray(0); // Enable vertex attrib
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0); // Set vertex attrib for position
        // Vertex Normals
        glEnableVertexAttribArray(1); // Enable vertex attrib
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Normal)); // Set vertex attrib for normal
        // Vertex Texture Coords
        glEnableVertexAttribArray(2); // Enable vertex attrib
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, TexCoords)); // Set vertex attrib for texcoords

        glBindVertexArray(0); // Bind 0
    }
};


# data[].attributes.attachment_link.readable_text
#include <iostream>  // iostream include

// GLEW
#define GLEW_STATIC // Define glew_static
#include <GL/glew.h> // glew include

// GLFW
#include <GLFW/glfw3.h> // glfw include

// SOIL
#include <SOIL/SOIL.h> // soil include

// GLM Mathematics
#include <glm/glm.hpp> // glm include
#include <glm/gtc/matrix_transform.hpp> // glm matrix math include
#include <glm/gtc/type_ptr.hpp> // glm gtc include

// Other includes
#include "shader.h" // Include shader class
#include "Camera.h" // Include Camera class
#include "Model.h" // Include Model class

const GLuint WIDTH = 800, HEIGHT = 600; // Global variables for width and height of window

// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode); // key_callback method
void do_movement(); // Movement method

Camera camera(glm::vec3(0.0f, 0.0f, 0.0f)); // Sets iniital camera pos (0, 0, 0)
GLfloat lastX = WIDTH / 2.0; // Used for camera motion
GLfloat lastY = HEIGHT / 2.0; // Used for camera motion
bool keys[1024]; // Allowable number of key strokes

glm::vec3 lightPos(1.0f, 1.0f, -2.0f); // Sets light position

GLfloat deltaTime = 0.0f; // Initialize deltaTime for camera movement
GLfloat lastFrame = 0.0f; // Initialize lastFrame for camera movement

int main() {
    // Init GLFW
    glfwInit(); // Initialize GLFW
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Set major context version
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Set minor context version
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Set profiles
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Set resizable to false

    // Create GLFWwindow
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Project 9", nullptr, nullptr); // Create window
    glfwMakeContextCurrent(window); // Make context method

    // Set required callback functions
    glfwSetKeyCallback(window, key_callback); // Set key_callback method

    glewExperimental = GL_TRUE; // Set glew to experimental

    glewInit(); // Initialize GLEW

    glViewport(0, 0, WIDTH, HEIGHT); // Define viewport dimensions

    glEnable(GL_DEPTH_TEST); // Set up OpenGL options

    // INSERT SHADERS HERE FOR PROJECT 10
    Shader checkerboardShader("checkerboard.vs", "checkerboard.frag"); // Create shader for checkerboard
    Shader cubeShader("cube.vs", "cube.frag"); // Create shader for cube object
    Shader cylinderShader("cylinder.vs", "cylinder.frag"); // Create shader for cylinder object
    Shader sphereShader("sphere.vs", "sphere.frag"); // Create shader for sphere object

    // Models for Cylinder and Sphere
    Model cylinderModel("cylinder.obj"); // Defines model for cylinder using obj
    Model sphereModel("sphere.obj"); // Define model for sphere using obj

    GLfloat vertices[] = {
        // Coordinates: 3 Position, 3 Color, 2 Texture
        // Back face of cube
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f,  0.0f, -1.0f, // Bottom left
        0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,  0.0f, -1.0f, // Bottom right
        0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f,  0.0f, -1.0f, // Upper right
        0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f,  0.0f, -1.0f, // Upper right
        -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,  0.0f, -1.0f, // Upper left
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f,  0.0f, -1.0f, // Bottom left

        // Front face of cube
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 0.0f,  0.0f,  1.0f, // Bottom left
        0.5f, -0.5f,  0.5f,  1.0f, 0.0f, 0.0f,  0.0f,  1.0f, // Bottom right
        0.5f,  0.5f,  0.5f,  1.0f, 1.0f, 0.0f,  0.0f,  1.0f, // Upper right
        0.5f,  0.5f,  0.5f,  1.0f, 1.0f, 0.0f,  0.0f,  1.0f, // Upper right
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f, 0.0f,  0.0f,  1.0f, // Upper left
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 0.0f,  0.0f,  1.0f, // Bottom left

        // Left face
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f, -1.0f,  0.0f,  0.0f, // Upper close
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f, -1.0f,  0.0f,  0.0f, // Upper far
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, -1.0f,  0.0f,  0.0f, // Lower far
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, -1.0f,  0.0f,  0.0f, // Lower far
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, -1.0f,  0.0f,  0.0f, // Lower close
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f, -1.0f,  0.0f,  0.0f, // Upper close

        // Right face
        0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 1.0f,  0.0f,  0.0f, // Upper close
        0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 1.0f,  0.0f,  0.0f, // Upper far
        0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 1.0f,  0.0f,  0.0f, // Lower far
        0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 1.0f,  0.0f,  0.0f, // Lower far
        0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f,  0.0f,  0.0f, // Lower close
        0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 1.0f,  0.0f,  0.0f, // Upper close

        // Bottom face
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,  0.0f, -1.0f,  0.0f, // Left far
        0.5f, -0.5f, -0.5f,  1.0f, 1.0f,  0.0f, -1.0f,  0.0f, // Right far
        0.5f, -0.5f,  0.5f,  1.0f, 0.0f,  0.0f, -1.0f,  0.0f, // Right close
        0.5f, -0.5f,  0.5f,  1.0f, 0.0f,  0.0f, -1.0f,  0.0f, // Right close
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,  0.0f, -1.0f,  0.0f, // Left close
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,  0.0f, -1.0f,  0.0f, // Left far

        // Top Face
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  1.0f,  0.0f, // Left far
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 0.0f,  1.0f,  0.0f, // Right far
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 0.0f,  1.0f,  0.0f, // Right close
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 0.0f,  1.0f,  0.0f, // Right close
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f, 0.0f,  1.0f,  0.0f, // Left close
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  1.0f,  0.0f // Left far
    };
    GLuint VBO, VAO; // Initialize VBO, VAO
    glGenVertexArrays(1, &VAO); // Generate VAO
    glGenBuffers(1, &VBO); // Generate VBO

    glBindVertexArray(VAO);  // Bind VAO

    glBindBuffer(GL_ARRAY_BUFFER, VBO);  // Bind VBO
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);  // Buffer Data

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0); // Set vertex attribute pointer for pos
    glEnableVertexAttribArray(0); // Enable vertex attrib array with 0
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); // Set vertex attrib pointer for texcoord
    glEnableVertexAttribArray(2); // Enable with 2
    // Normalized Position Attribute
    glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0); // Set normalized pos vertex attrib
    glEnableVertexAttribArray(4); // Enable with 4

    glBindVertexArray(0); // Unbind VAO

    // DEFINE TEXTURES HERE Project 10 --> NOTE FOR PROJECT 10

    // Game Loop
    while (!glfwWindowShouldClose(window)) {
        // Calculate deltaTime for camera movement
        GLfloat currentFrame = glfwGetTime(); // Get current time
        deltaTime = currentFrame - lastFrame; // Calculate change in time
        lastFrame = currentFrame; // Set last frame to current frame

        // Check for events
        glfwPollEvents(); // Callback glfwPollEvents to check for events
        do_movement(); // Callback do_movement()

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // Set background color
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear buffers

        
        // Initialize Camera
        glm::mat4 view = glm::mat4(1.0f); // Initialize view to identity
        view = camera.GetViewMatrix(); // Set view based on camera
        glm::mat4 projection = glm::perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f); // Initialize projection using initial values
        glm::mat4 model = glm::mat4(1.0f); // Initialize model to be 4x4 identity

        // BIND TEXTURES HERE PROJECT 10

        // CHECKERBOARD
        checkerboardShader.Use(); // Use checkerboard shader

        GLint squareColorLoc = glGetUniformLocation(checkerboardShader.Program, "squareColor"); // Retrieve uniform location for squareColor
        GLint lightColorLoc = glGetUniformLocation(checkerboardShader.Program, "lightColor"); // Retrieve uniform location for lightColor
        GLint lightPosLoc = glGetUniformLocation(checkerboardShader.Program, "lightPos"); // Retrieve uniform location for lightPos
        GLint viewPosLoc = glGetUniformLocation(checkerboardShader.Program, "viewPos"); // Retrieve uniform location for viewPos

        glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f); // Pass white color to lightColorLoc uniform
        glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z); // Pass light position to lightPosLoc uniform
        glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z); // Pass camera position to viewPosLoc uniform

        glm::mat4 view_square = view; // Create mat4 view_square equal to view generic defined above

        GLint modelLoc = glGetUniformLocation(checkerboardShader.Program, "model"); // Retrieve model uniform location
        GLint viewLoc = glGetUniformLocation(checkerboardShader.Program, "view"); // Retrieve view uniform location
        GLint projLoc = glGetUniformLocation(checkerboardShader.Program, "projection"); // Retrieve projection uniform location

        for (int i = 0; i < 8; i++) { // For 8 rows
            for (int j = 0; j < 8; j++) { // For 8 columns
                if ((i+j) % 2 == 0) { // Check if i+j is odd or even for color purposes
                    glUniform3f(squareColorLoc, 1.0f, 0.0f, 1.0f); // If even square color is purple --> pass purple to uniform
                } else {
                    glUniform3f(squareColorLoc, 1.0f, 1.0f, 1.0f); // If even square color is white --> pas white to uniform
                }
                view_square = glm::translate(view_square, glm::vec3(j-4.0f, -0.5f, i-9.0f)); // Translate square to posiiton [setting x and z for grid]
                view_square = glm::scale(view_square, glm::vec3(1.0f, 0.1f, 1.0f)); // Scale squares to be like tiles
                glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); // Pass model to uniform
                glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view_square)); // Pass view_square to uniform
                glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection)); // Pass projection to uniform
                // Draw square
                glBindVertexArray(VAO); // Bind vertex arrays
                glDrawArrays(GL_TRIANGLES, 0, 36); // Draw arrays for cube
                view_square = view; // Reset view_square to original view identity [i.e. Translations are independent]
                
            }
        }

        // CUBE
        cubeShader.Use(); // Activate cube shader

        // Set uniform locations
        GLint cubeColorLoc = glGetUniformLocation(cubeShader.Program, "cubeColor"); // Retrieve uniform location
        lightColorLoc = glGetUniformLocation(cubeShader.Program, "lightColor"); // Reset uniform location for cubeShader
        lightPosLoc = glGetUniformLocation(cubeShader.Program, "lightPos"); // Reset uniform location for cubeShader
        viewPosLoc = glGetUniformLocation(cubeShader.Program, "viewPos"); // Reset uniform location for cubeShader

        // Pass to shaders
        glUniform3f(cubeColorLoc, 1.0f, 0.0f, 0.0f); // Pass cube color to uniform
        glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f); // Pass light color to uniform
        glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z); // Pass light position to uniform
        glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z); // Pass camera position to uniform

        glm::mat4 view_cube = view; // Create mat4 view_cube equal to identity view
        view_cube = glm::translate(view_cube, glm::vec3(0.0f, 0.0f, -5.0f)); // Translate cube back

        // Get uniform location
        modelLoc = glGetUniformLocation(cubeShader.Program, "model"); // Reset modelLoc using cubeShader
        viewLoc = glGetUniformLocation(cubeShader.Program, "view"); // Reset viewLoc using cubeShader
        projLoc = glGetUniformLocation(cubeShader.Program, "projection"); // Reset projLoc using cubeShader
        // Pass locations to shader
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); // Pass model to shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view_cube)); // Pass view_cube to shader
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection)); // Pass projection to shader
        // Draw cube
        glBindVertexArray(VAO); // Bind vertex arrays
        glDrawArrays(GL_TRIANGLES, 0, 36); // Draw cube

        
        // CYLINDER
        cylinderShader.Use(); // Activate cylinder shader

        GLint cylinderColorLoc = glGetUniformLocation(cylinderShader.Program, "cylinderColor"); // Retrieve cylinderColor location
        lightColorLoc = glGetUniformLocation(cylinderShader.Program, "lightColor"); // Reset lightColor location
        lightPosLoc = glGetUniformLocation(cylinderShader.Program, "lightPos"); // Reset lightPos location
        viewPosLoc = glGetUniformLocation(cylinderShader.Program, "viewPos"); // Reset viewPos location

        glUniform3f(cylinderColorLoc, 0.0f, 1.0f, 0.0f); // Pass color to uniform
        glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f); // Pass light color to uniform
        glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z); // Pass light position to uniform
        glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z); // Pass camera position to uniform

        modelLoc = glGetUniformLocation(cylinderShader.Program, "model"); // Reset view location for cylinderShader
        viewLoc = glGetUniformLocation(cylinderShader.Program, "view"); // Reset view location for cylinderShader
        projLoc = glGetUniformLocation(cylinderShader.Program, "projection"); // Reset view location for cylinderShader

        glm::mat4 view_cylinder = view; // Create mat4 view_cylinder using generic view identity
        view_cylinder = glm::translate(view_cylinder, glm::vec3(1.2f, -3.0f, -5.5f)); // Translate cylinder back, to the right, and down
        view_cylinder = glm::scale(view_cylinder, glm::vec3(0.5, 3.0, 0.5)); // Increase height of cylinder

        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view_cylinder)); // Pass view_cylinder to shader
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection)); // Pass projection to shader
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); // Pass moel to shader

        cylinderModel.Draw(cylinderShader); // Draw obj model

        
        // SPHERE
        sphereShader.Use(); // Activate sphereShader

        GLint sphereColorLoc = glGetUniformLocation(sphereShader.Program, "sphereColor"); // Retrieve sphereColor location
        lightColorLoc = glGetUniformLocation(sphereShader.Program, "lightColor"); // Reset lightColor location for sphereShader
        lightPosLoc = glGetUniformLocation(sphereShader.Program, "lightPos"); // Reset lightPos location for sphereShader
        viewPosLoc = glGetUniformLocation(sphereShader.Program, "viewPos"); // Reset viewPos location for sphereShader

        glUniform3f(sphereColorLoc, 0.0f, 0.0f, 1.0f); // Pass in sphere color to uniform
        glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f); // Pass in light color to uniform
        glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z); // Pass in light position to uniform
        glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z); // Pass in camera position to uniform

        modelLoc = glGetUniformLocation(sphereShader.Program, "model"); // Reset model uniform location for sphereShader
        viewLoc = glGetUniformLocation(sphereShader.Program, "view"); // Reset view uniform location for sphereShader
        projLoc = glGetUniformLocation(sphereShader.Program, "projection"); // Reset projection uniform location for sphereShader

        glm::mat4 view_sphere = view; // Create mat4 view_sphere equal to view identity
        view_sphere = glm::translate(view_sphere, glm::vec3(-1.2f, 0.0f, -5.0f)); // Translate sphere back and to the left
        view_sphere = glm::scale(view_sphere, glm::vec3(0.5f, 0.5f, 0.5f)); // Scale down sphere
        
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view_sphere)); // Pass view_sphere to uniform
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection)); // Pass projection to uniform
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); // Pass model to uniform

        sphereModel.Draw(sphereShader); // Draw sphere obj model

        glBindVertexArray(0); // Bind zero at end
        glfwSwapBuffers(window); // Swap screen buffers

    }
    // Deallocate resources
    glDeleteVertexArrays(1, &VAO); // Deallocate vertex arrays
    glDeleteBuffers(1, &VBO); // Deallocate buffers
    glfwTerminate(); // Terminate window
    return 0; // Returns 0 for end of int main()

}

// Method for key input
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { // If ESC pressed
        glfwSetWindowShouldClose(window, GL_TRUE); // Close window
    } if (key >= 0 && key < 1024) { // Allow for 1024 key presses
        if (action == GLFW_PRESS) { // If pressed
            keys[key] = true; // Set keys[key] = true [key pressed]
        } else if (action == GLFW_RELEASE) { // If released
            keys[key] = false; // Set keys[key] = false [key not pressed]
        }
    }
}

// Initiates movement based on keyboard input
void do_movement() {
    if (keys[GLFW_KEY_LEFT_SHIFT] || keys[GLFW_KEY_RIGHT_SHIFT]) { // If either shift keys are pressed
        if (keys[GLFW_KEY_UP]) { // If up arrow
            camera.ProcessKeyboard(FORWARD, deltaTime); // Move camera forward with callback
        } else if (keys[GLFW_KEY_DOWN]) { // If down arrow
            camera.ProcessKeyboard(BACKWARD, deltaTime); // Move camera backward with callback
        } else if (keys[GLFW_KEY_COMMA]) { // If comma --> less than symbol
            camera.ProcessKeyboard(UPROLL, deltaTime); // Move camera in positive roll with callback
        } else if (keys[GLFW_KEY_PERIOD]) { // If period --> greater than symbol
            camera.ProcessKeyboard(DOWNROLL, deltaTime); // Move camera in negative roll with callback
        }
    }
    else if (keys[GLFW_KEY_LEFT_CONTROL] || keys[GLFW_KEY_RIGHT_CONTROL]) { // If either ctrl key is pressed
        if (keys[GLFW_KEY_DOWN]) { // If down arrow
            camera.ProcessKeyboard(UPPITCH, deltaTime); // Move camera in positive pitch with callback
        } else if (keys[GLFW_KEY_UP]) { // If up arrow
            camera.ProcessKeyboard(DOWNPITCH, deltaTime); // Move camera in negative pitch with callback
        } else if (keys[GLFW_KEY_RIGHT]) { // If right arrow
            camera.ProcessKeyboard(UPYAW, deltaTime); // Move camera in positive yaw with callback
        } else if (keys[GLFW_KEY_LEFT]) { // If left arrow
            camera.ProcessKeyboard(DOWNYAW, deltaTime); // Move camera in negative yaw with callback
        }
    }
    else if (keys[GLFW_KEY_RIGHT]) { // If right arrow
        camera.ProcessKeyboard(RIGHT, deltaTime); // Translate camera right with callback
    }
    else if (keys[GLFW_KEY_LEFT]) { // If left arrow
        camera.ProcessKeyboard(LEFT, deltaTime); // Translate camera left with callback
    }
    else if (keys[GLFW_KEY_UP]) { // If up arrow
        camera.ProcessKeyboard(UP, deltaTime); // Translate camera up with callback
    }
    else if (keys[GLFW_KEY_DOWN]) { // If down arrow
        camera.ProcessKeyboard(DOWN, deltaTime); // Translate camera down with callback
    }
    else if (keys[GLFW_KEY_R]) { // If r is pressed
        camera.ResetCamera(); // Reset camera with callback
    }
}

# data[].attributes.attachment_link.readable_text
#ifndef CAMERA_H
#define CAMERA_H

#include <GL/glew.h> // Glew include
#include <glm/glm.hpp> // GLM include
#include <glm/gtc/matrix_transform.hpp> // Matrix transform include

#include <vector> // Include vector

// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {
	FORWARD, // Allow forward camera movement [z]
	BACKWARD, // Allow backward camera movement [z]
	LEFT, // Move along x-axis
	RIGHT, // Move along x-axis
	UP, // Move along y-axis
	DOWN, // Move along y-axis
	UPPITCH, // Rotate pitch positive
	DOWNPITCH, // Rotate pitch negative
	UPYAW, // Rotate yaw positive
	DOWNYAW, // Rotate yaw negative
	UPROLL, // Rotate roll positive
	DOWNROLL // Rotate roll negative
};

// Default camera values
const float YAW = -90.0f; // Initialize yaw [flip screen to normal]
const float PITCH = 0.0f; // Initailize pitch
const float ROLL = 0.0f; // Initialize roll
const float SPEED = 2.5f; // Initialize speed


// An abstract camera class that processes input and calculates the corresponding Euler Angles, Vectors and Matrices for use in OpenGL
class Camera
{
public:
	// Camera Attributes
	glm::vec3 Position; // Position vector
	glm::vec3 Front; // Front dir vector
	glm::vec3 Up; // Up dir vector
	glm::vec3 Right; // Right dir vector
	glm::vec3 WorldUp; // WorldUp vector
	// Euler Angles
	float Yaw; // yaw
	float Pitch; // pitch
	float Roll; // roll
	// Camera options
	float MovementSpeed; // speed for translation

	// Constructor with vectors
	Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH, float roll = ROLL) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED)
	{
		Position = position; // Set position based on input
		WorldUp = up; // Set up reference based on input
		Yaw = yaw; // Set yaw based on input
		Pitch = pitch; // Set pitch based on input
		Roll = roll; // Set roll based on input
		updateCameraVectors(); // Call class method to set front, right, and up vectors
	}
	// Constructor with scalar values
	Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch, float roll) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED)
	{
		Position = glm::vec3(posX, posY, posZ); // Set position elements manually from input
		WorldUp = glm::vec3(upX, upY, upZ); // Set WorldUp elements manually from input
		Yaw = yaw; // Set yaw based on input
		Pitch = pitch; // Set pitch based on input
		Roll = roll; // Set roll based on input
		updateCameraVectors(); // Call class method to set front, right, and up vectors
	}

	// Returns the view matrix calculated using Euler Angles and the LookAt Matrix
	glm::mat4 GetViewMatrix()
	{
		return glm::lookAt(Position, Position + Front, Up); // Returns lookAt output using Posiiton, Position + Front, and Up
	}

	// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
	void ProcessKeyboard(Camera_Movement direction, float deltaTime)
	{
		// Defines all directions
		float velocity = MovementSpeed * deltaTime; // Calculate velocity using speed and time
		if (direction == FORWARD) // If forward direction
			Position += Front * velocity; // Add front * velocity to position
		if (direction == BACKWARD) // If backward
			Position -= Front * velocity; // Subtract front * velocity from position
		if (direction == LEFT) // If left
			Position -= Right * velocity; // Subtract Right * velocity from position
		if (direction == RIGHT) // If right
			Position += Right * velocity; // Add right * velocity to position
		if (direction == UP) // If up
			Position += Up * velocity; // Add up * velocity to position
		if (direction == DOWN) // If down
			Position -= Up * velocity; // Subtract up * velocity to position
		if (direction == UPPITCH) // If up pitch
			Pitch += 2.0f; // Increment pitch by 2 [degrees]
			updateCameraVectors(); // Update rotation using callback
		if (direction == DOWNPITCH) // If down pitch
			Pitch -= 2.0f; // Decrement pitch by 2 [degrees]
			updateCameraVectors(); // Update rotation using callback
		if (direction == UPYAW) // If up yaw
			Yaw += 2.0f; // Increment yaw by 2 [degrees]
			updateCameraVectors(); // Update rotation using callback
		if (direction == DOWNYAW) // If down yaw
			Yaw -= 2.0f; // Decrement yaw by 2 [degrees]
			updateCameraVectors(); // Update rotation using callback
		if (direction == UPROLL) // If up roll
			Roll += 2.0f; // Increment roll by 2 [degrees]
			updateCameraVectors(); // Update rotation using callback
		if (direction == DOWNROLL) // If down roll
			Roll -= 2.0f; // Decrement roll by 2 [degrees]
			updateCameraVectors(); // Update rotation using callback
	}

	// Resets camera position and rotation vectors
	void ResetCamera() {
		Position = glm::vec3(0.0f, 0.0f, 0.0f); // Resets position
		WorldUp = glm::vec3(0.0f, 1.0f, 0.0f); // Resets worldup vector
		Yaw = YAW; // Resets yaw
		Pitch = PITCH; // Resets pitch
		Roll = ROLL; // Resets roll
		updateCameraVectors(); // Updates rotation with reset values using callback
	}

private:
	// Calculates the front vector from the Camera's (updated) Euler Angles
	void updateCameraVectors()
	{
		// Calculate the new Front vector
		glm::vec3 front; // Initialize front
		front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch)); // Define x value for front using cos(Yaw) * cos(Pitch)
		front.y = sin(glm::radians(Pitch));// Define y value for front using sin(pitch)
		front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch)); // Define z value for front using sin(Yaw) * cos(Pitch)
		Front = glm::normalize(front); // Normalize vector
		// // Also re-calculate the Right and Up vector --> normalize right and up and cross product with Front
		Right = glm::normalize(glm::cross(Front, WorldUp));  // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
		Up = glm::normalize(glm::cross(Right, Front)); // Normalize the cross product of Right and Front
		Up.x = sin(glm::radians(Roll)); // Set x of Up to sin(Roll)
		Up.y = cos(glm::radians(Roll)); // Set y of Up to cos(Roll)
		Up = glm::normalize(Up); // Normalize new Up
		Right = glm::normalize(glm::cross(Front, Up)); // Reset Right to reflect new Up axis vector
	}
};
#endif

# data[].attributes.body
1. Change <SOIL.h> to <SOIL/SOIL.h>
2. Change glm::mat4 model; to glm::mat4 model = glm::mat4(1.0f);
3. Ensure all files are present in directory (awesomeface.png, container_original.jpg, Shader.h, CameraCircle.frag, CameraCircle.vs)
4. Ensure line with "Shader ourShader" says ourShader("CameraCircle.vs", "CameraCircle.frag"), not ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.frag")

# data[].attributes.body
Right now it doesn't work with larger objects. You'll see that some faces aren't rendered. I believe that something is just capping the number of faces that are being rendered. It might be an issue with how im exporting the object.

# data[].attributes.attachment_link.readable_text
#include <iostream>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL/SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

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

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "More 3D example", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // Setup OpenGL options
    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader ourShader("MoreCubes.vs", "MoreCubes.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture 
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container_original.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

        // Activate shader
        ourShader.Use();

        // Create transformations
        glm::mat4 model = glm::mat4(1.0f);
        glm::mat4 view = glm::mat4(1.0f);
        glm::mat4 projection;
        
        GLfloat angle = 20.0f * 1; // this together with below won't make it spin
        model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f)); // this won't make rotate
        
        //model = glm::rotate(model, (GLfloat)glfwGetTime() * 50.0f, glm::vec3(0.5f, 1.0f, 0.0f)); // this will make it spin
        
        view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
        // Note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once.
        projection = glm::perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get their uniform location
        GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
        GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");
        // Pass them to the shaders
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // Draw container
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

# data[].attributes.attachment_link.readable_text
sudo apt-get install libglfw3 libglfw3-dev


# data[].attributes.attachment_link.readable_text
#include <math.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstdio>
#include <locale>
#include <unistd.h>

using namespace std;

char color;
float r, g, b;

void pick()
{
    cout << "\n\nPlease input a color (Red = r, Green = g, Blue = b, Black = l, White = w, Yellow = y, Magenta = m):\n";
    
    cin >> color;
    
    cout << "\n\n";
    
    switch (color)
    {
        case 'r':
            r = 1.0f, g = 0.0f, b = 0.0f;
            break;
        case 'g':
            r = 0.0f, g = 1.0f, b = 0.0f;
            break;
        case 'b':
            r = 0.0f, g = 0.0f, b = 1.0f;
            break;
        case 'l':
            r = 0.0f, g = 0.0f, b = 0.0f;
            break;
        case 'w':
            r = 1.0f, g = 1.0f, b = 1.0f;
            break;
        case 'y':
            r = 1.0f, g = 1.0f, b = 0.0f;
            break;
        case 'm':
            r = 1.0f, g = 0.0f, b = 1.0f;
            break;
        default:
            cout << "Invalid selection\n\n";
            exit(0);
    }
}

void myDisplay(void)
{
    glClearColor(r, g, b, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);     // clear the screen 
    glFlush();
}

int main(int argc, char** argv)
{
    
    pick();
	glutInit(&argc, argv);          // initialize the toolkit
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // set display mode
	glutInitWindowSize(500,500);     // set window size
	glutInitWindowPosition(100, 150); // set window position on screen
	glutCreateWindow("Ask Color"); // open the screen window
	glutDisplayFunc(myDisplay);     // register redraw function
	glutMainLoop(); 		     // go into a perpetual loop
}

# data[].attributes.attachment_link.readable_text
void hardwiredHouse(void)
{
	glBegin(GL_LINE_LOOP);
		glVertex2i(40, 40);	// draw the shell of house 
		glVertex2i(40, 90);
		glVertex2i(70, 120);
		glVertex2i(100, 90);
		glVertex2i(100, 40);
	glEnd();
	glBegin(GL_LINE_STRIP); 
		glVertex2i(50, 100);	// draw the chimney 
		glVertex2i(50, 120);
		glVertex2i(60, 120);
		glVertex2i(60, 110);
	glEnd();
		// draw the door 
		// draw the window 
}

void parameterizedHouse(GLintPoint peak, GLint width, GLint height)
 // the top of house is at the peak; the size of house is given
 //  by height and width
{
	glBegin(GL_LINE_LOOP);
	  glVertex2i(peak.x,             peak.y);  // draw shell of house 
	  glVertex2i(peak.x + width / 2, peak.y - 3 * height /8);
	  glVertex2i(peak.x + width / 2  peak.y -     height);
	  glVertex2i(peak.x - width / 2, peak.y -     height);
	  glVertex2i(peak.x - width / 2, peak.y - 3 * height /8);	
	glEnd();
	draw chimney in the same fashion
	draw the door
	draw the window
}

# data[].attributes.attachment_link.readable_text
//Michael Dunn
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <stdio.h>
#include <iostream>
#include <typeinfo>
#include <fstream>

using namespace std;

int screenHeight = 1000;
struct GLintPoint {
  GLfloat x, y;
};

void myMouseLine(int button, int state, int x, int y)
{
    static GLintPoint corner[2];
    static int numCorners = 0;		         // initial value is 0
    if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){ 
        corner[numCorners].x = x;
        corner[numCorners].y = screenHeight - y;    // flip y coordinate
        numCorners++;		                     // have another point
        if(numCorners == 2){
            glBegin(GL_LINES);
            glVertex2f(corner[0].x,corner[0].y);
            glVertex2f(corner[1].x,corner[1].y);
            glEnd();
            glFlush();
            //glRecti(corner[0].x, corner[0].y, corner[1].x, corner[1].y);
            numCorners = 0;				   // back to 0 corners
		  }
	 }
	 else if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
	glClear(GL_COLOR_BUFFER_BIT);	         // clear the window
	 glFlush();
}

void myMouseRecti(int button, int state, int x, int y)
{
    static GLintPoint corner[2];
    static int numCorners = 0;		         // initial value is 0
    if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){ 
        corner[numCorners].x = x;
        corner[numCorners].y = screenHeight - y;    // flip y coordinate
        numCorners++;		                     // have another point
        if(numCorners == 2){
            glRecti(corner[0].x, corner[0].y, corner[1].x, corner[1].y);
            numCorners = 0;				   // back to 0 corners
		  }
        }
	 else if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
	glClear(GL_COLOR_BUFFER_BIT);	         // clear the window
	 glFlush();
}

void renderScene(void) {
    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor(0.0,0.0,0.0,0.0);
    glLoadIdentity();
    gluOrtho2D(0.0, 1000.0, 0.0, 1000.0);
    glFlush();
}

int main(int argc, char** argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
  glutInitWindowSize(1000, 1000);
  glutInitWindowPosition(0, 0);
  glutCreateWindow("Gasket");
  glutDisplayFunc(renderScene);
  glutMouseFunc(myMouseLine);
  glutMainLoop();
  return 0;
}

# data[].attributes.attachment_link.readable_text
void myKeyboard(unsigned char theKey, int mouseX, int mouseY)
{
	  GLint x = mouseX;
  GLint y = screenHeight - mouseY; // flip the y value as always
	  switch(theKey)
  {
		  case p:
		  	drawDot(x, y);	// draw a dot at the mouse position
		  	break;
	  	case GLUT_KEY_LEFT: List[++last].x = x; // add a point
                        List[  last].y = y;
			  break;
    		 case E:
			  exit(-1);		//terminate the program		
	  	default: 
			break;		      // do nothing
	}
}

# data[].attributes.body
https://learnopengl.com/Advanced-OpenGL/Cubemaps

# data[].attributes.attachment_link.readable_text
We've been using 2D textures for a while now, but there are more texture types we haven't explored yet and in this chapter we'll discuss a texture type that is a combination of multiple textures mapped into one: a cube map.

A cubemap is a texture that contains 6 individual 2D textures that each form one side of a cube: a textured cube. You may be wondering what the point is of such a cube? Why bother combining 6 individual textures into a single entity instead of just using 6 individual textures? Well, cube maps have the useful property that they can be indexed/sampled using a direction vector. Imagine we have a 1x1x1 unit cube with the origin of a direction vector residing at its center. Sampling a texture value from the cube map with an orange direction vector looks a bit like this:

The magnitude of the direction vector doesn't matter. As long as a direction is supplied, OpenGL retrieves the corresponding texels that the direction hits (eventually) and returns the properly sampled texture value.

If we imagine we have a cube shape that we attach such a cubemap to, this direction vector would be similar to the (interpolated) local vertex position of the cube. This way we can sample the cubemap using the cube's actual position vectors as long as the cube is centered on the origin. We thus consider all vertex positions of the cube to be its texture coordinates when sampling a cubemap. The result is a texture coordinate that accesses the proper individual face texture of the cubemap.

Creating a cubemap

A cubemap is a texture like any other texture, so to create one we generate a texture and bind it to the proper texture target before we do any further texture operations. This time binding it to GL_TEXTURE_CUBE_MAP:

 unsigned int textureID; glGenTextures(1, &textureID);glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

Because a cubemap contains 6 textures, one for each face, we have to call glTexImage2D six times with their parameters set similarly to the previous chapters. This time however, we have to set the texture target parameter to match a specific face of the cubemap, telling OpenGL which side of the cubemap we're creating a texture for. This means we have to call glTexImage2D once for each face of the cubemap.

Since we have 6 faces OpenGL gives us 6 special texture targets for targeting a face of the cubemap:

Like many of OpenGL's enums, their behind-the-scenes int value is linearly incremented, so if we were to have an array or vector of texture locations we could loop over them by starting with GL_TEXTURE_CUBE_MAP_POSITIVE_X and incrementing the enum by 1 each iteration, effectively looping through all the texture targets:

 int width, height, nrChannels; unsigned char *data; for(unsigned int i = 0; i < textures_faces.size(); i++) { data = stbi_load(textures_faces[i].c_str(), &width, &height, &nrChannels, 0); glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data ); }

Here we have a vector called textures_faces that contain the locations of all the textures required for the cubemap in the order as given in the table. This generates a texture for each face of the currently bound cubemap.

Because a cubemap is a texture like any other texture, we will also specify its wrapping and filtering methods:

 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

Don't be scared by the GL_TEXTURE_WRAP_R, this simply sets the wrapping method for the texture's R coordinate which corresponds to the texture's 3rd dimension (like z for positions). We set the wrapping method to GL_CLAMP_TO_EDGE since texture coordinates that are exactly between two faces may not hit an exact face (due to some hardware limitations) so by using GL_CLAMP_TO_EDGE OpenGL always returns their edge values whenever we sample between faces.

Then before drawing the objects that will use the cubemap, we activate the corresponding texture unit and bind the cubemap before rendering; not much of a difference compared to normal 2D textures.

Within the fragment shader we also have to use a different sampler of the type samplerCube that we sample from using the texture function, but this time using a vec3 direction vector instead of a vec2. An example of fragment shader using a cubemap looks like this:

 in vec3 textureDir; // direction vector representing a 3D texture coordinate uniform samplerCube cubemap; // cubemap texture sampler void main() { FragColor = texture(cubemap, textureDir); }

That is still great and all, but why bother? Well, it just so happens that there are quite a few interesting techniques that are a lot easier to implement with a cubemap. One of those techniques is creating a skybox.

A skybox is a (large) cube that encompasses the entire scene and contains 6 images of a surrounding environment, giving the player the illusion that the environment he's in is actually much larger than it actually is. Some examples of skyboxes used in videogames are images of mountains, of clouds, or of a starry night sky. An example of a skybox, using starry night sky images, can be seen in the following screenshot of the third elder scrolls game:

You probably guessed by now that skyboxes like this suit cubemaps perfectly: we have a cube that has 6 faces and needs to be textured per face. In the previous image they used several images of a night sky to give the illusion the player is in some large universe while he's actually inside a tiny little box.

There are usually enough resources online where you could find skyboxes like that. These skybox images usually have the following pattern:

If you would fold those 6 sides into a cube you'd get the completely textured cube that simulates a large landscape. Some resources provide the skybox in a format like that in which case you'd have to manually extract the 6 face images, but in most cases they're provided as 6 single texture images.

This particular (high-quality) skybox is what we'll use for our scene and can be downloaded here.

Loading a skybox

Since a skybox is by itself just a cubemap, loading a skybox isn't too different from what we've seen at the start of this chapter. To load the skybox we're going to use the following function that accepts a vector of 6 texture locations:

 unsigned int loadCubemap(vector<std::string> faces) { unsigned int textureID; glGenTextures(1, &textureID);glBindTexture(GL_TEXTURE_CUBE_MAP, textureID); int width, height, nrChannels; for (unsigned int i = 0; i < faces.size(); i++) { unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0); if (data) {glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data ); stbi_image_free(data); } else { std::cout << "Cubemap tex failed to load at path: " << faces[i] << std::endl; stbi_image_free(data); } }glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); return textureID; }

The function itself shouldn't be too surprising. It is basically all the cubemap code we've seen in the previous section, but combined in a single manageable function.

Now, before we call this function we'll load the appropriate texture paths in a vector in the order as specified by the cubemap enums:

 vector<std::string> faces; { "right.jpg", "left.jpg", "top.jpg", "bottom.jpg", "front.jpg", "back.jpg" }; unsigned int cubemapTexture = loadCubemap(faces);

We loaded the skybox as a cubemap with cubemapTexture as its id. We can now finally bind it to a cube to replace that lame clear color we've been using all this time.

Displaying a skybox

Because a skybox is drawn on a cube we'll need another VAO, VBO and a fresh set of vertices like any other 3D object. You can get its vertex data here.

A cubemap used to texture a 3D cube can be sampled using the local positions of the cube as its texture coordinates. When a cube is centered on the origin (0,0,0) each of its position vectors is also a direction vector from the origin. This direction vector is exactly what we need to get the corresponding texture value at that specific cube's position. For this reason we only need to supply position vectors and don't need texture coordinates.

To render the skybox we'll need a new set of shaders which aren't too complicated. Because we only have one vertex attribute the vertex shader is quite simple:

 #version 330 core layout (location = 0) in vec3 aPos; out vec3 TexCoords; uniform mat4 projection; uniform mat4 view; void main() { TexCoords = aPos; gl_Position = projection * view * vec4(aPos, 1.0); }

The interesting part of this vertex shader is that we set the incoming local position vector as the outcoming texture coordinate for (interpolated) use in the fragment shader. The fragment shader then takes these as input to sample a samplerCube:

 #version 330 core out vec4 FragColor; in vec3 TexCoords; uniform samplerCube skybox; void main() { FragColor = texture(skybox, TexCoords); }

The fragment shader is relatively straightforward. We take the vertex attribute's interpolated position vector as the texture's direction vector and use it to sample the texture values from the cubemap.

Rendering the skybox is easy now that we have a cubemap texture, we simply bind the cubemap texture and the skybox sampler is automatically filled with the skybox cubemap. To draw the skybox we're going to draw it as the first object in the scene and disable depth writing. This way the skybox will always be drawn at the background of all the other objects since the unit cube is most likely smaller than the rest of the scene.

 glDepthMask(GL_FALSE); skyboxShader.use(); // ... set view and projection matrixglBindVertexArray(skyboxVAO);glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);glDrawArrays(GL_TRIANGLES, 0, 36);glDepthMask(GL_TRUE); // ... draw rest of the scene

If you run this you will get into difficulties though. We want the skybox to be centered around the player so that no matter how far the player moves, the skybox won't get any closer, giving the impression the surrounding environment is extremely large. The current view matrix however transforms all the skybox's positions by rotating, scaling and translating them, so if the player moves, the cubemap moves as well! We want to remove the translation part of the view matrix so only rotation will affect the skybox's position vectors.

You may remember from the basic lighting chapter that we can remove the translation section of transformation matrices by taking the upper-left 3x3 matrix of the 4x4 matrix. We can achieve this by converting the view matrix to a 3x3 matrix (removing translation) and converting it back to a 4x4 matrix:

 glm::mat4 view = glm::mat4(glm::mat3(camera.GetViewMatrix()));

This removes any translation, but keeps all rotation transformations so the user can still look around the scene.

The result is a scene that instantly looks enormous due to our skybox. If you'd fly around the basic container you immediately get a sense of scale which dramatically improves the realism of the scene. The result looks something like this:

Try experimenting with different skyboxes and see how they can have an enormous impact on the look and feel of your scene.

An optimization

Right now we've rendered the skybox first before we rendered all the other objects in the scene. This works great, but isn't too efficient. If we render the skybox first we're running the fragment shader for each pixel on the screen even though only a small part of the skybox will eventually be visible; fragments that could have easily been discarded using early depth testing saving us valuable bandwidth.

So to give us a slight performance boost we're going to render the skybox last. This way, the depth buffer is completely filled with all the scene's depth values so we only have to render the skybox's fragments wherever the early depth test passes, greatly reducing the number of fragment shader calls. The problem is that the skybox will most likely render on top of all other objects since it's only a 1x1x1 cube, succeeding most depth tests. Simply rendering it without depth testing is not a solution since the skybox will then still overwrite all the other objects in the scene as it's rendered last. We need to trick the depth buffer into believing that the skybox has the maximum depth value of 1.0 so that it fails the depth test wherever there's a different object in front of it.

In the coordinate systems chapter we said that perspective division is performed after the vertex shader has run, dividing the gl_Position's xyz coordinates by its w component. We also know from the depth testing chapter that the z component of the resulting division is equal to that vertex's depth value. Using this information we can set the z component of the output position equal to its w component which will result in a z component that is always equal to 1.0, because when the perspective division is applied its z component translates to w / w = 1.0:

 void main() { TexCoords = aPos; vec4 pos = projection * view * vec4(aPos, 1.0); gl_Position = pos.xyww; }

The resulting normalized device coordinates will then always have a z value equal to 1.0: the maximum depth value. The skybox will as a result only be rendered wherever there are no objects visible (only then it will pass the depth test, everything else is in front of the skybox).

We do have to change the depth function a little by setting it to GL_LEQUAL instead of the default GL_LESS. The depth buffer will be filled with values of 1.0 for the skybox, so we need to make sure the skybox passes the depth tests with values less than or equal to the depth buffer instead of less than.

You can find the more optimized version of the source code here.

We now have the entire surrounding environment mapped in a single texture object and we could use that information for more than just a skybox. Using a cubemap with an environment, we could give objects reflective or refractive properties. Techniques that use an environment cubemap like this are called environment mapping techniques and the two most popular ones are reflection and refraction.

Reflection

Reflection is the property that an object (or part of an object) reflects its surrounding environment e.g. the object's colors are more or less equal to its environment based on the angle of the viewer. A mirror for example is a reflective object: it reflects its surroundings based on the viewer's angle.

The basics of reflection are not that difficult. The following image shows how we can calculate a reflection vector and use that vector to sample from a cubemap:

We calculate a reflection vector \(\color{green}{\bar{R}}\) around the object's normal vector \(\color{red}{\bar{N}}\) based on the view direction vector \(\color{gray}{\bar{I}}\). We can calculate this reflection vector using GLSL's built-in reflect function. The resulting vector \(\color{green}{\bar{R}}\) is then used as a direction vector to index/sample the cubemap, returning a color value of the environment. The resulting effect is that the object seems to reflect the skybox.

Since we already have a skybox setup in our scene, creating reflections isn't too difficult. We'll change the fragment shader used by the container to give the container reflective properties:

 #version 330 core out vec4 FragColor; in vec3 Normal; in vec3 Position; uniform vec3 cameraPos; uniform samplerCube skybox; void main() { vec3 I = normalize(Position - cameraPos); vec3 R = reflect(I, normalize(Normal)); FragColor = vec4(texture(skybox, R).rgb, 1.0); }

We first calculate the view/camera direction vector I and use this to calculate the reflect vector R which we then use to sample from the skybox cubemap. Note that we have the fragment's interpolated Normal and Position variable again so we'll need to adjust the vertex shader as well:

 #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; out vec3 Normal; out vec3 Position; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { Normal = mat3(transpose(inverse(model))) * aNormal; Position = vec3(model * vec4(aPos, 1.0)); gl_Position = projection * view * vec4(Position, 1.0); }

We're using normal vectors so we'll want to transform them with a normal matrix again. The Position output vector is a world-space position vector. This Position output of the vertex shader is used to calculate the view direction vector in the fragment shader.

Because we're using normals you'll want to update the vertex data and update the attribute pointers as well. Also make sure to set the cameraPos uniform.

Then we also want to bind the cubemap texture before rendering the container:

 glBindVertexArray(cubeVAO);glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxTexture);glDrawArrays(GL_TRIANGLES, 0, 36);

Compiling and running your code gives you a container that acts like a perfect mirror. The surrounding skybox is perfectly reflected on the container:

You can find the full source code here.

When reflection is applied to an entire object (like the container) the object looks as if it has a high reflective material like steel or chrome. If we were to load a more interesting object (like the backpack model from the model loading chapters) we'd get the effect that the object looks to be entirely made out of chrome:

This looks quite awesome, but in reality most models aren't all completely reflective. We could for instance introduce reflection maps that give the models another extra level of detail. Just like diffuse and specular maps, reflection maps are texture images that we can sample to determine the reflectivity of a fragment. Using these reflection maps we can determine which parts of the model show reflection and by what intensity.

Refraction

Another form of environment mapping is called refraction and is similar to reflection. Refraction is the change in direction of light due to the change of the material the light flows through. Refraction is what we commonly see with water-like surfaces where the light doesn't enter straight through, but bends a little. It's like looking at your arm when it's halfway in the water.

Refraction is described by Snell's law that with environment maps looks a bit like this:

Again, we have a view vector \(\color{gray}{\bar{I}}\), a normal vector \(\color{red}{\bar{N}}\) and this time a resulting refraction vector \(\color{green}{\bar{R}}\). As you can see, the direction of the view vector is slightly bend. This resulting bended vector \(\color{green}{\bar{R}}\) is then used to sample from the cubemap.

Refraction is fairly easy to implement using GLSL's built-in refract function that expects a normal vector, a view direction, and a ratio between both materials' refractive indices.

The refractive index determines the amount light distorts/bends in a material where each material has its own refractive index. A list of the most common refractive indices are given in the following table:

We use these refractive indices to calculate the ratio between both materials the light passes through. In our case, the light/view ray goes from air to glass (if we assume the object is made of glass) so the ratio becomes \(\frac{1.00}{1.52} = 0.658\).

We already have the cubemap bound, supplied the vertex data with normals, and set the camera position as a uniform. The only thing we have to change is the fragment shader:

 void main() { float ratio = 1.00 / 1.52; vec3 I = normalize(Position - cameraPos); vec3 R = refract(I, normalize(Normal), ratio); FragColor = vec4(texture(skybox, R).rgb, 1.0); }

By changing the refractive indices you can create completely different visual results. Compiling the application and running the results on the container object is not so interesting though as it doesn't really show the effect refraction has aside that it acts as a magnifying glass right now. Using the same shaders on the loaded 3D model however does show us the effect we're looking for: a glass-like object.

You can imagine that with the right combination of lighting, reflection, refraction and vertex movement, you can create pretty neat water graphics. Do note that for physically accurate results we should refract the light again when it leaves the object; now we simply used single-sided refraction which is fine for most purposes.

Dynamic environment maps

Right now we've been using a static combination of images as the skybox, which looks great, but it doesn't include the actual 3D scene with possibly moving objects. We didn't really notice this so far, because we only used a single object. If we had a mirror-like objects with multiple surrounding objects, only the skybox would be visible in the mirror as if it was the only object in the scene.

Using framebuffers it is possible to create a texture of the scene for all 6 different angles from the object in question and store those in a cubemap each frame. We can then use this (dynamically generated) cubemap to create realistic reflection and refractive surfaces that include all other objects. This is called dynamic environment mapping, because we dynamically create a cubemap of an object's surroundings and use that as its environment map.

While it looks great, it has one enormous disadvantage: we have to render the scene 6 times per object using an environment map, which is an enormous performance penalty on your application. Modern applications try to use the skybox as much as possible and where possible pre-render cubemaps wherever they can to still sort-of create dynamic environment maps. While dynamic environment mapping is a great technique, it requires a lot of clever tricks and hacks to get it working in an actual rendering application without too many performance drops.

# data[].attributes.attachment_link.description
We've been using 2D textures for a while now, but there are more texture types we haven't explored yet and in this chapter we'll discuss a texture type that is a combination of multiple textures mapped into one: a cube map.

# data[].attributes.body
Reinstall Ubuntu and follow this tutorial
Possibly only works for Windows 11
https://ubuntu.com/tutorials/install-ubuntu-on-wsl2-on-windows-11-with-gui-support#5-install-and-use-a-gui-package

# data[].attributes.attachment_link.readable_text
Windows Subsystem for Linux (WSL) allows you to install a complete Ubuntu terminal environment in minutes on your Windows machine, allowing you to develop cross-platform applications without leaving windows.

What you will learn:

How to enable and install WSL on Windows 11

How to install and run a simple graphical application that uses WSLg

How to install and run a much more advanced application that uses WSLg

What you will need:

A Windows 11 physical or virtual machine with all the updates installed

We saw in the tutorial Install Ubuntu WSL on Windows 10 that WSL can be installed from the command line. In Windows 11, it is also available directly from the Microsoft store as an application. Installation from the store will install the latest version of WSL with all the latest features.

Prerequisites

Make sure the following prerequisites are met before installing:

You are running Windows 11 (build 22000 or higher)

Virtual Machine Platform optional feature is enabled

To check the build number, open the Windows menu by pressing the Windows key, then type about in the search field, as shown in the following screenshot:

And select the line About your PC to launch the System / About page.

The OS build number must be higher than 22000. If it is not, then WSL cannot be installed by following this tutorial. Follow the instructions here instead.

The second prerequisite to verify is the Virtual Platform feature.

Search for Windows features.

Open the control panel and scroll down to Virtual Machine Platform.

After installation of the Virtual Machine Platform component, you must restart Windows 11 before proceeding with the installation of WSL components and an Ubuntu application.

Installation of WSL from the Microsoft Store

Duration: 3:00

WSL components are now available as a preview version directly from the Microsoft Store like other Windows applications.

To install the WSL application from the Microsoft Store, open it and search for Windows subsystem.

Click on the item Windows Subsystem for Linux Preview to open the corresponding application page.

Click on Get to download and install the application.

Upon installation, you can click on Open, but it will not do much since there is no Linux distribution installed.

However, if you really want to open the WSL application without installing a distribution, you'll see a nice and short help message that you must follow in order to make something useful with WSL:

You can now proceed with the installation of Ubuntu.

WSL supports a variety of Linux distributions including the latest Ubuntu release, Ubuntu 20.04 LTS, and Ubuntu 18.04 LTS. You can find them by opening the Microsoft Store app and searching for Ubuntu.

Choose the distribution you prefer and then select Get.

Ubuntu will then install on your machine.

Once installed, you can either launch the application directly from the store or search for Ubuntu in your Windows search bar.

Congratulations, you now have an Ubuntu terminal running on your Windows machine!

Once it has finished its initial setup, you will need to create a username and password (this does not need to match your Windows user credentials).

Finally, it's always good practice to install the latest updates with the following commands, entering your password when prompted.
sudo apt update
Then
sudo apt full-upgrade
Press Y when prompted.

On Windows 11, WSL comes with WSLg enabled by default. WSLg allows you to run graphical linux applications.

To check that you have the latest package lists, type:
sudo apt update
Then, start with some basic X11 applications:
sudo apt install x11-apps
To run the xeyes, a "follow the mouse" application, type:

The & at the end of the line will execute the command asynchronously. In other words, the shell will run the command in the background and return to the command prompt immediately.

The first launch of a GUI application takes a few seconds while WSL is initializing the graphics stack. Next executions of GUI applications are much faster.

Leave xeyes opened and run the calculator xcalc with:

When you move the cursor over the calculator, xeyes follows the cursor. This shows that several GUI applications can interact together.

Note that applications running under WSLg display a little penguin at the bottom right corner of their icons in the Windows taskbar. That's one way you can distinguish applications running on Windows or Ubuntu (besides the window decoration and styling).

Close xeyes and xcalc by pressing the cross icon on the top right corner of each X application window.

Xcalc and xeyes are very basic X Windows applications but there are plenty of choices in the Linux ecosystem corresponding to your needs and available out of the box on Ubuntu.

In the following example, we will use GNU Octave to perform numerical computation.

We will use it to calculate and draw a beautiful Julia fractal. The goal here is to use Octave to demonstrate how WSLg works, not to go through the theory of fractals.

First thing is to install the software like we did for x11-apps, from the terminal prompt run:
sudo apt install octave
Then start the application:

Do not forget the ampersand & at the end of the line, so the application is started in the background and we can continue using the same terminal window.

In Octave, click on the New script icon to open a new editor window and copy/paste the following code:

#{ Inspired by the work of Bruno Girin ([Geek Thoughts: Fractals with Octave: Classic Mandelbrot and Julia](http://brunogirin.blogspot.com/2008/12/fractals-with-octave-classic-mandelbrot.html)) Calculate a Julia set zmin: Minimum value of c zmax: Maximum value of c hpx: Number of horizontal pixels niter: Number of iterations c: A complex number #} function M = julia(zmin, zmax, hpx, niter, c) %% Number of vertical pixels vpx=round(hpx*abs(imag(zmax-zmin)/real(zmax-zmin))); %% Prepare the complex plane [zRe,zIm]=meshgrid(linspace(real(zmin),real(zmax),hpx), linspace(imag(zmin),imag(zmax),vpx)); z=zRe+i*zIm; M=zeros(vpx,hpx); %% Generate Julia for s=1:niter mask=abs(z)<2; M(mask)=M(mask)+1; z(mask)=z(mask).^2+c; end M(mask)=0; end

This code is the function that will calculate the Julia set.

Save it to a file named julia.m. Since it is a function definition, the name of the file must match the name of the function.

Open a second editor window with the New Script button and copy and paste the following code:

Jc1=julia(-1.6+1.2i, 1.6-1.2i, 640, 128, -0.75+0.2i); imagesc(Jc1) axis off colormap('default');

This code calls the function defined in julia.m. You can later change the parameters if you want to explore the Julia fractal.

Save it to a file named juliatest.m.

And finally, press the button Save File and Run.

After a few seconds, depending on your hardware and the parameters, a Julia fractal is displayed.

Like Octave, this window is displayed using WSLg completely transparently to the user.

Enjoy!

# data[].attributes.attachment_link.title
Install Ubuntu on WSL2 on Windows 11 with GUI support | Ubuntu

# data[].attributes.attachment_link.description
Windows Subsystem for Linux (WSL) allows you to install a complete Ubuntu terminal environment in minutes on your Windows machine, allowing you to develop cross-platform applications without leaving windows.

# data[].attributes.attachment_link.readable_text
1) Prepare your PC
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

2) Go to microsoft store
install Linux

3) Set up Linux
sudo apt-get update
sudo apt-get upgrade

4) Install GUI
Download and install Xming from https://sourceforge.net/projects/xming/
or 
Download and Install VcXsrv which is an X windows server emulator https://sourceforge.net/projects/vcxsrv/ (preferable to install this emulator)

5) Tweak Ubuntu
echo �export DISPLAY=:0.0� >> ~/.bashrc
echo "export DISPLAY=localhost:0" >> ~/.bashrc

6) Install a graphical program
sudo apt-get install gedit

7) invoke gedit from terminal
It should work. Now you are ready to install other graphical programs

8)[Accessing Windows Directories]
C: Drive location /mnt/c do cd /mnt/c and we can access the files in window.

9)In case you want to install a a lightweight one called Xfce4. 
Run each command here in turn:

sudo apt install xfce4
sudo apt install gnome-themes-standard
sudo apt install conky
then run the command
nano ~/.xinitrc
In the text editor, enter the lines of code that follow. When done, 
press Ctrl-O and hit Return to save it, and Ctrl-x to exit the text editor.
#!/usr/bin/env bash
export LANG="en_US.UTF-8�
export LC_ALL="en_US.UTF-8�
exec startxfce4

Run:
chmod +x ~/.xinitrc

This will make the file we�ve just created executable. Then:
ln -s ~/.xinitrc ~/.xsession

This will �link� that file with another standard X configuration file. 
Start your newly installed Linux desktop with the following command: 

dbuslaunch --exit-with-session ~/.xsession

On the first run, a dialog pops up�select to use the default. 
The Xfce4 desktop appears over the Windows 10 one, so you might want to 
ensure the Ubuntu window isn�t full-screen, because the window bar can be blocked. 
You should spot an �Application� menu top-left, there�s a dock at the bottom, 
with a taskbar running along the top. To quit, use the �Log out� option in the 
top-left menu (see image below).

You won�t find too much to play with at this point, because it�s a Linux desktop 
running on a largely sparse distro. You can add programs, such as Firefox, via sudo 
apt-get install firefox and this then appears under the �Internet� menu. Have fun!


creating a virtual machine
https://www.windowscentral.com/how-run-linux-distros-windows-10-using-hyper-v

# data[].attributes.attachment_link.readable_text
// This application shows balls bouncing on a checkerboard, with no respect
// for the laws of Newtonian Mechanics.  There's a little spotlight to make
// the animation interesting, and arrow keys move the camera for even more
// fun.

#include <GL/glut.h>
#include <cmath>

// Colors
GLfloat WHITE[] = {1, 1, 1};
GLfloat RED[] = {1, 0, 0};
GLfloat GREEN[] = {0, 1, 0};
GLfloat MAGENTA[] = {1, 0, 1};

// A camera.  It moves horizontally in a circle centered at the origin of
// radius 10.  It moves vertically straight up and down.
class Camera {
  double theta;      // determines the x and z positions
  double y;          // the current y position
  double dTheta;     // increment in theta for swinging the camera around
  double dy;         // increment in y for moving the camera up/down
public:
  Camera(): theta(0), y(3), dTheta(0.04), dy(0.2) {}
  double getX() {return 10 * cos(theta);}
  double getY() {return y;}
  double getZ() {return 10 * sin(theta);}
  void moveRight() {theta += dTheta;}
  void moveLeft() {theta -= dTheta;}
  void moveUp() {y += dy;}
  void moveDown() {if (y > dy) y -= dy;}
};

// A ball.  A ball has a radius, a color, and bounces up and down between
// a maximum height and the xz plane.  Therefore its x and z coordinates
// are fixed.  It uses a lame bouncing algorithm, simply moving up or
// down by 0.05 units at each frame.
class Ball {
  double radius;
  GLfloat* color;
  double maximumHeight;
  double x;
  double y;
  double z;
  int direction;
public:
  Ball(double r, GLfloat* c, double h, double x, double z):
      radius(r), color(c), maximumHeight(h), direction(-1),
      y(h), x(x), z(z) {
  }
  void update() {
    y += direction * 0.05;
    if (y > maximumHeight) {
      y = maximumHeight; direction = -1;
    } else if (y < radius) {
      y = radius; direction = 1;
    }
    glPushMatrix();
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
    glTranslated(x, y, z);
    glutSolidSphere(radius, 30, 30);
    glPopMatrix();
  }
};

// A checkerboard class.  A checkerboard has alternating red and white
// squares.  The number of squares is set in the constructor.  Each square
// is 1 x 1.  One corner of the board is (0, 0) and the board stretches out
// along positive x and positive z.  It rests on the xz plane.  I put a
// spotlight at (4, 3, 7).
class Checkerboard {
  int displayListId;
  int width;
  int depth;
public:
  Checkerboard(int width, int depth): width(width), depth(depth) {}
  double centerx() {return width / 2;}
  double centerz() {return depth / 2;}
  void create() {
    displayListId = glGenLists(1);
    glNewList(displayListId, GL_COMPILE);
    GLfloat lightPosition[] = {4, 3, 7, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    glBegin(GL_QUADS);
    glNormal3d(0, 1, 0);
    for (int x = 0; x < width - 1; x++) {
      for (int z = 0; z < depth - 1; z++) {
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
                     (x + z) % 2 == 0 ? RED : WHITE);
        glVertex3d(x, 0, z);
        glVertex3d(x+1, 0, z);
        glVertex3d(x+1, 0, z+1);
        glVertex3d(x, 0, z+1);
      }
    }
    glEnd();
    glEndList();
  }
  void draw() {
    glCallList(displayListId);
  }
};

// Global variables: a camera, a checkerboard and some balls.
Checkerboard checkerboard(8, 8);
Camera camera;
Ball balls[] = {
  Ball(1, GREEN, 7, 6, 1),
  Ball(1.5, MAGENTA, 6, 3, 4),
  Ball(0.4, WHITE, 5, 1, 7)
};


// Application-specific initialization: Set up global lighting parameters
// and create display lists.
void init() {
  glEnable(GL_DEPTH_TEST);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, WHITE);
  glLightfv(GL_LIGHT0, GL_SPECULAR, WHITE);
  glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE);
  glMaterialf(GL_FRONT, GL_SHININESS, 30);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  checkerboard.create();
}

// Draws one frame, the checkerboard then the balls, from the current camera
// position.
void display() {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();
  gluLookAt(camera.getX(), camera.getY(), camera.getZ(),
            checkerboard.centerx(), 0.0, checkerboard.centerz(),
            0.0, 1.0, 0.0);
  checkerboard.draw();
  for (int i = 0; i < sizeof balls / sizeof(Ball); i++) {
    balls[i].update();
  }
  glFlush();
  glutSwapBuffers();
}

// On reshape, constructs a camera that perfectly fits the window.
void reshape(GLint w, GLint h) {
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(40.0, GLfloat(w) / GLfloat(h), 1.0, 150.0);
  glMatrixMode(GL_MODELVIEW);
}

// Requests to draw the next frame.
void timer(int v) {
  glutPostRedisplay();
  glutTimerFunc(1000/60, timer, v);
}

// Moves the camera according to the key pressed, then ask to refresh the
// display.
void special(int key, int, int) {
  switch (key) {
    case GLUT_KEY_LEFT: camera.moveLeft(); break;
    case GLUT_KEY_RIGHT: camera.moveRight(); break;
    case GLUT_KEY_UP: camera.moveUp(); break;
    case GLUT_KEY_DOWN: camera.moveDown(); break;
  }
  glutPostRedisplay();
}

// Initializes GLUT and enters the main loop.
int main(int argc, char** argv) {
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowPosition(80, 80);
  glutInitWindowSize(800, 600);
  glutCreateWindow("Bouncing Balls");
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutSpecialFunc(special);
  glutTimerFunc(100, timer, 0);
  init();
  glutMainLoop();
}

# data[].attributes.attachment_link.readable_text
#include <iostream>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL.h>

// Other includes
#include "Shader.h"


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

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

// Holds uniform value of texture mix
GLfloat mixValue = 0.2f;

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);


    // Build and compile our shader program
    Shader ourShader("path/to/shaders/textures.vs", "path/to/shaders/textures.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        // Positions          // Colors           // Texture Coords
         0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // Top Right
         0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // Bottom Right
        -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // Bottom Left
        -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // Top Left 
    };
    GLuint indices[] = {  // Note that we start from 0!
        0, 1, 3, // First Triangle
        1, 2, 3  // Second Triangle
    };
    GLuint VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // Color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture 
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);
   

    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
	
        // Activate shader
        ourShader.Use();     

        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);  

        // Set current value of uniform mix
        glUniform1f(glGetUniformLocation(ourShader.Program, "mixValue"), mixValue);
        
        // Draw container
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    // Change value of uniform with arrow keys (sets amount of textre mix)
    if (key == GLFW_KEY_UP && action == GLFW_PRESS)
    {
        mixValue += 0.1f;
        if (mixValue >= 1.0f)
            mixValue = 1.0f;
    }
    if (key == GLFW_KEY_DOWN && action == GLFW_PRESS)
    {
        mixValue -= 0.1f;
        if (mixValue <= 0.0f)
            mixValue = 0.0f;
    }
}

# data[].attributes.attachment_link.readable_text
#include <iostream>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL.h>

// Other includes
#include "Shader.h"


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

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

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);


    // Build and compile our shader program
    Shader ourShader("path/to/shaders/textures.vs", "path/to/shaders/textures.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        // Positions          // Colors           // Texture Coords
         0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // Top Right
         0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // Bottom Right
        -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // Bottom Left
        -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // Top Left 
    };
    GLuint indices[] = {  // Note that we start from 0!
        0, 1, 3, // First Triangle
        1, 2, 3  // Second Triangle
    };
    GLuint VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // Color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture 
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Activate shader
        ourShader.Use();     

        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);  
        
        // Draw container
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

# data[].attributes.attachment_link.readable_text
#include <iostream>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL.h>

// Other includes
#include "Shader.h"


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

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

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);


    // Build and compile our shader program
    Shader ourShader("path/to/shaders/textures.vs", "path/to/shaders/textures.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        // Positions          // Colors           // Texture Coords
         0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // Top Right
         0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // Bottom Right
        -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // Bottom Left
        -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // Top Left 
    };
    GLuint indices[] = {  // Note that we start from 0!
        0, 1, 3, // First Triangle
        1, 2, 3  // Second Triangle
    };
    GLuint VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // Color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture 
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture); // All upcoming GL_TEXTURE_2D operations now have effect on this texture object
    // Set the texture wrapping parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT (usually basic wrapping method)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load image, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);


        // Bind Texture
        glBindTexture(GL_TEXTURE_2D, texture);

        // Activate shader
        ourShader.Use();       
        
        // Draw container
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

# data[].attributes.attachment_link.readable_text
#include <GL/glut.h>

#include <complex>
using std::complex;

// Render the Mandelbrot set into the image array.
// The parameters specify the region on the complex plane to plot.
void compute_mandelbrot( double left, double right, double top, double bottom )
{
    // The number of times to iterate before we assume that a point isn't in the
    // Mandelbrot set.
    // (You may need to turn this up if you zoom further into the set.)
    const int MAX_ITERATIONS = 500;

    const int width = glutGet( GLUT_WINDOW_WIDTH );
    const int height = glutGet( GLUT_WINDOW_HEIGHT );

    glBegin( GL_POINTS ); // start drawing in single pixel mode
    for( int y = 0; y < height; ++y )
    {
        for( int x = 0; x < width; ++x )
        {
            // Work out the point in the complex plane that
            // corresponds to this pixel in the output image.
            complex<double> c( left + ( x * ( right - left ) / width ),
                top + ( y * ( bottom - top ) / height ) );

            // Start off z at (0, 0).
            complex<double> z( 0.0, 0.0 );

            // Iterate z = z^2 + c until z moves more than 2 units
            // away from (0, 0), or we've iterated too many times.
            int iterations = 0;
            while( abs( z ) < 2.0 && iterations < MAX_ITERATIONS )
            {
                z = ( z * z ) + c;

                ++iterations;
            }

            if( iterations == MAX_ITERATIONS )
            {
                glColor3f( 1.0, 0.0, 0.0 ); // Set color to draw mandelbrot
                // z didn't escape from the circle.
                // This point is in the Mandelbrot set.
                glVertex2i( x, y );
            }
            else
            {
                glColor3f( 0.0, 0.0, 0.0 ); //Set pixel to black
                // z escaped within less than MAX_ITERATIONS
                // iterations. This point isn't in the set.
                glVertex2i( x, y );
            }
        }
    }
    glEnd();
}

void display()
{
    glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
    glClear( GL_COLOR_BUFFER_BIT );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    const int width = glutGet( GLUT_WINDOW_WIDTH );
    const int height = glutGet( GLUT_WINDOW_HEIGHT );
    glOrtho( 0, width, 0, height, -1, 1 );

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    compute_mandelbrot( -2.0, 1.0, 1.125, -1.125 );
    glutSwapBuffers();
}

int main( int argc, char** argv )
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA );
    glutInitWindowSize( 300, 300 );
    glutCreateWindow( "Mandelbrot" );
    glutDisplayFunc( display );
    glutMainLoop();
    return 0;
}

# data[].attributes.attachment_link.readable_text
////////////////////////////////////////////////////////////////////////////////////////         
// fractals.cpp
//
// This program draws a fractal Koch snowflake, a variant of the Koch snowflake
// and a fractal tree, all at various levels of recursion. The same data structures 
// are applied to draw each by changing only certain class member functions.
//
// Interaction:
// Press left/right arrows keys to cycle through the fractals.
// Press up/down arrow keys to increase/decrease the recursion level.
//
// Sumanta Guha.
////////////////////////////////////////////////////////////////////////////////////////

#include <cstdlib>
#include <cmath>
#include <vector>
#include <iostream>
#include <fstream>

#ifdef __APPLE__
#  include <GLUT/glut.h>
#else
#  include <GL/glut.h>
#endif

#define PI 3.14159265
#define ROOT3 1.73205081
#define ONEBYROOT3 0.57735027

#define KOCH 0
#define KOCHVARIANT 1
#define TREE 2

#define RATIO 0.85 // Growth ratio = length of tree sub-branch to length of branch.
#define ANGLE 40 // Angle between the two tree sub-branches.

using namespace std;

// Globals.
static int maxLevel = 0; // Recursion level.
static int shape = KOCH; // Shape index.
static long font = (long)GLUT_BITMAP_8_BY_13; // Font selection.

class Source; // Make source class visible.

// Routine to draw a bitmap character string.
void writeBitmapString(void *font, char *string)
{  
   char *c;

   for (c = string; *c != '\0'; c++) glutBitmapCharacter(font, *c);
}

// Routine to convert integer to char string.
void intToString(char * destStr, int val) 
{
	sprintf(destStr,"%d",val);
}

// Write message.
void writeData(void)
{
   char buffer [33];

   glColor3f(0.0, 0.0, 0.0);
   intToString(buffer, maxLevel);
   glRasterPos3f(-20.0, -45.0, 0.0);

   if (shape == KOCH) writeBitmapString((void*)font, "Koch Snowflake    Level: ");
   if (shape == KOCHVARIANT) writeBitmapString((void*)font, "Variant Koch Snowflake    Level: ");
   if (shape == TREE) writeBitmapString((void*)font, "Tree    Level: ");
   writeBitmapString((void*)font, buffer);
}

// Sequel class.
class Sequel
{
public:
   Sequel() { coords.clear(); v.clear(); }
   void drawKochOrVariant(); // Routine to draw Sequel object in case it represents 
                             // a Koch snowflake or variant Koch snowflake.
   void drawTree(); // Routine to draw Sequel object in case it represents a tree.

   friend class Source;

private:
   vector<float> coords; // Vector of x, y co-ordinates of points specifying sequel object.
   vector<Source> v; // Vector of associated source objects to be produced recursively.
};

// Routine to draw Sequel object in case it represents a Koch snowflake or variant Koch snowflake.
void Sequel::drawKochOrVariant()
{
   glBegin(GL_LINE_STRIP);
   for (int i=0; i < 5; i++)
      glVertex2f(coords[2*i], coords[2*i+1]);
   glEnd();
}

// Routine to draw Sequel object in case it represents a tree.
void Sequel::drawTree()
{
   glBegin(GL_LINE_STRIP);
   for (int i=0; i < 3; i++)
      glVertex2f(coords[2*i], coords[2*i+1]);
   glEnd();
}

// Source class.
class Source
{
public:
   Source(){ }
   Source(float coordsVal[4])
   {
      for (int i=0; i < 4; i++) coords[i] = coordsVal[i];
   }

   void draw(); // Routine to draw source line segment.

   Sequel sourceToSequelKoch(); // Routine to generate Sequel object in case of Koch snowflake.
   void produceKoch(int level); // Recursive routine to produce Koch snowflake.
   
   Sequel sourceToSequelKochVariant(); // Routine to generate Sequel object in case of variant Koch snowflake.
   void produceKochVariant(int level); // Recursive routine to produce variant Koch snowflake.

   Sequel sourceToSequelTree(); // Routine to generate Sequel object in case of tree.
   void produceTree(int level); // Recursive routine to produce tree.

   friend class Sequel;

private:
   float coords[4]; // x, y co-ordinates of a line segment endpoints.
};

// Routine to draw source line segment.
void Source::draw()
{
   glBegin(GL_LINES);
   for (int i=0; i < 2; i++)
      glVertex2f(coords[2*i], coords[2*i+1]);
   glEnd();
}

// Routine to generate Sequel object in case of Koch snowflake.
Sequel Source::sourceToSequelKoch()
{
   float x0, y0, x1, y1, coordsVal[10], coordsVal1[4];
   int i, j;
   Source s;
   Sequel seq = *new Sequel();

   x0 = coords[0]; y0 = coords[1]; x1 = coords[2]; y1 = coords[3];

   // From the co-ordinates of the two segment endpoints calculate the co-ordinates of 
   // the 5 vertices of the Koch polyline.
   coordsVal[0] = x0;
   coordsVal[1] = y0;
   coordsVal[2] = 0.66666667*x0 + 0.33333333*x1;
   coordsVal[3] = 0.66666667*y0 + 0.33333333*y1;
   coordsVal[4] = 0.5*(x0 + x1) - 0.5*ONEBYROOT3*(y1 - y0);
   coordsVal[5] = 0.5*(y0 + y1) + 0.5*ONEBYROOT3*(x1 - x0);
   coordsVal[6] = 0.33333333*x0 + 0.66666667*x1;
   coordsVal[7] = 0.33333333*y0 + 0.66666667*y1;
   coordsVal[8] = x1;
   coordsVal[9] = y1;

   // Enter Koch polyline vertices into sequel object.
   for (i=0; i < 10; i++) seq.coords.push_back(coordsVal[i]);

   // Specify all 4 segments of the Koch polyline for recursive production.
   for (i=0; i < 4; i++)
   {
      for (j=0; j < 4; j++)
	  {
	     coordsVal1[j] = coordsVal[2*i+j];
	  }
	  s = *new Source(coordsVal1);
	  seq.v.push_back(s);
   }

   return seq;
}

// Recursive routine to produce Koch snowflake.
void Source::produceKoch(int level)
{
   glColor3f(0.0, 0.0, 0.0);

   // Only sequels at given level are drawn.
   if (maxLevel == 0) this->draw();
   else if (maxLevel == 1) this->sourceToSequelKoch().drawKochOrVariant();
        else if (level < maxLevel-1)
	            for (int i=0; i < 4; i++) this->sourceToSequelKoch().v[i].produceKoch(level+1);	
             else this->sourceToSequelKoch().drawKochOrVariant();                                
}

// Routine to generate Sequel object in case of variant Koch snowflake.
Sequel Source::sourceToSequelKochVariant()
{
   float x0, y0, x1, y1, coordsVal[10], coordsVal1[4];
   int i;
   Source s;
   Sequel seq = *new Sequel();

   x0 = coords[0]; y0 = coords[1]; x1 = coords[2]; y1 = coords[3];

   // From the co-ordinates of the two segment endpoints calculate the co-ordinates of 
   // the 5 vertices of the Koch polyline.
   coordsVal[0] = x0;
   coordsVal[1] = y0;
   coordsVal[2] = 0.66666667*x0 + 0.33333333*x1;
   coordsVal[3] = 0.66666667*y0 + 0.33333333*y1;
   coordsVal[4] = 0.5*(x0 + x1) - 0.5*ONEBYROOT3*(y1 - y0);
   coordsVal[5] = 0.5*(y0 + y1) + 0.5*ONEBYROOT3*(x1 - x0);
   coordsVal[6] = 0.33333333*x0 + 0.66666667*x1;
   coordsVal[7] = 0.33333333*y0 + 0.66666667*y1;
   coordsVal[8] = x1;
   coordsVal[9] = y1;

   // Enter Koch polyline vertices into sequel object.
   for (i=0; i < 10; i++) seq.coords.push_back(coordsVal[i]);
   
   // Specify the segment joining the first vertex of the Koch polyline to the middle vertex
   // and the segment joining the middle vertex to the last vertex for recursive production.
   for (i=0; i < 2; i++)
   {
	  coordsVal1[0] = coordsVal[4*i];
	  coordsVal1[1] = coordsVal[4*i+1];
	  coordsVal1[2] = coordsVal[4*i+4];
	  coordsVal1[3] = coordsVal[4*i+5];
	  s = *new Source(coordsVal1);
	  seq.v.push_back(s);
   }

   return seq;
}

// Recursive routine to produce variant Koch snowflake.
void Source::produceKochVariant(int level)
{
   glColor3f(0.0, 0.0, 0.0);

   // Only sequels at given level are drawn.
   if (maxLevel == 0) this->draw();
   else if (maxLevel == 1) this->sourceToSequelKochVariant().drawKochOrVariant();
        else if (level < maxLevel-1)
	            for (int i=0; i < 2; i++) this->sourceToSequelKochVariant().v[i].produceKochVariant(level+1);	
             else this->sourceToSequelKochVariant().drawKochOrVariant();                                
}

// Routine to generate Sequel object in case of tree.
Sequel Source::sourceToSequelTree()
{
   float x0, y0, x1, y1, coordsVal[10], coordsVal1[4];
   int i, j;
   Source s;
   Sequel seq = *new Sequel();

   x0 = coords[0]; y0 = coords[1]; x1 = coords[2]; y1 = coords[3];

   // From the co-ordinates of the two segment endpoints calculate the co-ordinates of
   // the 3 vertices of the sub-branch V-shape.
   coordsVal[0] = x1 + RATIO*cos((PI/180.0)*ANGLE/2.0)*(x1-x0) - RATIO*sin((PI/180.0)*ANGLE/2.0)*(y1-y0);
   coordsVal[1] = y1 + RATIO*cos((PI/180.0)*ANGLE/2.0)*(y1-y0) + RATIO*sin((PI/180.0)*ANGLE/2.0)*(x1-x0);
   coordsVal[2] = x1;
   coordsVal[3] = y1;
   coordsVal[4] = x1 + RATIO*cos((PI/180.0)*ANGLE/2.0)*(x1-x0) + RATIO*sin((PI/180.0)*ANGLE/2.0)*(y1-y0);
   coordsVal[5] = y1 + RATIO*cos((PI/180.0)*ANGLE/2.0)*(y1-y0) - RATIO*sin((PI/180.0)*ANGLE/2.0)*(x1-x0);

   // Enter V-shape vertices into sequel object.
   for (i=0; i < 6; i++) seq.coords.push_back(coordsVal[i]);

   // Specify both sub-branch segments of the V-shape for recursive production.
   for (i=0; i < 2; i++)
   {
	  coordsVal1[0] = coordsVal[2];
	  coordsVal1[1] = coordsVal[3];
      for (j=2; j < 4; j++)
	  {
	     coordsVal1[j] = coordsVal[4*i+j-2];
	  }
	  s = *new Source(coordsVal1);
	  seq.v.push_back(s);
   }

   return seq;
}

// Routine to draw leaf called by the following routine to produce tree.
void drawLeaf(float x, float y)
{
   glColor3f(0.0, 1.0, 0.0);
   glPushMatrix();
   glTranslatef(x, y, 0.0);
   glRotatef(rand()%360, 0.0, 0.0, 1.0);
   glBegin(GL_QUADS);
      glVertex2f(0.0, 0.0);
      glVertex2f(1.0, 2.0);
	  glVertex2f(0.0, 4.0);
	  glVertex2f(-1.0, 2.0);
   glEnd();
   glPopMatrix();
}

// Recursive routine to produce tree.
void Source::produceTree(int level)
{
   glColor3f(0.4, 0.5, 0.5);

   // Branches are thinner up the tree.
   glLineWidth(2*(maxLevel - level));

   // Source and sequels at all prior levels are drawn (different from Kock and Koch variant).
   if (maxLevel == 0) this->draw();
   else if (maxLevel == 1) {this->draw(); this->sourceToSequelTree().drawTree();}
        else if (level < maxLevel)
		{
		   if (level == 0) this->draw();
		   this->sourceToSequelTree().drawTree(); 
	       for (int i=0; i < 2; i++) this->sourceToSequelTree().v[i].produceTree(level+1);
		} 

   // Embellish with leaves.
   if (level == maxLevel-1) 
   {
      drawLeaf(this->sourceToSequelTree().coords[0], this->sourceToSequelTree().coords[1]);
	  drawLeaf(this->sourceToSequelTree().coords[4], this->sourceToSequelTree().coords[5]);
   }
   
   // Restore line width.
   glLineWidth(1.0);
}


// Drawing routine.
void drawScene(void)
{  
   glClear(GL_COLOR_BUFFER_BIT);
   glColor3f(0.0, 0.0, 0.0);

   float coordsVal1[4] = {-30.0, -15.0, 0.0, -15.0 + ROOT3*0.5*60.0}; 
   float coordsVal2[4] = {0.0, -15.0 + ROOT3*0.5*60.0, 30.0, -15.0};
   float coordsVal3[4] = {30.0, -15.0, -30.0, -15.0}; 
   float coordsVal4[4] = {0.0, -30.0, 0.0, -15.0};

   Source src1 = *new Source(coordsVal1); // Edge of an equilateral triangle.
   Source src2 = *new Source(coordsVal2); // Edge of an equilateral triangle.
   Source src3 = *new Source(coordsVal3); // Edge of an equilateral triangle.
   Source src4 = *new Source(coordsVal4); // Vertical line segment.

   writeData();

   if (shape == KOCH)
   // Produce on all three edges of an equilateral triangle.
   {
      src1.produceKoch(0);
      src2.produceKoch(0);
      src3.produceKoch(0);
   }

   if (shape == KOCHVARIANT)
   // Produce on all three edges of an equilateral triangle.
   {
      src1.produceKochVariant(0);
      src2.produceKochVariant(0);
      src3.produceKochVariant(0);
   }

   if (shape == TREE) 
   // Produce on a vertical line segment.	   
   src4.produceTree(0);

   glFlush();
}

// Initialization routine.
void setup(void) 
{
   glClearColor(1.0, 1.0, 1.0, 0.0);
}

// OpenGL window reshape routine.
void resize(int w, int h)
{
   glViewport(0, 0, (GLsizei)w, (GLsizei)h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, 1.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

// Keyboard input processing routine.
void keyInput(unsigned char key, int x, int y)
{
   switch(key) 
   {
      case 27:
         exit(0);
         break;
      default:
         break;
   }
}

// Callback routine for non-ASCII key entry.
void specialKeyInput(int key, int x, int y)
{
   if (key == GLUT_KEY_UP) maxLevel++;
   if( key == GLUT_KEY_DOWN) if (maxLevel > 0) maxLevel--;
   if (key == GLUT_KEY_RIGHT) if (shape < 2) shape++; else shape = 0;
   if (key == GLUT_KEY_LEFT) if (shape > 0) shape--; else shape = 2;
   glutPostRedisplay();
}

// Routine to output interaction instructions to the C++ window.
void printInteraction(void)
{
   cout << "Interaction:" << endl;
   cout << "Press left/right arrows keys to cycle through the fractals." << endl
        << "Press up/down arrow keys to increase/decrease the recursion level." << endl; 
}

// Main routine.
int main(int argc, char **argv) 
{
   printInteraction();
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); 
   glutInitWindowSize(500, 500);
   glutInitWindowPosition(100, 100); 
   glutCreateWindow("fractals.cpp");
   setup(); 
   glutDisplayFunc(drawScene); 
   glutReshapeFunc(resize);  
   glutKeyboardFunc(keyInput);
   glutSpecialFunc(specialKeyInput);
   glutMainLoop(); 

   return 0;  
}

# data[].attributes.attachment_link.readable_text
#ifndef CAMERA_H
#define CAMERA_H

#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <vector>

// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {
	FORWARD,
	BACKWARD,
	LEFT,
	RIGHT,
	UP,
	DOWN
};

// Default camera values
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
//const float SENSITIVITY = 0.1f;
const float SENSITIVITY = 0.001f;
const float ZOOM = 45.0f;


// An abstract camera class that processes input and calculates the corresponding Euler Angles, Vectors and Matrices for use in OpenGL
class Camera
{
public:
	// Camera Attributes
	glm::vec3 Position;
	glm::vec3 Front;
	glm::vec3 Up;
	glm::vec3 Right;
	glm::vec3 WorldUp;
	// Euler Angles
	float Yaw;
	float Pitch;
	// Camera options
	float MovementSpeed;
	float MouseSensitivity;
	float Zoom;

	// Constructor with vectors
	Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
	{
		Position = position;
		WorldUp = up;
		Yaw = yaw;
		Pitch = pitch;
		updateCameraVectors();
	}
	// Constructor with scalar values
	Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
	{
		Position = glm::vec3(posX, posY, posZ);
		WorldUp = glm::vec3(upX, upY, upZ);
		Yaw = yaw;
		Pitch = pitch;
		updateCameraVectors();
	}

	// Returns the view matrix calculated using Euler Angles and the LookAt Matrix
	glm::mat4 GetViewMatrix()
	{
		return glm::lookAt(Position, Position + Front, Up);
	}

	// Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
	void ProcessKeyboard(Camera_Movement direction, float deltaTime)
	{
		float velocity = MovementSpeed * deltaTime;
		if (direction == FORWARD)
			Position += Front * velocity;
		if (direction == BACKWARD)
			Position -= Front * velocity;
		if (direction == LEFT)
			Position -= Right * velocity;
		if (direction == RIGHT)
			Position += Right * velocity;
		if (direction == UP)
			Position -= Up * velocity;
		if (direction == DOWN)
			Position += Up * velocity;
	}

	// Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
	void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
	{
		xoffset *= MouseSensitivity;
		yoffset *= MouseSensitivity;

		Yaw += xoffset;
		Pitch += yoffset;

		// Make sure that when pitch is out of bounds, screen doesn't get flipped
		if (constrainPitch)
		{
			if (Pitch > 89.0f)
				Pitch = 89.0f;
			if (Pitch < -89.0f)
				Pitch = -89.0f;
		}

		// Update Front, Right and Up Vectors using the updated Euler angles
		updateCameraVectors();
	}

	// Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
	void ProcessMouseScroll(float yoffset)
	{
		if (Zoom >= 1.0f && Zoom <= 45.0f)
			Zoom -= yoffset;
		if (Zoom <= 1.0f)
			Zoom = 1.0f;
		if (Zoom >= 45.0f)
			Zoom = 45.0f;
	}

private:
	// Calculates the front vector from the Camera's (updated) Euler Angles
	void updateCameraVectors()
	{
		// Calculate the new Front vector
		glm::vec3 front;
		front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
		front.y = sin(glm::radians(Pitch));
		front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
		Front = glm::normalize(front);
		// Also re-calculate the Right and Up vector
		Right = glm::normalize(glm::cross(Front, WorldUp));  // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
		Up = glm::normalize(glm::cross(Right, Front));
	}
};
#endif

# data[].attributes.attachment_link.readable_text
#include <iostream>
#include <cmath>
#include <string>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL/SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"
#include "Camera1.h"

using namespace std;


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void do_movement();
void move_cube();

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



// Camera
Camera  camera(glm::vec3(0.0f, 0.0f, 3.0f));
GLfloat lastX  =  WIDTH  / 2.0;
GLfloat lastY  =  HEIGHT / 2.0;
bool    keys[1024];

// Light attributes
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

// Deltatime
GLfloat deltaTime = 0.0f;	// Time between current frame and last frame
GLfloat lastFrame = 0.0f;  	// Time of last frame

// The MAIN function, from here we start the application and run the game loop
int main()
{
    
    float r = 0.0;
    float g = 0.0;
    float b = 0.0;
    cout << "R:"<<endl;
    cin >> r;
    cout << "G:"<<endl;
    cin >> g;
    cout<<"B:"<<endl;
    cin >> b;


    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Lighting and Colors", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // GLFW Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // OpenGL options
    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader lampShader("lamp.vs", "lamp1.frag");
    Shader lightingShader("lighting.vs", "lighting.frag");
    

    // Set up vertex data (and buffer(s)) and attribute pointers
 
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,

        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,

         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,

        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f
    };
    
 
    // First, set the container's VAO (and VBO)
    GLuint VBO, containerVAO;
    glGenVertexArrays(1, &containerVAO);
    glGenBuffers(1, &VBO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindVertexArray(containerVAO);
    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    
    // Then, we set the light's VAO (VBO stays the same. After all, the vertices are the same for the light object (also a 3D cube))
    GLuint lightVAO;
    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);
    // We only need to bind to the VBO (to link it with glVertexAttribPointer), no need to fill it; the VBO's data already contains all we need.
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // Set the vertex attributes (only position data for the lamp))
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    

    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Calculate deltatime of current frame
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();
        do_movement();

        // Clear the colorbuffer
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        //glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Use cooresponding shader when setting uniforms/drawing objects
        lightingShader.Use();
        GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor");
        GLint lightColorLoc  = glGetUniformLocation(lightingShader.Program, "lightColor");
        
        GLfloat red = r;
        GLfloat green = g;
        GLfloat blue = b;
        glUniform3f(objectColorLoc, red, green, blue);
        glUniform3f(lightColorLoc,  1.0f, 1.0f, 1.0f);

        // Create camera transformations
        glm::mat4 view = glm::mat4(1.0f);
        view = camera.GetViewMatrix();
        glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");
        GLint viewLoc  = glGetUniformLocation(lightingShader.Program,  "view");
        GLint projLoc  = glGetUniformLocation(lightingShader.Program,  "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // Draw the container (using container's vertex attributes)
        
        glm::mat4 model = glm::mat4(1.0f);
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
       
        glBindVertexArray(containerVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        // glBindVertexArray(0);

        // Also draw the lamp object, again binding the appropriate shader
        lampShader.Use();
        // Get location objects for the matrices on the lamp shader (these could be different on a different shader)
        modelLoc = glGetUniformLocation(lampShader.Program, "model");
        viewLoc  = glGetUniformLocation(lampShader.Program, "view");
        projLoc  = glGetUniformLocation(lampShader.Program, "projection");
        // Set matrices
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
        // model = glm::mat4(1.0f);
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        // Draw the light object (using light's vertex attributes)
        glBindVertexArray(lightVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }

    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

void do_movement()
{
    // Camera controls
    if (keys[GLFW_KEY_W])
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (keys[GLFW_KEY_S])
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (keys[GLFW_KEY_A])
        camera.ProcessKeyboard(LEFT, deltaTime);
    //if (keys[GLFW_KEY_D])
        //camera.ProcessKeyboard(RIGHT, deltaTime);
    if (keys[GLFW_KEY_E])
        exit(0);
    if (keys[GLFW_KEY_U])
        camera.ProcessKeyboard(UP, deltaTime);
    if (keys[GLFW_KEY_L])
        camera.ProcessKeyboard(DOWN, deltaTime);
}


bool firstMouse = true;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;  // Reversed since y-coordinates go from bottom to left

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);

}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}

# data[].attributes.attachment_link.readable_text
#include <iostream>
#include <cmath>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL/SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"
#include "Camera.h"


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void do_movement();

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

// Camera
Camera  camera(glm::vec3(0.0f, 0.0f, 3.0f));
GLfloat lastX  =  WIDTH  / 2.0;
GLfloat lastY  =  HEIGHT / 2.0;
bool    keys[1024];

// Light attributes
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

// Deltatime
GLfloat deltaTime = 0.0f;	// Time between current frame and last frame
GLfloat lastFrame = 0.0f;  	// Time of last frame

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Lighting and Colors", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // GLFW Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // OpenGL options
    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader lampShader("lamp.vs", "lamp1.fs");
    Shader lightingShader("lighting.vs", "lighting.frag");
    

    // Set up vertex data (and buffer(s)) and attribute pointers
 
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,

        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,

         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,

        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f
    };
    
 
    // First, set the container's VAO (and VBO)
    GLuint VBO, containerVAO;
    glGenVertexArrays(1, &containerVAO);
    glGenBuffers(1, &VBO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindVertexArray(containerVAO);
    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    
    // Then, we set the light's VAO (VBO stays the same. After all, the vertices are the same for the light object (also a 3D cube))
    GLuint lightVAO;
    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);
    // We only need to bind to the VBO (to link it with glVertexAttribPointer), no need to fill it; the VBO's data already contains all we need.
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // Set the vertex attributes (only position data for the lamp))
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    

    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Calculate deltatime of current frame
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();
        do_movement();

        // Clear the colorbuffer
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        //glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Use cooresponding shader when setting uniforms/drawing objects
        lightingShader.Use();
        GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor");
        GLint lightColorLoc  = glGetUniformLocation(lightingShader.Program, "lightColor");
        glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);
        glUniform3f(lightColorLoc,  1.0f, 1.0f, 1.0f);

        // Create camera transformations
        glm::mat4 view = glm::mat4(1.0f);
        view = camera.GetViewMatrix();
        glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");
        GLint viewLoc  = glGetUniformLocation(lightingShader.Program,  "view");
        GLint projLoc  = glGetUniformLocation(lightingShader.Program,  "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // Draw the container (using container's vertex attributes)
        glBindVertexArray(containerVAO);
        glm::mat4 model = glm::mat4(1.0f);
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // Also draw the lamp object, again binding the appropriate shader
        lampShader.Use();
        // Get location objects for the matrices on the lamp shader (these could be different on a different shader)
        modelLoc = glGetUniformLocation(lampShader.Program, "model");
        viewLoc  = glGetUniformLocation(lampShader.Program, "view");
        projLoc  = glGetUniformLocation(lampShader.Program, "projection");
        // Set matrices
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
        model = glm::mat4();
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        // Draw the light object (using light's vertex attributes)
        glBindVertexArray(lightVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }

    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

void do_movement()
{
    /*
    // Camera controls
    if (keys[GLFW_KEY_W])
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (keys[GLFW_KEY_S])
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (keys[GLFW_KEY_A])
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (keys[GLFW_KEY_D])
        camera.ProcessKeyboard(RIGHT, deltaTime);
    if (keys[GLFW_KEY_E])
        exit(0);
*/
    // Camera controls
    if (keys[GLFW_KEY_F])
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (keys[GLFW_KEY_B])
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (keys[GLFW_KEY_R])
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (keys[GLFW_KEY_L])
        camera.ProcessKeyboard(RIGHT, deltaTime);
    if (keys[GLFW_KEY_D])
        camera.ProcessKeyboard(UP, deltaTime);
    if (keys[GLFW_KEY_U])
        camera.ProcessKeyboard(DOWN, deltaTime);
    if (keys[GLFW_KEY_E])
        exit(0);
    
    
    
}

bool firstMouse = true;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;  // Reversed since y-coordinates go from bottom to left

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);

}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}

# data[].attributes.attachment_link.readable_text
// Justin Canode
// 10/14/2020
// In class activitys 

// This program implements the updated code from the inclass activitys, namely, introduction of movement of objects along the z axix by use of i/k keys
// and prompting user for cube color which is entered as RGB values. 


#include <iostream>
#include <cmath>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL/SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"
#include "Camera.h"

using namespace std;
// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void do_movement();

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

// Camera
Camera  camera(glm::vec3(0.0f, 0.0f, 3.0f));
GLfloat lastX  =  WIDTH  / 2.0;
GLfloat lastY  =  HEIGHT / 2.0;
bool    keys[1024];

// Light attributes
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

// Deltatime
GLfloat deltaTime = 0.0f;	// Time between current frame and last frame
GLfloat lastFrame = 0.0f;  	// Time of last frame

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "Lighting and Colors", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // GLFW Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // OpenGL options
    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader lampShader("lamp.vs", "lamp1.frag");
    Shader lightingShader("lighting.vs", "lighting.frag");
    

    // Set up vertex data (and buffer(s)) and attribute pointers
 
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,

        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,

         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,

        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f, -0.5f,

        -0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f
    };
    
    // prompt user for first color
    float cube_r = 0.0;
    float cube_g = 0.0;
    float cube_b = 0.0;

    cout << "enter red value for color "  << endl;
    cin >> cube_r;
    cout << "enter green value for color "  << endl;
    cin >> cube_g;
    cout << "enter blue value for color "  << endl;
    cin >> cube_b;
    //normalize values
    cube_r = cube_r / 255;
    cube_g = cube_g /255;
    cube_b = cube_b / 255;
    // convert to glfloat for shader input
    GLfloat cube_r_color = cube_r;    
    GLfloat cube_g_color = cube_g; 
    GLfloat cube_b_color = cube_b;

    // First, set the container's VAO (and VBO)
    GLuint VBO, containerVAO;
    glGenVertexArrays(1, &containerVAO);
    glGenBuffers(1, &VBO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindVertexArray(containerVAO);
    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    
    // Then, we set the light's VAO (VBO stays the same. After all, the vertices are the same for the light object (also a 3D cube))
    GLuint lightVAO;
    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);
    // We only need to bind to the VBO (to link it with glVertexAttribPointer), no need to fill it; the VBO's data already contains all we need.
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // Set the vertex attributes (only position data for the lamp))
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    

    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Calculate deltatime of current frame
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();
        do_movement();

        // Clear the colorbuffer
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        //glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Use cooresponding shader when setting uniforms/drawing objects
        lightingShader.Use();
        GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor");
        GLint lightColorLoc  = glGetUniformLocation(lightingShader.Program, "lightColor");
        glUniform3f(objectColorLoc, cube_r_color, cube_g_color, cube_b_color); //passes color into shader
        glUniform3f(lightColorLoc,  1.0f, 1.0f, 1.0f);

        // Create camera transformations
        glm::mat4 view = glm::mat4(1.0f);
        view = camera.GetViewMatrix();
        glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");
        GLint viewLoc  = glGetUniformLocation(lightingShader.Program,  "view");
        GLint projLoc  = glGetUniformLocation(lightingShader.Program,  "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        // Draw the container (using container's vertex attributes)
        glBindVertexArray(containerVAO);
        glm::mat4 model = glm::mat4(1.0f);
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // Also draw the lamp object, again binding the appropriate shader
        lampShader.Use();
        // Get location objects for the matrices on the lamp shader (these could be different on a different shader)
        modelLoc = glGetUniformLocation(lampShader.Program, "model");
        viewLoc  = glGetUniformLocation(lampShader.Program, "view");
        projLoc  = glGetUniformLocation(lampShader.Program, "projection");
        // Set matrices
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
        model = glm::mat4();
        model = glm::translate(model, lightPos);
        model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
        // Draw the light object (using light's vertex attributes)
        glBindVertexArray(lightVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }

    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

void do_movement()
{
    // Camera controls
    if (keys[GLFW_KEY_W])
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if (keys[GLFW_KEY_S])
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if (keys[GLFW_KEY_A])
        camera.ProcessKeyboard(LEFT, deltaTime);
    if (keys[GLFW_KEY_D])
        camera.ProcessKeyboard(RIGHT, deltaTime);
    if (keys[GLFW_KEY_I]) //added I and K keys for z axis
        camera.ProcessKeyboard(UP, deltaTime);
    if (keys[GLFW_KEY_K])
        camera.ProcessKeyboard(DOWN,deltaTime);
    if (keys[GLFW_KEY_E])
        exit(0);
}

bool firstMouse = true;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;  // Reversed since y-coordinates go from bottom to left

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);

}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}

# data[].attributes.attachment_link.readable_text
#pragma once
// Std. Includes
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
// GL Includes
#include <GL/glew.h> // Contains all the necessery OpenGL includes
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <SOIL/SOIL.h>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

#include "Mesh.h"

GLint TextureFromFile(const char* path, string directory);

class Model 
{
public:
	/*  Functions   */
	// Constructor, expects a filepath to a 3D model.
	Model(GLchar* path)
	{
		this->loadModel(path);
	}
	
	// Draws the model, and thus all its meshes
	void Draw(Shader shader)
	{
		for(GLuint i = 0; i < this->meshes.size(); i++)
			this->meshes[i].Draw(shader);
	}
	
private:
	/*  Model Data  */
	vector<Mesh> meshes;
	string directory;
	
	/*  Functions   */
	// Loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector.
	void loadModel(string path)
	{
		// Read file via ASSIMP
		Assimp::Importer importer;
		const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
		// Check for errors
		if(!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
		{
			cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;
			return;
		}
		// Retrieve the directory path of the filepath
		this->directory = path.substr(0, path.find_last_of('/'));
		
		// Process ASSIMP's root node recursively
		this->processNode(scene->mRootNode, scene);
	}
	
	// Processes a node in a recursive fashion. Processes each individual mesh located at the node and repeats this process on its children nodes (if any).
	void processNode(aiNode* node, const aiScene* scene)
	{
		// Process each mesh located at the current node
		for(GLuint i = 0; i < node->mNumMeshes; i++)
		{
			// The node object only contains indices to index the actual objects in the scene. 
			// The scene contains all the data, node is just to keep stuff organized (like relations between nodes).
			aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; 
			this->meshes.push_back(this->processMesh(mesh, scene));			
		}
		// After we've processed all of the meshes (if any) we then recursively process each of the children nodes
		for(GLuint i = 0; i < node->mNumChildren; i++)
		{
			this->processNode(node->mChildren[i], scene);
		}
		
	}
	
	Mesh processMesh(aiMesh* mesh, const aiScene* scene)
	{
		// Data to fill
		vector<Vertex> vertices;
		vector<GLuint> indices;
		vector<Texture> textures;
		
		// Walk through each of the mesh's vertices
		for(GLuint i = 0; i < mesh->mNumVertices; i++)
		{
			Vertex vertex;
			glm::vec3 vector; // We declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.
			// Positions
			vector.x = mesh->mVertices[i].x;
			vector.y = mesh->mVertices[i].y;
			vector.z = mesh->mVertices[i].z;
			vertex.Position = vector;
			// Normals
			vector.x = mesh->mNormals[i].x;
			vector.y = mesh->mNormals[i].y;
			vector.z = mesh->mNormals[i].z;
			vertex.Normal = vector;
			// Texture Coordinates
			if(mesh->mTextureCoords[0]) // Does the mesh contain texture coordinates?
			{
				glm::vec2 vec;
				// A vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't 
				// use models where a vertex can have multiple texture coordinates so we always take the first set (0).
				vec.x = mesh->mTextureCoords[0][i].x; 
				vec.y = mesh->mTextureCoords[0][i].y;
				vertex.TexCoords = vec;
			}
			else
				vertex.TexCoords = glm::vec2(0.0f, 0.0f);
			vertices.push_back(vertex);
		}
		// Now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
		for(GLuint i = 0; i < mesh->mNumFaces; i++)
		{
			aiFace face = mesh->mFaces[i];
			// Retrieve all indices of the face and store them in the indices vector
			for(GLuint j = 0; j < face.mNumIndices; j++)
				indices.push_back(face.mIndices[j]);
		}
		// Process materials
		if(mesh->mMaterialIndex >= 0)
		{
			aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
			// We assume a convention for sampler names in the shaders. Each diffuse texture should be named
			// as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER. 
			// Same applies to other texture as the following list summarizes:
			// Diffuse: texture_diffuseN
			// Specular: texture_specularN
			// Normal: texture_normalN
			
			// 1. Diffuse maps
			vector<Texture> diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
			textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
			// 2. Specular maps
			vector<Texture> specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
			textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
		}
		
		// Return a mesh object created from the extracted mesh data
		return Mesh(vertices, indices, textures);
	}
	
	// Checks all material textures of a given type and loads the textures if they're not loaded yet.
	// The required info is returned as a Texture struct.
	vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName)
	{
		vector<Texture> textures;
		for(GLuint i = 0; i < mat->GetTextureCount(type); i++)
		{
			aiString str;
			mat->GetTexture(type, i, &str);
			// Check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
			Texture texture;
			texture.id = TextureFromFile(str.C_Str(), this->directory);
			texture.type = typeName;
			texture.path = str;
			textures.push_back(texture);
		}
		return textures;
	}
};



GLint TextureFromFile(const char* path, string directory)
{
	//Generate texture ID and load texture data 
	string filename = string(path);
	filename = directory + '/' + filename;
	GLuint textureID;
	glGenTextures(1, &textureID);
	int width,height;
	unsigned char* image = SOIL_load_image(filename.c_str(), &width, &height, 0, SOIL_LOAD_RGB);
	// Assign texture to ID
	glBindTexture(GL_TEXTURE_2D, textureID);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
	glGenerateMipmap(GL_TEXTURE_2D);	
	
	// Parameters
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glBindTexture(GL_TEXTURE_2D, 0);
	SOIL_free_image_data(image);
	return textureID;
}

# data[].attributes.attachment_link.readable_text
// Std. Includes
#include <string>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// GL includes
#include "Shader.h"
#include "Camera.h"
#include "Model.h"

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

// Other Libs
#include <SOIL.h>

// Properties
GLuint screenWidth = 800, screenHeight = 600;

// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void Do_Movement();

// Camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
bool keys[1024];
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;

GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;

// The MAIN function, from here we start our application and run our Game loop
int main()
{
    // Init GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Initialize GLEW to setup the OpenGL Function pointers
    glewExperimental = GL_TRUE;
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, screenWidth, screenHeight);

    // Setup some OpenGL options
    glEnable(GL_DEPTH_TEST);

    // Setup and compile our shaders
    Shader shader("../../../Path/To/Shaders/model_loading.vs", "../../../Path/To/Shaders/model_loading.frag");

    // Load models
    Model ourModel("../../../Path/To/Nanosuit/nanosuit.obj");
    
    // Draw in wireframe
    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    // Game loop
    while(!glfwWindowShouldClose(window))
    {
        // Set frame time
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // Check and call events
        glfwPollEvents();
        Do_Movement();

        // Clear the colorbuffer
        glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        shader.Use();   // <-- Don't forget this one!
        // Transformation matrices
        glm::mat4 projection = glm::perspective(camera.Zoom, (float)screenWidth/(float)screenHeight, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
        glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));

        // Draw the loaded model
        glm::mat4 model;
        model = glm::translate(model, glm::vec3(0.0f, -1.75f, 0.0f)); // Translate it down a bit so it's at the center of the scene
        model = glm::scale(model, glm::vec3(0.2f, 0.2f, 0.2f));	// It's a bit too big for our scene, so scale it down
        glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));
        ourModel.Draw(shader);

        // Swap the buffers
        glfwSwapBuffers(window);
    }

    glfwTerminate();
    return 0;
}

#pragma region "User input"

// Moves/alters the camera positions based on user input
void Do_Movement()
{
    // Camera controls
    if(keys[GLFW_KEY_W])
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if(keys[GLFW_KEY_S])
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if(keys[GLFW_KEY_A])
        camera.ProcessKeyboard(LEFT, deltaTime);
    if(keys[GLFW_KEY_D])
        camera.ProcessKeyboard(RIGHT, deltaTime);
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);

    if(action == GLFW_PRESS)
        keys[key] = true;
    else if(action == GLFW_RELEASE)
        keys[key] = false;	
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if(firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos; 
    
    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
}	

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}

#pragma endregion

# data[].attributes.attachment_link.readable_text
#pragma once
// Std. Includes
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
// GL Includes
#include <GL/glew.h> // Contains all the necessery OpenGL includes
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <SOIL.h>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

#include "Mesh.h"

GLint TextureFromFile(const char* path, string directory);

class Model 
{
public:
    /*  Functions   */
    // Constructor, expects a filepath to a 3D model.
    Model(GLchar* path)
    {
        this->loadModel(path);
    }

    // Draws the model, and thus all its meshes
    void Draw(Shader shader)
    {
        for(GLuint i = 0; i < this->meshes.size(); i++)
            this->meshes[i].Draw(shader);
    }
    
private:
    /*  Model Data  */
    vector<Mesh> meshes;
    string directory;

    /*  Functions   */
    // Loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector.
    void loadModel(string path)
    {
        // Read file via ASSIMP
        Assimp::Importer importer;
        const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
        // Check for errors
        if(!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
        {
            cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;
            return;
        }
        // Retrieve the directory path of the filepath
        this->directory = path.substr(0, path.find_last_of('/'));

        // Process ASSIMP's root node recursively
        this->processNode(scene->mRootNode, scene);
    }

    // Processes a node in a recursive fashion. Processes each individual mesh located at the node and repeats this process on its children nodes (if any).
    void processNode(aiNode* node, const aiScene* scene)
    {
        // Process each mesh located at the current node
        for(GLuint i = 0; i < node->mNumMeshes; i++)
        {
            // The node object only contains indices to index the actual objects in the scene. 
            // The scene contains all the data, node is just to keep stuff organized (like relations between nodes).
            aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; 
            this->meshes.push_back(this->processMesh(mesh, scene));			
        }
        // After we've processed all of the meshes (if any) we then recursively process each of the children nodes
        for(GLuint i = 0; i < node->mNumChildren; i++)
        {
            this->processNode(node->mChildren[i], scene);
        }

    }

    Mesh processMesh(aiMesh* mesh, const aiScene* scene)
    {
        // Data to fill
        vector<Vertex> vertices;
        vector<GLuint> indices;
        vector<Texture> textures;

        // Walk through each of the mesh's vertices
        for(GLuint i = 0; i < mesh->mNumVertices; i++)
        {
            Vertex vertex;
            glm::vec3 vector; // We declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.
            // Positions
            vector.x = mesh->mVertices[i].x;
            vector.y = mesh->mVertices[i].y;
            vector.z = mesh->mVertices[i].z;
            vertex.Position = vector;
            // Normals
            vector.x = mesh->mNormals[i].x;
            vector.y = mesh->mNormals[i].y;
            vector.z = mesh->mNormals[i].z;
            vertex.Normal = vector;
            // Texture Coordinates
            if(mesh->mTextureCoords[0]) // Does the mesh contain texture coordinates?
            {
                glm::vec2 vec;
                // A vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't 
                // use models where a vertex can have multiple texture coordinates so we always take the first set (0).
                vec.x = mesh->mTextureCoords[0][i].x; 
                vec.y = mesh->mTextureCoords[0][i].y;
                vertex.TexCoords = vec;
            }
            else
                vertex.TexCoords = glm::vec2(0.0f, 0.0f);
            vertices.push_back(vertex);
        }
        // Now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
        for(GLuint i = 0; i < mesh->mNumFaces; i++)
        {
            aiFace face = mesh->mFaces[i];
            // Retrieve all indices of the face and store them in the indices vector
            for(GLuint j = 0; j < face.mNumIndices; j++)
                indices.push_back(face.mIndices[j]);
        }
        // Process materials
        if(mesh->mMaterialIndex >= 0)
        {
            aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
            // We assume a convention for sampler names in the shaders. Each diffuse texture should be named
            // as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER. 
            // Same applies to other texture as the following list summarizes:
            // Diffuse: texture_diffuseN
            // Specular: texture_specularN
            // Normal: texture_normalN

            // 1. Diffuse maps
            vector<Texture> diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
            textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
            // 2. Specular maps
            vector<Texture> specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
            textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
        }
        
        // Return a mesh object created from the extracted mesh data
        return Mesh(vertices, indices, textures);
    }

    // Checks all material textures of a given type and loads the textures if they're not loaded yet.
    // The required info is returned as a Texture struct.
    vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName)
    {
        vector<Texture> textures;
        for(GLuint i = 0; i < mat->GetTextureCount(type); i++)
        {
            aiString str;
            mat->GetTexture(type, i, &str);
            // Check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
            Texture texture;
            texture.id = TextureFromFile(str.C_Str(), this->directory);
            texture.type = typeName;
            texture.path = str;
            textures.push_back(texture);
        }
        return textures;
    }
};



GLint TextureFromFile(const char* path, string directory)
{
     //Generate texture ID and load texture data 
    string filename = string(path);
    filename = directory + '/' + filename;
    GLuint textureID;
    glGenTextures(1, &textureID);
    int width,height;
    unsigned char* image = SOIL_load_image(filename.c_str(), &width, &height, 0, SOIL_LOAD_RGB);
    // Assign texture to ID
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);	

    // Parameters
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);
    SOIL_free_image_data(image);
    return textureID;
}

# data[].attributes.attachment_link.readable_text
#pragma once
// Std. Includes
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
using namespace std;
// GL Includes
#include <GL/glew.h> // Contains all the necessery OpenGL includes
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>


struct Vertex {
    // Position
    glm::vec3 Position;
    // Normal
    glm::vec3 Normal;
    // TexCoords
    glm::vec2 TexCoords;
};

struct Texture {
    GLuint id;
    string type;
    aiString path;
};

class Mesh {
public:
    /*  Mesh Data  */
    vector<Vertex> vertices;
    vector<GLuint> indices;
    vector<Texture> textures;

    /*  Functions  */
    // Constructor
    Mesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures)
    {
        this->vertices = vertices;
        this->indices = indices;
        this->textures = textures;

        // Now that we have all the required data, set the vertex buffers and its attribute pointers.
        this->setupMesh();
    }

    // Render the mesh
    void Draw(Shader shader) 
    {
        // Bind appropriate textures
        GLuint diffuseNr = 1;
        GLuint specularNr = 1;
        for(GLuint i = 0; i < this->textures.size(); i++)
        {
            glActiveTexture(GL_TEXTURE0 + i); // Active proper texture unit before binding
            // Retrieve texture number (the N in diffuse_textureN)
            stringstream ss;
            string number;
            string name = this->textures[i].type;
            if(name == "texture_diffuse")
                ss << diffuseNr++; // Transfer GLuint to stream
            else if(name == "texture_specular")
                ss << specularNr++; // Transfer GLuint to stream
            number = ss.str(); 
            // Now set the sampler to the correct texture unit
            glUniform1i(glGetUniformLocation(shader.Program, (name + number).c_str()), i);
            // And finally bind the texture
            glBindTexture(GL_TEXTURE_2D, this->textures[i].id);
        }
        
        // Also set each mesh's shininess property to a default value (if you want you could extend this to another mesh property and possibly change this value)
        glUniform1f(glGetUniformLocation(shader.Program, "material.shininess"), 16.0f);

        // Draw mesh
        glBindVertexArray(this->VAO);
        glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);

        // Always good practice to set everything back to defaults once configured.
        for (GLuint i = 0; i < this->textures.size(); i++)
        {
            glActiveTexture(GL_TEXTURE0 + i);
            glBindTexture(GL_TEXTURE_2D, 0);
        }
    }

private:
    /*  Render data  */
    GLuint VAO, VBO, EBO;

    /*  Functions    */
    // Initializes all the buffer objects/arrays
    void setupMesh()
    {
        // Create buffers/arrays
        glGenVertexArrays(1, &this->VAO);
        glGenBuffers(1, &this->VBO);
        glGenBuffers(1, &this->EBO);

        glBindVertexArray(this->VAO);
        // Load data into vertex buffers
        glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
        // A great thing about structs is that their memory layout is sequential for all its items.
        // The effect is that we can simply pass a pointer to the struct and it translates perfectly to a glm::vec3/2 array which
        // again translates to 3/2 floats which translates to a byte array.
        glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(Vertex), &this->vertices[0], GL_STATIC_DRAW);  

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), &this->indices[0], GL_STATIC_DRAW);

        // Set the vertex attribute pointers
        // Vertex Positions
        glEnableVertexAttribArray(0);	
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0);
        // Vertex Normals
        glEnableVertexAttribArray(1);	
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Normal));
        // Vertex Texture Coords
        glEnableVertexAttribArray(2);	
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, TexCoords));

        glBindVertexArray(0);
    }
};


# data[].attributes.attachment_link.readable_text
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 2) in vec2 texCoord;

out vec2 TexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0f);
    TexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
}

# data[].attributes.attachment_link.readable_text
#include <iostream>
#include <cmath>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void do_movement();

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

// Camera
glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);
bool keys[1024];

// Deltatime
GLfloat deltaTime = 0.0f;	// Time between current frame and last frame
GLfloat lastFrame = 0.0f;  	// Time of last frame

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    glm::vec3 cubePositions[] = {
        glm::vec3( 0.0f,  0.0f,  0.0f),
        glm::vec3( 2.0f,  5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3( 2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f,  3.0f, -7.5f),
        glm::vec3( 1.3f, -2.0f, -2.5f),
        glm::vec3( 1.5f,  2.0f, -2.5f),
        glm::vec3( 1.5f,  0.2f, -1.5f),
        glm::vec3(-1.3f,  1.0f, -1.5f)
    };
    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture 
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Calculate deltatime of current frame
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();
        do_movement();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

        // Activate shader
        ourShader.Use();

        // Camera/View transformation
        glm::mat4 view;
        view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        // Projection 
        glm::mat4 projection;
        projection = glm::perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
        GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        glBindVertexArray(VAO);
        for (GLuint i = 0; i < 10; i++)
        {
            // Calculate the model matrix for each object and pass it to shader before drawing
            glm::mat4 model;
            model = glm::translate(model, cubePositions[i]);
            GLfloat angle = 20.0f * i;
            model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

void do_movement()
{
    // Camera controls
    GLfloat cameraSpeed = 5.0f * deltaTime;
    if (keys[GLFW_KEY_W])
        cameraPos += cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_S])
        cameraPos -= cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_A])
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (keys[GLFW_KEY_D])
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

# data[].attributes.attachment_link.readable_text
#include <iostream>
#include <cmath>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void do_movement();

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

// Camera
glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);
bool keys[1024];

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    glm::vec3 cubePositions[] = {
        glm::vec3( 0.0f,  0.0f,  0.0f),
        glm::vec3( 2.0f,  5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3( 2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f,  3.0f, -7.5f),
        glm::vec3( 1.3f, -2.0f, -2.5f),
        glm::vec3( 1.5f,  2.0f, -2.5f),
        glm::vec3( 1.5f,  0.2f, -1.5f),
        glm::vec3(-1.3f,  1.0f, -1.5f)
    };
    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture 
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();
        do_movement();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

        // Activate shader
        ourShader.Use();

        // Camera/View transformation
        glm::mat4 view;
        view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        // Projection 
        glm::mat4 projection;
        projection = glm::perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
        GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        glBindVertexArray(VAO);
        for (GLuint i = 0; i < 10; i++)
        {
            // Calculate the model matrix for each object and pass it to shader before drawing
            glm::mat4 model;
            model = glm::translate(model, cubePositions[i]);
            GLfloat angle = 20.0f * i;
            model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

void do_movement()
{
    // Camera controls
    GLfloat cameraSpeed = 0.01f;
    if (keys[GLFW_KEY_W])
        cameraPos += cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_S])
        cameraPos -= cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_A])
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (keys[GLFW_KEY_D])
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

# data[].attributes.attachment_link.readable_text
#version 330 core
in vec2 TexCoord;

out vec4 color;

uniform sampler2D ourTexture1;
uniform sampler2D ourTexture2;

void main()
{
    color = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2);
}

# data[].attributes.attachment_link.readable_text
#include <iostream>
#include <cmath>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void do_movement();

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

// Camera
glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);
GLfloat yaw   = -90.0f;	// Yaw is initialized to -90.0 degrees since a yaw of 0.0 results in a direction vector pointing to the right (due to how Eular angles work) so we initially rotate a bit to the left.
GLfloat pitch =   0.0f;
GLfloat lastX =  WIDTH  / 2.0;
GLfloat lastY =  HEIGHT / 2.0;
bool keys[1024];

// Deltatime
GLfloat deltaTime = 0.0f;	// Time between current frame and last frame
GLfloat lastFrame = 0.0f;  	// Time of last frame

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);

    // GLFW Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    glm::vec3 cubePositions[] = {
        glm::vec3( 0.0f,  0.0f,  0.0f),
        glm::vec3( 2.0f,  5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3( 2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f,  3.0f, -7.5f),
        glm::vec3( 1.3f, -2.0f, -2.5f),
        glm::vec3( 1.5f,  2.0f, -2.5f),
        glm::vec3( 1.5f,  0.2f, -1.5f),
        glm::vec3(-1.3f,  1.0f, -1.5f)
    };
    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture 
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Calculate deltatime of current frame
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();
        do_movement();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

        // Activate shader
        ourShader.Use();

        // Camera/View transformation
        glm::mat4 view;
        view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        // Projection 
        glm::mat4 projection;
        projection = glm::perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
        GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        glBindVertexArray(VAO);
        for (GLuint i = 0; i < 10; i++)
        {
            // Calculate the model matrix for each object and pass it to shader before drawing
            glm::mat4 model;
            model = glm::translate(model, cubePositions[i]);
            GLfloat angle = 20.0f * i;
            model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

void do_movement()
{
    // Camera controls
    GLfloat cameraSpeed = 5.0f * deltaTime;
    if (keys[GLFW_KEY_W])
        cameraPos += cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_S])
        cameraPos -= cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_A])
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (keys[GLFW_KEY_D])
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

bool firstMouse = true;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos; // Reversed since y-coordinates go from bottom to left
    lastX = xpos;
    lastY = ypos;

    GLfloat sensitivity = 0.05;	// Change this value to your liking
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw   += xoffset;
    pitch += yoffset;

    // Make sure that when pitch is out of bounds, screen doesn't get flipped
    if (pitch > 89.0f)
        pitch = 89.0f;
    if (pitch < -89.0f)
        pitch = -89.0f;

    glm::vec3 front;
    front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    front.y = sin(glm::radians(pitch));
    front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    cameraFront = glm::normalize(front);
}

# data[].attributes.attachment_link.readable_text
// Std. Includes
#include <string>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// GL includes
#include "Shader.h"
#include "Camera.h"

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

// Other Libs
#include <SOIL.h>

// Properties
GLuint screenWidth = 800, screenHeight = 600;

// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void Do_Movement();

// Camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
bool keys[1024];
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;

GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;

// The MAIN function, from here we start our application and run our Game loop
int main()
{
    // Init GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
    glfwWindowHint(GLFW_SAMPLES, 4);

    GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Initialize GLEW to setup the OpenGL Function pointers
    glewExperimental = GL_TRUE;
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, screenWidth, screenHeight);

    // Setup some OpenGL options
    glEnable(GL_DEPTH_TEST);

    // Setup and compile our shaders
    Shader ourShader("../../../Path/To/Shaders/transform.vs", "../../../Path/To/Shaders/transform.frag");

    // Set up our vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    glm::vec3 cubePositions[] = {
        glm::vec3(0.0f, 0.0f, 0.0f), 
        glm::vec3(2.0f, 5.0f, -15.0f), 
        glm::vec3(-1.5f, -2.2f, -2.5f),  
        glm::vec3(-3.8f, -2.0f, -12.3f),  
        glm::vec3(2.4f, -0.4f, -3.5f),  
        glm::vec3(-1.7f, 3.0f, -7.5f),  
        glm::vec3(1.3f, -2.0f, -2.5f),  
        glm::vec3(1.5f, 2.0f, -2.5f), 
        glm::vec3(1.5f, 0.2f, -1.5f), 
        glm::vec3(-1.3f, 1.0f, -1.5f)  
    };

    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    // Bind our Vertex Array Object first, then bind and set our buffers and pointers.
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO

    // Load and create a texture 
    GLuint texture1;
    GLuint texture2;
    // --== TEXTURE 1 == --
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("../../../Path/To/Textures/container.jpg", &width, &height, 0, SOIL_LOAD_RGB); 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // --== TEXTURE 2 == --
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2); 
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("../../../Path/To/Textures/awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB); 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); 
    
    // Game loop
    while(!glfwWindowShouldClose(window))
    {
        // Set frame time
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // Check and call events
        glfwPollEvents();
        Do_Movement();

        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Draw our first triangle
        ourShader.Use();

        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);
        
        // Create camera transformation
        glm::mat4 view;
        view = camera.GetViewMatrix();
        glm::mat4 projection;	
        projection = glm::perspective(camera.Zoom, (float)screenWidth/(float)screenHeight, 0.1f, 1000.0f);
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
        GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
        
        glBindVertexArray(VAO);
        for(GLuint i = 0; i < 10; i++)
        {
            // Calculate the model matrix for each object and pass it to shader before drawing
            glm::mat4 model;
            model = glm::translate(model, cubePositions[i]);
            GLfloat angle = 20.0f * i; 
            model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

            glDrawArrays(GL_TRIANGLES, 0, 36);			
        }
        glBindVertexArray(0);
        // Swap the buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glfwTerminate();
    return 0;
}

// Moves/alters the camera positions based on user input
void Do_Movement()
{
    // Camera controls
    if(keys[GLFW_KEY_W])
        camera.ProcessKeyboard(FORWARD, deltaTime);
    if(keys[GLFW_KEY_S])
        camera.ProcessKeyboard(BACKWARD, deltaTime);
    if(keys[GLFW_KEY_A])
        camera.ProcessKeyboard(LEFT, deltaTime);
    if(keys[GLFW_KEY_D])
        camera.ProcessKeyboard(RIGHT, deltaTime);
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    //cout << key << endl;
    if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
        if(action == GLFW_PRESS)
            keys[key] = true;
        else if(action == GLFW_RELEASE)
            keys[key] = false;	
    }
}

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if(firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;  // Reversed since y-coordinates go from bottom to left
    
    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
}	


void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.ProcessMouseScroll(yoffset);
}

# data[].attributes.attachment_link.readable_text
#include <iostream>
#include <cmath>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void do_movement();

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

// Camera
glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);
GLfloat yaw    = -90.0f;	// Yaw is initialized to -90.0 degrees since a yaw of 0.0 results in a direction vector pointing to the right (due to how Eular angles work) so we initially rotate a bit to the left.
GLfloat pitch  =  0.0f;
GLfloat lastX  =  WIDTH  / 2.0;
GLfloat lastY  =  HEIGHT / 2.0;
GLfloat fov =  45.0f;
bool keys[1024];

// Deltatime
GLfloat deltaTime = 0.0f;	// Time between current frame and last frame
GLfloat lastFrame = 0.0f;  	// Time of last frame

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, mouse_callback);
    glfwSetScrollCallback(window, scroll_callback);

    // GLFW Options
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    glm::vec3 cubePositions[] = {
        glm::vec3( 0.0f,  0.0f,  0.0f),
        glm::vec3( 2.0f,  5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3( 2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f,  3.0f, -7.5f),
        glm::vec3( 1.3f, -2.0f, -2.5f),
        glm::vec3( 1.5f,  2.0f, -2.5f),
        glm::vec3( 1.5f,  0.2f, -1.5f),
        glm::vec3(-1.3f,  1.0f, -1.5f)
    };
    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture 
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Calculate deltatime of current frame
        GLfloat currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();
        do_movement();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

        // Activate shader
        ourShader.Use();

        // Camera/View transformation
        glm::mat4 view;
        view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        // Projection 
        glm::mat4 projection;
        projection = glm::perspective(fov, (GLfloat)WIDTH/(GLfloat)HEIGHT, 0.1f, 100.0f);  
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
        GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        glBindVertexArray(VAO);
        for (GLuint i = 0; i < 10; i++)
        {
            // Calculate the model matrix for each object and pass it to shader before drawing
            glm::mat4 model;
            model = glm::translate(model, cubePositions[i]);
            GLfloat angle = 20.0f * i;
            model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keys[key] = true;
        else if (action == GLFW_RELEASE)
            keys[key] = false;
    }
}

void do_movement()
{
    // Camera controls
    GLfloat cameraSpeed = 5.0f * deltaTime;
    if (keys[GLFW_KEY_W])
        cameraPos += cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_S])
        cameraPos -= cameraSpeed * cameraFront;
    if (keys[GLFW_KEY_A])
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
    if (keys[GLFW_KEY_D])
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}

bool firstMouse = true;
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos; // Reversed since y-coordinates go from bottom to left
    lastX = xpos;
    lastY = ypos;

    GLfloat sensitivity = 0.05;	// Change this value to your liking
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    yaw   += xoffset;
    pitch += yoffset;

    // Make sure that when pitch is out of bounds, screen doesn't get flipped
    if (pitch > 89.0f)
        pitch = 89.0f;
    if (pitch < -89.0f)
        pitch = -89.0f;

    glm::vec3 front;
    front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
    front.y = sin(glm::radians(pitch));
    front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
    cameraFront = glm::normalize(front);
}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    if (fov >= 1.0f && fov <= 45.0f)
        fov -= yoffset;
    if (fov <= 1.0f)
        fov = 1.0f;
    if (fov >= 45.0f)
        fov = 45.0f;
}

# data[].attributes.attachment_link.readable_text
#include <iostream>
#include <cmath>

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>

// GLFW
#include <GLFW/glfw3.h>

// Other Libs
#include <SOIL.h>
// GLM Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

// Other includes
#include "Shader.h"


// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);

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

// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    glm::vec3 cubePositions[] = {
        glm::vec3( 0.0f,  0.0f,  0.0f),
        glm::vec3( 2.0f,  5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3( 2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f,  3.0f, -7.5f),
        glm::vec3( 1.3f, -2.0f, -2.5f),
        glm::vec3( 1.5f,  2.0f, -2.5f),
        glm::vec3( 1.5f,  0.2f, -1.5f),
        glm::vec3(-1.3f,  1.0f, -1.5f)
    };
    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture 
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

        // Activate shader
        ourShader.Use();

        // Camera/View transformation
        glm::mat4 view;
        GLfloat radius = 10.0f;
        GLfloat camX = sin(glfwGetTime()) * radius;
        GLfloat camZ = cos(glfwGetTime()) * radius;
        view = glm::lookAt(glm::vec3(camX, 0.0f, camZ), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
        // Projection 
        glm::mat4 projection;
        projection = glm::perspective(45.0f, (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
        // Get the uniform locations
        GLint modelLoc = glGetUniformLocation(ourShader.Program, "model");
        GLint viewLoc = glGetUniformLocation(ourShader.Program, "view");
        GLint projLoc = glGetUniformLocation(ourShader.Program, "projection");
        // Pass the matrices to the shader
        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

        glBindVertexArray(VAO);
        for (GLuint i = 0; i < 10; i++)
        {
            // Calculate the model matrix for each object and pass it to shader before drawing
            glm::mat4 model;
            model = glm::translate(model, cubePositions[i]);
            GLfloat angle = 20.0f * i;
            model = glm::rotate(model, angle, glm::vec3(1.0f, 0.3f, 0.5f));
            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}

// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

# data[].attributes.attachment_link.readable_text

#include <GL/glut.h>
#include <cmath>


// angle of rotation for the camera direction
float angle=0.0;
// actual vector representing the camera's direction
float lx=0.0f,lz=-1.0f;
// XZ position of the camera
float x=0.0f,z=5.0f;

void drawSnowMan() {

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

// Draw Body
	glTranslatef(0.0f ,0.75f, 0.0f);
	glutSolidSphere(0.75f,20,20);

// Draw Head
	glTranslatef(0.0f, 1.0f, 0.0f);
	glutSolidSphere(0.25f,20,20);

// Draw Eyes
	glPushMatrix();
	glColor3f(0.0f,0.0f,0.0f);
	glTranslatef(0.05f, 0.10f, 0.18f);
	glutSolidSphere(0.05f,10,10);
	glTranslatef(-0.1f, 0.0f, 0.0f);
	glutSolidSphere(0.05f,10,10);
	glPopMatrix();

// Draw Nose
	glColor3f(1.0f, 0.5f , 0.5f);
	glutSolidCone(0.08f,0.5f,10,2);
}

void renderScene(void) {

	// Clear Color and Depth Buffers

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Reset transformations
	glLoadIdentity();
	// Set the camera
	gluLookAt(	x, 1.0f, z,
			x+lx, 1.0f,  z+lz,
			0.0f, 1.0f,  0.0f);

        // Draw ground
	glColor3f(0.9f, 0.9f, 0.9f);
	glBegin(GL_QUADS);
		glVertex3f(-100.0f, 0.0f, -100.0f);
		glVertex3f(-100.0f, 0.0f,  100.0f);
		glVertex3f( 100.0f, 0.0f,  100.0f);
		glVertex3f( 100.0f, 0.0f, -100.0f);
	glEnd();

        // Draw 36 SnowMen
	for(int i = -3; i < 3; i++)
		for(int j=-3; j < 3; j++) {
			glPushMatrix();
			glTranslatef(i*10.0,0,j * 10.0);
			drawSnowMan();
			glPopMatrix();
		}

	glutSwapBuffers();
}

void processSpecialKeys(int key, int xx, int yy) {

	float fraction = 0.1f;

	switch (key) {
		case GLUT_KEY_LEFT :
			angle -= 0.01f;
			lx = sin(angle);
			lz = -cos(angle);
			break;
		case GLUT_KEY_RIGHT :
			angle += 0.01f;
			lx = sin(angle);
			lz = -cos(angle);
			break;
		case GLUT_KEY_UP :
			x += lx * fraction;
			z += lz * fraction;
			break;
		case GLUT_KEY_DOWN :
			x -= lx * fraction;
			z -= lz * fraction;
			break;
	}
}

int main(int argc, char **argv) {

	// init GLUT and create window

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100,100);
	glutInitWindowSize(320,320);
	glutCreateWindow("Lighthouse3D - GLUT Tutorial");

	// register callbacks
	glutDisplayFunc(renderScene);
	glutReshapeFunc(changeSize);
	glutIdleFunc(renderScene);
	glutKeyboardFunc(processNormalKeys);
	glutSpecialFunc(processSpecialKeys);

	// OpenGL init
	glEnable(GL_DEPTH_TEST);

	// enter GLUT event processing cycle
	glutMainLoop();

	return 1;
}



# data[].attributes.attachment_link.readable_text
#pragma once

// Std. Includes
#include <vector>

// GL Includes
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>



// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
enum Camera_Movement {
    FORWARD,
    BACKWARD,
    LEFT,
    RIGHT
};

// Default camera values
const GLfloat YAW        = -90.0f;
const GLfloat PITCH      =  0.0f;
const GLfloat SPEED      =  3.0f;
const GLfloat SENSITIVTY =  0.25f;
const GLfloat ZOOM       =  45.0f;


// An abstract camera class that processes input and calculates the corresponding Eular Angles, Vectors and Matrices for use in OpenGL
class Camera
{
public:
    // Camera Attributes
    glm::vec3 Position;
    glm::vec3 Front;
    glm::vec3 Up;
    glm::vec3 Right;
    glm::vec3 WorldUp;
    // Eular Angles
    GLfloat Yaw;
    GLfloat Pitch;
    // Camera options
    GLfloat MovementSpeed;
    GLfloat MouseSensitivity;
    GLfloat Zoom;

    // Constructor with vectors
    Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), GLfloat yaw = YAW, GLfloat pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM)
    {
        this->Position = position;
        this->WorldUp = up;
        this->Yaw = yaw;
        this->Pitch = pitch;
        this->updateCameraVectors();
    }
    // Constructor with scalar values
    Camera(GLfloat posX, GLfloat posY, GLfloat posZ, GLfloat upX, GLfloat upY, GLfloat upZ, GLfloat yaw, GLfloat pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM)
    {
        this->Position = glm::vec3(posX, posY, posZ);
        this->WorldUp = glm::vec3(upX, upY, upZ);
        this->Yaw = yaw;
        this->Pitch = pitch;
        this->updateCameraVectors();
    }

    // Returns the view matrix calculated using Eular Angles and the LookAt Matrix
    glm::mat4 GetViewMatrix()
    {
        return glm::lookAt(this->Position, this->Position + this->Front, this->Up);
    }

    // Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
    void ProcessKeyboard(Camera_Movement direction, GLfloat deltaTime)
    {
        GLfloat velocity = this->MovementSpeed * deltaTime;
        if (direction == FORWARD)
            this->Position += this->Front * velocity;
        if (direction == BACKWARD)
            this->Position -= this->Front * velocity;
        if (direction == LEFT)
            this->Position -= this->Right * velocity;
        if (direction == RIGHT)
            this->Position += this->Right * velocity;
    }

    // Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
    void ProcessMouseMovement(GLfloat xoffset, GLfloat yoffset, GLboolean constrainPitch = true)
    {
        xoffset *= this->MouseSensitivity;
        yoffset *= this->MouseSensitivity;

        this->Yaw   += xoffset;
        this->Pitch += yoffset;

        // Make sure that when pitch is out of bounds, screen doesn't get flipped
        if (constrainPitch)
        {
            if (this->Pitch > 89.0f)
                this->Pitch = 89.0f;
            if (this->Pitch < -89.0f)
                this->Pitch = -89.0f;
        }

        // Update Front, Right and Up Vectors using the updated Eular angles
        this->updateCameraVectors();
    }

    // Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
    void ProcessMouseScroll(GLfloat yoffset)
    {
        if (this->Zoom >= 1.0f && this->Zoom <= 45.0f)
            this->Zoom -= yoffset;
        if (this->Zoom <= 1.0f)
            this->Zoom = 1.0f;
        if (this->Zoom >= 45.0f)
            this->Zoom = 45.0f;
    }

private:
    // Calculates the front vector from the Camera's (updated) Eular Angles
    void updateCameraVectors()
    {
        // Calculate the new Front vector
        glm::vec3 front;
        front.x = cos(glm::radians(this->Yaw)) * cos(glm::radians(this->Pitch));
        front.y = sin(glm::radians(this->Pitch));
        front.z = sin(glm::radians(this->Yaw)) * cos(glm::radians(this->Pitch));
        this->Front = glm::normalize(front);
        // Also re-calculate the Right and Up vector
        this->Right = glm::normalize(glm::cross(this->Front, this->WorldUp));  // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
        this->Up    = glm::normalize(glm::cross(this->Right, this->Front));
    }
};