<repo_name>SHIHUAMarryMe/opengl_demo<file_sep>/transform/main.cpp

#include <cmath>
#include <iostream>

#include <glad/glad.h> // must include this before glfw(#error OpenGL header already included, remove this include, glad already provides it);
#include <GLFW/glfw3.h>

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

#include "stb_image/stb_image.h"

static constexpr int WIDTH{800};
static constexpr int HEIGHT{600};

static constexpr const char *vertexShaderSource{
    "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "layout (location = 1) in vec2 aTexCoord;\n"
    "out vec2 texCoord;\n"
    "uniform mat4 model;\n"
    "uniform mat4 view;\n"
    "uniform mat4 projection;\n"
    "void main()\n"
    "{\n"
    "	gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
    "	texCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
    "}"};

static constexpr const char *fragmentShaderSource{
    "#version 330 core\n"
    "out vec4 fragmentColor;\n"
    "in vec2 texCoord;\n"
    "uniform sampler2D texture1;\n"
    "uniform sampler2D texture2;\n"
    "void main()\n"
    "{\n"
    "	fragmentColor = mix(texture(texture1, texCoord), texture(texture2, texCoord), 0.2);\n"
    "}"};

static void processInput(GLFWwindow *window)
{
  if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    glfwSetWindowShouldClose(window, true);
}

static void framebuffer_size_callback(GLFWwindow *window, int width,
                                      int height)
{
  // make sure the viewport matches the new window dimensions; note that width
  // and height will be significantly larger than specified on retina displays.
  glViewport(0, 0, width, height);
}

int main()
{
  glfwInit();
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  // glfw window creation
  // --------------------
  GLFWwindow *window =
      glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
  if (window == nullptr)
  {
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();
    return -1;
  }
  glfwMakeContextCurrent(window);
  glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

  // glad: load all OpenGL function pointers
  if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
  {
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
  }

  // build and compile our shader program
  // ------------------------------------
  // vertex shader
  int vertexShader = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
  glCompileShader(vertexShader);

  // check for shader compile errors
  int success{};
  char infoLog[512]{};
  glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
  if (!success)
  {
    glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
    std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"
              << infoLog << std::endl;
  }
  // fragment shader
  int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
  glCompileShader(fragmentShader);
  // check for shader compile errors
  glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
  if (!success)
  {
    glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
    std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n"
              << infoLog << std::endl;
  }

  // link shaders
  int shaderProgram = glCreateProgram();
  glAttachShader(shaderProgram, vertexShader);
  glAttachShader(shaderProgram, fragmentShader);
  glLinkProgram(shaderProgram);

  // check for linking errors
  glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
  if (!success)
  {
    glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n"
              << infoLog << std::endl;
  }

  // after linking program, you can delete shaders.
  glDeleteShader(vertexShader);
  glDeleteShader(fragmentShader);

  const float 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};

  // world space positions of our cubes
  const 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 VAO{}; // vertex array object.
  GLuint VBO{}; // vertex buffer object.

  glGenVertexArrays(1, &VAO);
  glGenBuffers(1, &VBO);

  glBindVertexArray(VAO);

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

  // tell shader how to parse vertices.
  // position attribute.
  GLint offset{};
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float),
                        reinterpret_cast<void *>(offset));
  glEnableVertexAttribArray(0);

  // texture coordinate.
  offset = 3 * sizeof(float);
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float),
                        reinterpret_cast<void *>(offset));
  glEnableVertexAttribArray(1);

  // 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 (default 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
  GLint width{}, height{}, nrChannels{};
  unsigned char *data{stbi_load("/home/shihua/projects/learn_opengl/transform/image/container.jpg", &width, &height, &nrChannels, 0)};

  if (data)
  {
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
  }
  else
  {
    std::cout << "Failed to load texture" << std::endl;
  }
  stbi_image_free(data);
  data = nullptr;

  GLuint texture2{};
  glGenTextures(1, &texture2);
  glBindTexture(GL_TEXTURE_2D, texture2);

  // set the texture wrapping parameters
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default 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);

  // tell stb_image.h to flip loaded texture's on the y-axis.
  stbi_set_flip_vertically_on_load(true);

  // load image, create texture and generate mipmaps
  data = stbi_load("/home/shihua/projects/learn_opengl/transform/image/awesomeface.png", &width, &height, &nrChannels, 0);

  if (data)
  {
    // note that the awesomeface.png has transparency and thus an alpha channel,
    // so make sure to tell OpenGL the data type is of GL_RGBA
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
  }
  else
  {
    std::cout << "Failed to load texture" << std::endl;
  }
  stbi_image_free(data);
  data = nullptr;

  // tell opengl for each sampler2D to which texture unit it belongs to (only
  // has to be done once)
  glUseProgram(shaderProgram);

  GLint texture1Id{glGetUniformLocation(shaderProgram, "texture1")};
  glUniform1i(texture1Id, 0);

  GLint texture2Id{glGetUniformLocation(shaderProgram, "texture2")};
  glUniform1i(texture2Id, 1);

  // set the uniform var projection.
  glm::mat4 projectionMat4{glm::perspective(glm::radians(45.0f),
                                            static_cast<float>(WIDTH / HEIGHT), 0.1f, 100.0f)};
  GLint projectionLocation{glGetUniformLocation(shaderProgram, "projection")};
  glUniformMatrix4fv(projectionLocation, 1, GL_FALSE, &projectionMat4[0][0]);

  // render loop
  while (!glfwWindowShouldClose(window))
  {
    // input
    // -----
    processInput(window);

    // render
    // ------
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear last time depth-testing.

    // bind textures to specify uniform.
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture2);

    // render container
    glUseProgram(shaderProgram);

    // camera/view
    glm::mat4 view{1.0f};
    GLfloat radius{45.0f};
    GLfloat cameraX{static_cast<GLfloat>(std::sin(glfwGetTime())) * radius};
    GLfloat cameraZ{static_cast<GLfloat>(std::cos(glfwGetTime())) * radius};

    view = glm::lookAt(glm::vec3{cameraX, 0.0f, cameraZ}, glm::vec3{.0f, .0f, .0f}, glm::vec3{.0f, 1.0f, .0f});

    GLint viewLocation{glGetUniformLocation(shaderProgram, "view")};
    glUniformMatrix4fv(viewLocation, 1, GL_FALSE, &view[0][0]);

    glBindVertexArray(VAO);

    std::size_t cubesN{sizeof(cubePositions) / sizeof(glm::vec3)};

    for (std::size_t index = 0; index < cubesN; ++index)
    {
      glm::mat4 model{1.0f};
      model = glm::translate(model, cubePositions[index]);

      // translate local position to world position.
      GLfloat angle{20.0f * index};
      model = glm::rotate(model, glm::radians(angle), glm::vec3{1.0f, 0.3f, 0.5f});

      // initialize model(matrix4) in glsl.
      GLint modelLocation{glGetUniformLocation(shaderProgram, "model")};
      glUniformMatrix4fv(modelLocation, 1, GL_FALSE, &model[0][0]);

      glDrawArrays(GL_TRIANGLES, 0, 36);
    }

    // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved
    // etc.)
    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  // optional: de-allocate all resources once they've outlived their purpose:
  glDeleteVertexArrays(1, &VAO);
  glDeleteBuffers(1, &VBO);

  // glfw: terminate, clearing all previously allocated GLFW resources.
  glfwTerminate();
  return 0;
}<file_sep>/camera/main.cpp

#include <iostream>

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

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

#include "stb_image/stb_image.h"

static constexpr GLint WIDTH{800};
static constexpr GLint HEIGHT{600};

static glm::vec3 cameraPos{0.0f, 0.0f, 3.0f};
static glm::vec3 cameraFront{0.0f, 0.0f, -1.0f};
static glm::vec3 cameraUp{0.0f, 1.0f, 0.0f};

// camera
static float delta_time{};
static float last_frame{};

//mouse and scroll
static float field_of_view{45.0f};
static bool firstMouse{true};
static float lastX{800.f / 2.f};
static float lastY{600.f / 2.f};
static float pitch{};
static float yaw{-90.f};

static void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);

    float camera_speed{5.0f * delta_time};

    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
    {
        cameraPos += (cameraFront * camera_speed);
    }

    if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
    {
        cameraPos -= camera_speed * cameraFront;
    }

    if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
    {
        cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * camera_speed;
    }

    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * camera_speed;
}

static void framebuffer_size_callback(GLFWwindow *window, int width,
                                      int height)
{
    // make sure the viewport matches the new window dimensions; note that width
    // and height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{

    if (field_of_view >= 1.0f && field_of_view <= 45.0f)
    {
        field_of_view -= yoffset;
    }

    if (field_of_view <= 1.f)
    {
        field_of_view = 1.0f;
    }

    if (field_of_view >= 45.0f)
    {
        field_of_view = 45.0f;
    }
    std::cout << xoffset << "----------->" << yoffset << "   " << field_of_view << std::endl;
}

// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
static void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
    if (firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastX;
    float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
    lastX = xpos;
    lastY = ypos;

    float sensitivity = 0.1f; // 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);
}

static constexpr const GLchar *vertexShaderSource{
    "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "layout (location = 1) in vec2 aTexCoord;\n"

    "out vec2 TexCoord;\n"

    "uniform mat4 model;\n"
    "uniform mat4 view;\n"
    "uniform mat4 projection;\n"

    "void main()\n"
    "{\n"
    "	gl_Position = projection * view * model * vec4(aPos, 1.0f);\n"
    "	TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
    "}"};

static constexpr const GLchar *fragmentShaderSource{
    "#version 330 core\n"
    "out vec4 FragColor;\n"
    "in vec2 TexCoord;\n"

    // texture samplers
    "uniform sampler2D texture1;\n"
    "uniform sampler2D texture2;\n"

    "void main()\n"
    "{\n"
    // linearly interpolate between both textures (80% container, 20% awesomeface)
    "	FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);\n"
    "}"};

int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif

    // glfw window creation
    // --------------------
    GLFWwindow *window{glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr)};
    if (window == nullptr)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetScrollCallback(window, scroll_callback);
    glfwSetCursorPosCallback(window, mouse_callback);

    // tell GLFW to capture our mouse
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    glEnable(GL_DEPTH_TEST);

    GLuint vertexShader{}, fragmentShader{};
    // vertex shader
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    GLint success{};
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        GLchar infoLog[1024]{};
        glGetShaderInfoLog(vertexShader, 1024, nullptr, infoLog);
        std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
                  << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
    }

    // fragment Shader
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    success = 0;
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        GLchar infoLog[1024]{};
        glGetShaderInfoLog(fragmentShader, 1024, nullptr, infoLog);
        std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
                  << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
    }

    // shader Program
    GLuint programId{glCreateProgram()};
    glAttachShader(programId, vertexShader);
    glAttachShader(programId, fragmentShader);
    glLinkProgram(programId);

    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    const float 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};

    // world space positions of our cubes
    const 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(float), reinterpret_cast<void *>(0));
    glEnableVertexAttribArray(0);
    // texture coord attribute
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void *>(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    // load and create a texture
    // -------------------------
    GLuint texture1{};
    // texture 1
    // ---------
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1);
    // set the texture wrapping parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    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
    GLint width{}, height{}, nrChannels{};
    stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.
    unsigned char *data = stbi_load("/home/shihua/projects/learn_opengl/camera/image/container.jpg", &width, &height, &nrChannels, 0);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);
    data = nullptr;

    GLuint texture2{};
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // set the texture wrapping parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    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);

    width = 0;
    height = 0;
    nrChannels = 0;
    data = stbi_load("/home/shihua/projects/learn_opengl/camera/image/awesomeface.png", &width, &height, &nrChannels, 0);
    if (data)
    {
        // note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBA
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);
    data = nullptr;

    glUseProgram(programId);

    GLint tex1_loc{glGetUniformLocation(programId, "texture1")};
    glUniform1i(tex1_loc, 0);

    GLint tex2_loc{glGetUniformLocation(programId, "texture2")};
    glUniform1i(tex2_loc, 1);

    while (!glfwWindowShouldClose(window))
    {
        float currentFrame{glfwGetTime()};
        delta_time = currentFrame - last_frame;
        last_frame = currentFrame;

        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // bind textures on corresponding texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);

        glUseProgram(programId);

        glm::mat4 projection{glm::perspective(glm::radians(field_of_view), static_cast<float>(WIDTH / HEIGHT), 0.1f, 100.0f)};
        glUniformMatrix4fv(glGetUniformLocation(programId, "projection"), 1, GL_FALSE, &projection[0][0]);

        // camera/view transformation
        glm::mat4 view{1.0f}; // make sure to initialize matrix to identity matrix first
        view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
        glUniformMatrix4fv(glGetUniformLocation(programId, "view"), 1, GL_FALSE, &view[0][0]);

        // render boxes
        glBindVertexArray(VAO);
        for (unsigned int i = 0; i < 10; i++)
        {
            // calculate the model matrix for each object and pass it to shader before drawing
            glm::mat4 model{1.0f};
            model = glm::translate(model, cubePositions[i]);
            float angle = 20.0f * i;
            model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.0f, 0.0f));
            glUniformMatrix4fv(glGetUniformLocation(programId, "model"), 1, GL_FALSE, &model[0][0]);

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;

    return 0;
}<file_sep>/draw_circle/main.cpp

#include <cmath>
#include <iostream>

#include <glad/glad.h> // must include this before glfw(#error OpenGL header already included, remove this include, glad already provides it);
#include <GLFW/glfw3.h>

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

#include "stb_image/stb_image.h"

static constexpr int WIDTH{800};
static constexpr int HEIGHT{600};

static constexpr const char *vertexShaderSource{
    "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "layout (location = 1) in vec2 aTexCoord;\n"
    "out vec2 texCoord;\n"
    "uniform mat4 model;\n"
    "uniform mat4 view;\n"
    "uniform mat4 projection;\n"
    "void main()\n"
    "{\n"
    "	gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
    "	texCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
    "}"};

static constexpr const char *fragmentShaderSource{
    "#version 330 core\n"
    "out vec4 fragmentColor;\n"
    "in vec2 texCoord;\n"
    "uniform sampler2D texture1;\n"
    "uniform sampler2D texture2;\n"
    "void main()\n"
    "{\n"
    "	fragmentColor = mix(texture(texture1, texCoord), texture(texture2, "
    "texCoord), 0.2);\n"
    "}"};

static void processInput(GLFWwindow *window)
{
  if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    glfwSetWindowShouldClose(window, true);
}

static void framebuffer_size_callback(GLFWwindow *window, int width,
                                      int height)
{
  // make sure the viewport matches the new window dimensions; note that width
  // and height will be significantly larger than specified on retina displays.
  glViewport(0, 0, width, height);
}

const int n = 3;
const GLfloat R = 0.5f;
const GLfloat pi = 3.1415926536f;

int main()
{
  glfwInit();
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  // glfw window creation
  // --------------------
  GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);

  if (window == nullptr)
  {
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();
    return -1;
  }
  glfwMakeContextCurrent(window);
  glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

  // glad: load all OpenGL function pointers
  if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
  {
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
  }

  // render loop
  while (!glfwWindowShouldClose(window))
  {
    // input
    // -----
    processInput(window);

    // render
    // ------
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear last time depth-testing.

    int i = 0;
    glClear(GL_COLOR_BUFFER_BIT);

    // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved
    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  // glfw: terminate, clearing all previously allocated GLFW resources.
  glfwTerminate();
  return 0;
}<file_sep>/window_blending/window_blending/main.cpp
#define GLM_FORCE_CXX14
#define GLM_FORCE_INTRINSICS
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

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

#include <map>
#include <vector>
#include <atomic>
#include <iostream>

#include "shader.hpp"
#include "stb_image/stb_image.h"

static  const int WIDTH{ 1280 };
static  const int HEIGHT{ 720 };

// lighting.
static const glm::vec3 light_pos{ 1.2f, 1.0f, 2.0f };

// camera
static glm::vec3 camera_pos{ 0.0f, 0.0f, 3.0f };
static glm::vec3 camera_front{};
static glm::vec3 camera_up{ 0.0f, 1.0f, 0.0f };
static glm::vec3 camera_right{};
static glm::vec3 world_up = camera_up;
static float delta_time{};
static float last_frame{};

//mouse and scroll
static float field_of_view{ 45.0f };
static std::atomic<bool> first_click{ true };
static float lastX{ WIDTH / 2.f };
static float lastY{ HEIGHT / 2.f };
static float pitch{ 0.0f };
static float yaw{ -90.f };
static float speed{ 2.5f };
static float sensitivity{ 0.05f };

static void update_camera_vectors()
{
	// Calculate the new Front vector
	glm::vec3 front{};
	front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	front.y = std::sin(glm::radians(pitch));
	front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	camera_front = glm::normalize(front);

	// Also re-calculate the Right and Up vector
	camera_right = glm::normalize(glm::cross(camera_front, world_up)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
	camera_up = glm::normalize(glm::cross(camera_right, camera_front));
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
static void process_input(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);

	float camera_speed{ speed * delta_time };

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
	{
		camera_pos += (camera_front * camera_speed);
	}

	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
	{
		camera_pos -= camera_speed * camera_front;
	}

	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
	{
		camera_pos -= camera_right * camera_speed;
	}

	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
	{
		camera_pos += camera_right * camera_speed;
	}
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
static void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
	// make sure the viewport matches the new window dimensions; note that width and
	// height will be significantly larger than specified on retina displays.
	glViewport(0, 0, width, height);
}


// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
static void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
	if (first_click.load())
	{
		lastX = xpos;
		lastY = ypos;
		first_click.store(false);
	}

	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
	lastX = xpos;
	lastY = ypos;

	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;

	update_camera_vectors();
}


// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
	if (field_of_view >= 1.0f && field_of_view <= 45.0f)
	{
		field_of_view -= yoffset;
	}

	if (field_of_view <= 1.f)
	{
		field_of_view = 1.0f;
	}

	if (field_of_view >= 45.0f)
	{
		field_of_view = 45.0f;
	}
}


static GLuint load_texture(const char * path)
{
	GLuint texture_id{};
	glGenTextures(1, &texture_id);

	int width{}, height{}, nrComponents{};
	unsigned char *data{ stbi_load(path, &width, &height, &nrComponents, 0) };

	if (data)
	{
		GLenum format{};
		if (nrComponents == 1)
			format = GL_RED;
		else if (nrComponents == 3)
			format = GL_RGB;
		else if (nrComponents == 4)
			format = GL_RGBA;

		glBindTexture(GL_TEXTURE_2D, texture_id);
		glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);

		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);

		stbi_image_free(data);
	}
	else
	{
		std::cout << __FUNCTION__ << "   " << __LINE__ << "  " << "Texture failed to load at path : " << path << std::endl;
		stbi_image_free(data);
	}

	data = nullptr;
	return texture_id;
}


int main()
{
	// glfw: initialize and configure
// ------------------------------
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// glfw window creation
	// --------------------
	GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}


	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	// tell GLFW to capture our mouse
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	// glad: load all OpenGL function pointers
	// ---------------------------------------
	if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// update camera parameters.
	update_camera_vectors();

	// configure global opengl state
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


	GLuint vertex_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\depth_test\\depth_test\\glsl\\stencil_testing_vertex_shader.glsl", shader_type::vertex_shader) };
	GLuint fragment_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\depth_test\\depth_test\\glsl\\stencil_testing_fragment_shader.glsl", shader_type::fragment_shader) };

	GLuint program_id{ glCreateProgram() };
	glAttachShader(program_id, vertex_shader_id);
	glAttachShader(program_id, fragment_shader_id);
	glLinkProgram(program_id);
	glDeleteShader(vertex_shader_id);
	glDeleteShader(fragment_shader_id);
	shader::checkout_shader_state(program_id, shader_type::program);



	// set up vertex data (and buffer(s)) and configure vertex attributes
	// ------------------------------------------------------------------
	const float cubes_vertices[]{
		// positions          // texture Coords
		-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
	};

	const float floor_vertices[]{
		// positions          // texture Coords 
		 5.0f, -0.5f,  5.0f,  2.0f, 0.0f,
		-5.0f, -0.5f,  5.0f,  0.0f, 0.0f,
		-5.0f, -0.5f, -5.0f,  0.0f, 2.0f,

		 5.0f, -0.5f,  5.0f,  2.0f, 0.0f,
		-5.0f, -0.5f, -5.0f,  0.0f, 2.0f,
		 5.0f, -0.5f, -5.0f,  2.0f, 2.0f
	};

	const float window_vertices[]{
		// positions         // texture Coords (swapped y coordinates because texture is flipped upside down)
		0.0f,  0.5f,  0.0f,  0.0f,  0.0f,
		0.0f, -0.5f,  0.0f,  0.0f,  1.0f,
		1.0f, -0.5f,  0.0f,  1.0f,  1.0f,

		0.0f,  0.5f,  0.0f,  0.0f,  0.0f,
		1.0f, -0.5f,  0.0f,  1.0f,  1.0f,
		1.0f,  0.5f,  0.0f,  1.0f,  0.0f
	};




	GLuint cube_VAO{};
	GLuint cube_VBO{};

	glGenVertexArrays(1, &cube_VAO);
	glGenBuffers(1, &cube_VBO);

	glBindVertexArray(cube_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, cube_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(cubes_vertices), cubes_vertices, GL_STATIC_DRAW);

	std::size_t offset{ 0 };
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 3 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	// notice that.
	glBindVertexArray(0);

	GLuint floor_VAO{};
	GLuint floor_VBO{};

	glGenVertexArrays(1, &floor_VAO);
	glGenBuffers(1, &floor_VBO);

	glBindVertexArray(floor_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, floor_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(floor_vertices), floor_vertices, GL_STATIC_DRAW);

	offset = 0;
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 3 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	// notice that
	glBindVertexArray(0);

	// transparent VAO
	GLuint window_VAO{}, window_VBO{};
	glGenVertexArrays(1, &window_VAO);
	glGenBuffers(1, &window_VBO);

	glBindVertexArray(window_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, window_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(window_vertices), window_vertices, GL_STATIC_DRAW);

	offset = 0;
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 3 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));
	glBindVertexArray(0);


	GLuint wall_texture_id{ load_texture("C:\\Users\\shihua\\source\\repos\\opengl_demo\\window_blending\\window_blending\\image\\marble.jpg") };
	GLuint floor_texture_id{ load_texture("C:\\Users\\shihua\\source\\repos\\opengl_demo\\window_blending\\window_blending\\image\\metal.png") };
	GLuint window_texture_id{ load_texture("C:\\Users\\shihua\\source\\repos\\opengl_demo\\window_blending\\window_blending\\image\\blending_transparent_window.png") };


	std::vector<glm::vec3> windows_locations
	{
		glm::vec3(-1.5f, 0.0f, -0.48f),
		glm::vec3(1.5f, 0.0f, 0.51f),
		glm::vec3(0.0f, 0.0f, 0.7f),
		glm::vec3(-0.3f, 0.0f, -2.3f),
		glm::vec3(0.5f, 0.0f, -0.6f)
	};


	glUseProgram(program_id);
	shader::set_int(program_id, "texture_1", 0);


	while (!glfwWindowShouldClose(window))
	{
		double current_time{ glfwGetTime() };
		delta_time = current_time - last_frame;
		last_frame = current_time;

		process_input(window);

		// sort the transparent windows before rendering
// ---------------------------------------------
		std::map<float, glm::vec3> sorted;
		for (std::size_t index = 0; index < windows_locations.size(); ++index)
		{
			float distance = glm::length(camera_pos - windows_locations[index]);
			sorted[distance] = windows_locations[index];
		}


		glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glUseProgram(program_id);
		glm::mat4 projection{ glm::perspective(glm::radians(field_of_view), WIDTH * 1.0f / HEIGHT * 1.0f, 0.1f, 100.0f) };
		glm::mat4 view{ glm::lookAt(camera_pos, camera_pos + camera_front, camera_up) };
		glm::mat4 model{ 1.0f };
		shader::set_mat4(program_id, "projection", projection);
		shader::set_mat4(program_id, "view", view);

		// cubes
		glBindVertexArray(cube_VAO);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, wall_texture_id);
		model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f));
		shader::set_mat4(program_id, "model", model);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		model = glm::mat4{ 1.0f };
		model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f));
		shader::set_mat4(program_id, "model", model);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		// floor
		glBindVertexArray(floor_VAO);
		glBindTexture(GL_TEXTURE_2D, floor_texture_id);
		model = glm::mat4(1.0f);
		shader::set_mat4(program_id, "model", model);
		glDrawArrays(GL_TRIANGLES, 0, 36);


		// windows (from furthest to nearest)
		glBindVertexArray(window_VAO);
		glBindTexture(GL_TEXTURE_2D, window_texture_id);
		for (std::map<float, glm::vec3>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it)
		{
			model = glm::mat4(1.0f);
			model = glm::translate(model, it->second);
			shader::set_mat4(program_id, "model", model);
			glDrawArrays(GL_TRIANGLES, 0, 6);
		}


		// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	// glfw: terminate, clearing all previously allocated GLFW resources.
	// ------------------------------------------------------------------
	glDeleteVertexArrays(1, &cube_VAO);
	glDeleteVertexArrays(1, &floor_VAO);
	glDeleteVertexArrays(1, &window_VAO);
	glDeleteBuffers(1, &cube_VBO);
	glDeleteBuffers(1, &floor_VBO);
	glDeleteBuffers(1, &window_VBO);

	glfwTerminate();
	return 0;
}<file_sep>/lightcasters-pointlight/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)

project(lightcasters-pointlight)

set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-ggdb -O2")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)


message(${CMAKE_CXX_FLAGS})


include_directories("../glad/include")
include_directories("./stb_image")
include_directories("../glm")

find_package(glfw3 3.3 REQUIRED)
find_package(OpenGL REQUIRED)


if(glfw3_FOUND)
    message("found glfw3")
endif()

if(OpenGL_FOUND)
    message("found opengl")
endif()



add_executable(${PROJECT_NAME} main.cpp glad_src/glad.c stb_image/stb_image.cpp)

target_link_libraries(${PROJECT_NAME}
    # Boost::system
    glfw3 #if do not link glfwxxx.a will: undefined reference to symbol 'pthread_key_delete@@GLIBC_2.2.5'
    pthread
    m
    GL
    GLU
    X11
    Xxf86vm
    Xrandr
    Xi
    dl
)

<file_sep>/exploding_object/exploding_object/main.cpp
﻿#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/texture.h>
#include <assimp/mesh.h>


#define GLM_FORCE_CXX14
#define GLM_FORCE_INTRINSICS
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

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

#include <atomic>
#include <iostream>

#include "shader.hpp"
#include "model.hpp"

static constexpr const int WIDTH{ 800 };
static constexpr const int HEIGHT{ 600 };

// lighting.
static const glm::vec3 light_pos{ 1.2f, 1.0f, 2.0f };

// camera
static glm::vec3 camera_pos{ 0.0f, 0.0f, 3.0f };
static glm::vec3 camera_front{};
static glm::vec3 camera_up{ 0.0f, 1.0f, 0.0f };
static glm::vec3 camera_right{};
static glm::vec3 world_up = camera_up;
static float delta_time{};
static float last_frame{};

//mouse and scroll
static float field_of_view{ 45.0f };
static std::atomic<bool> first_click{ true };
static float lastX{ WIDTH / 2.f };
static float lastY{ HEIGHT / 2.f };
static float pitch{ 0.0f };
static float yaw{ -90.f };
static float speed{ 2.5f };
static float sensitivity{ 0.05f };

static void update_camera_vectors()
{
	// Calculate the new Front vector
	glm::vec3 front{};
	front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	front.y = std::sin(glm::radians(pitch));
	front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	camera_front = glm::normalize(front);

	// Also re-calculate the Right and Up vector
	// Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
	camera_right = glm::normalize(glm::cross(camera_front, world_up));
	camera_up = glm::normalize(glm::cross(camera_right, camera_front));
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
static void process_input(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);

	float camera_speed{ speed * delta_time };

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
	{
		camera_pos += (camera_front * camera_speed);
	}

	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
	{
		camera_pos -= camera_speed * camera_front;
	}

	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
	{
		camera_pos -= camera_right * camera_speed;
	}

	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
	{
		camera_pos += camera_right * camera_speed;
	}
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
static void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
	// make sure the viewport matches the new window dimensions; note that width and
	// height will be significantly larger than specified on retina displays.
	glViewport(0, 0, width, height);
}


// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
static void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
	if (first_click.load())
	{
		lastX = xpos;
		lastY = ypos;
		first_click.store(false);
	}

	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
	lastX = xpos;
	lastY = ypos;

	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;

	update_camera_vectors();
}


// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
	if (field_of_view >= 1.0f && field_of_view <= 45.0f)
	{
		field_of_view -= yoffset;
	}

	if (field_of_view <= 1.f)
	{
		field_of_view = 1.0f;
	}

	if (field_of_view >= 45.0f)
	{
		field_of_view = 45.0f;
	}
}

int main()
{
	// glfw: initialize and configure
// ------------------------------
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// glfw window creation
	// --------------------
	GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}


	glfwMakeContextCurrent(window);

	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	// tell GLFW to capture our mouse
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	// glad: load all OpenGL function pointers
	// ---------------------------------------
	if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// configure global opengl state
	glEnable(GL_DEPTH_TEST);

	GLuint vertex_shader_id{ shader::create("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\exploding_object\\exploding_object\\glsl\\verterx_shader.glsl", shader_type::vertex_shader) };
	GLuint geometry_shader_id{ shader::create("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\exploding_object\\exploding_object\\glsl\\geometry_shader.glsl", shader_type::geometry_shader) };
	GLuint fragment_shader_id{ shader::create("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\exploding_object\\exploding_object\\glsl\\fragment_shader.glsl", shader_type::fragment_shader) };

	GLuint gl_program_id{ glCreateProgram() };
	glAttachShader(gl_program_id, vertex_shader_id);
	glAttachShader(gl_program_id, geometry_shader_id);
	glAttachShader(gl_program_id, fragment_shader_id);
	glLinkProgram(gl_program_id);
	shader::checkout_shader_state(gl_program_id, shader_type::program);

	std::unique_ptr<model_loader> model_loader_ptr{ std::make_unique<model_loader>() };
	model_loader_ptr->load_model("C:\\Users\\shihua\\source\\repos\\opengl_demo\\mesh\\mesh\\image\\nanosuit\\nanosuit.obj");
	model_loader_ptr->load_vertices_data();


	while (!glfwWindowShouldClose(window))
	{
		double current_time{ glfwGetTime() };
		delta_time = current_time - last_frame;
		last_frame = current_time;

		glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glUseProgram(gl_program_id);

		// configure transformation matrices
		glm::mat4 projection{ glm::perspective(glm::radians(45.0f), (WIDTH / HEIGHT)*1.0f, 1.0f, 100.0f) };
		glm::mat4 view{ 1.0f }; // make sure to initialize matrix to identity matrix first
		view = glm::lookAt(camera_pos, camera_pos + camera_front, camera_up);
		glm::mat4 model{ glm::mat4{1.0f } };

		shader::set_mat4(gl_program_id, "projection", projection);
		shader::set_mat4(gl_program_id, "model", model);
		shader::set_mat4(gl_program_id, "view", view);

		shader::set_float(gl_program_id, "time", glfwGetTime());


		// render model.
		model_loader_ptr->draw(gl_program_id);

		// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
		// -------------------------------------------------------------------------------
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	// glfw: terminate, clearing all previously allocated GLFW resources.
	// ------------------------------------------------------------------
	glfwTerminate();
	return 0;
}
<file_sep>/lightcasters-cllimated_light/README.MD
# notice
 - here **material.specular_**, I use GL_TEXTURE1 for sampling. It means that the specular value of middle positions(which is wood material) are vec3(0.0);

- because of **GL_TEXTURE1** is black color in middle postions, so **vec3 specularVec = light.specular_ * specularValue * texture(material.specular_, TexCoords).rgb** will be 0(zero).

- so, **middle positions(GL_TEXTURE1)** just has ambient value and diffuse value.
<file_sep>/multi-lightsource/multi-lightsource/main.cpp
#define GLM_FORCE_CXX14
#define GLM_FORCE_INTRINSICS
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

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

#include <iostream>

#include "stb_image/stb_image.h"

static constexpr const int WIDTH{ 800 };
static constexpr const int HEIGHT{ 600 };

// lighting.
static const glm::vec3 light_pos{ 1.2f, 1.0f, 2.0f };

// camera
static glm::vec3 cameraPos{ 0.0f, 0.0f, 3.0f };
static glm::vec3 cameraFront{};
static glm::vec3 cameraUp{ 0.0f, 1.0f, 0.0f };
static glm::vec3 cameraRight{};
static glm::vec3 worldUp = cameraUp;
static float delta_time{};
static float last_frame{};

//mouse and scroll
static float field_of_view{ 45.0f };
static bool firstMouse{ true };
static float lastX{ WIDTH / 2.f };
static float lastY{ HEIGHT / 2.f };
static float pitch{ 0.0f };
static float yaw{ -90.f };
static float speed{ 2.5f };
static float sensitivity{ 0.05f };

static void updateCameraVectors()
{
	// Calculate the new Front vector
	glm::vec3 front{};
	front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	front.y = std::sin(glm::radians(pitch));
	front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	cameraFront = glm::normalize(front);

	// Also re-calculate the Right and Up vector
	cameraRight = glm::normalize(glm::cross(cameraFront, worldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
	cameraUp = glm::normalize(glm::cross(cameraRight, cameraFront));
}

static constexpr const char *cubeVertexShaderSource{
	"#version 330 core\n"
	"layout (location = 0) in vec3 aPos;\n"
	"layout (location = 1) in vec3 aNormal;\n"
	"layout (location = 2) in vec2 aTexCoords;\n"

	"out vec3 FragPos;\n"
	"out vec3 Normal;\n"
	"out vec2 TexCoords;\n"

	"uniform mat4 model;\n"
	"uniform mat4 view;\n"
	"uniform mat4 projection;\n"

	"void main()\n"
	"{\n"
	"    FragPos = vec3(model * vec4(aPos, 1.0));\n"
	"    TexCoords = aTexCoords;\n"

	// must use Normal Matrix, it can keep NU Scale right.
	"    Normal = mat3(transpose(inverse(model))) * aNormal;\n" //transpose(inverse(model)) create Normal Matrix.

	"    gl_Position = projection * view * vec4(FragPos, 1.0);\n"
	"}" };
static constexpr const char *cubeFragmentShaderSource{
	"#version 330 core\n"
	"out vec4 FragColor;\n"

	"struct Material\n"
	"{\n"
	"	sampler2D diffuse_;\n"
	"	sampler2D specular_;\n"
	"	float shininess_;\n"
	"};\n"

"struct DirLight {\n"
"	vec3 direction_;\n"
"	vec3 ambient_;\n"
"	vec3 diffuse_;\n"
"	vec3 specular_;\n"
"};\n"

"struct PointLight\n"
"{\n"
"	vec3 position_;\n"

"	float constant_;\n"
"	float linear_;\n"
"	float quadratic_;\n"

"	vec3 ambient_;\n"
"	vec3 diffuse_;\n"
"	vec3 specular_;\n"
"};\n"


"struct Material\n"
"{\n"
"    sampler2D diffuse_;\n" //ambient color is the same with diffuse color, ususally.
"    sampler2D specular_;\n"
"    float shininess_;\n"
"};\n"

"struct SpotLight"
"{\n"
"vec3 position_;\n"   // bes same with camera position.
"vec3 direction_;\n" // be same with camera direction.
"float cutoff_;\n"
"float outer_cutoff_;\n"

"float constant_;\n"
"float linear_;\n"
"float quadratic_;\n"

"vec3 ambient_;\n"
"vec3 diffuse_;\n"
"vec3 specular_;\n"
"};\n"

"#define NR_POINT_LIGHTS 4\n"

"in vec3 Normal;\n"
"in vec3 FragPos;\n"
"in vec2 TexCoords;\n"

"uniform Material material;\n"
"uniform DirLight dirLight;\n" // cllimated light
"uniform PointLight pointLights[NR_POINT_LIGHTS];\n" // point lights
"uniform SpotLight spotLight;\n" // flash light

"uniform vec3 cameraPos;\n" //camera pos.

"vec3 CalcDirLight(DirLight dir_light, vec3 normal, vec3 viewer_dir);\n"
"vec3 CalcPointLight(PointLight point_light, vec3 normal, vec3 frag_pos, vec3 viewer_dir);\n"
"vec3 CalcSpotLight(SpotLight spot_light, vec3 normal, vec3 frag_pos, vec3 viewer_dir);\n"

"void main()\n"
"{\n"

// properties
"vec3 norm = normalize(Normal);\n"
"vec3 viewDir = normalize(cameraPos - FragPos);\n"

// == =====================================================
// Our lighting is set up in 3 phases: directional, point lights and an optional flashlight
// For each phase, a calculate function is defined that calculates the corresponding color
// per lamp. In the main() function we take all the calculated colors and sum them up for
// this fragment's final color.
// == =====================================================
// phase 1: directional lighting
"vec3 result = CalcDirLight(dirLight, norm, viewDir);\n"

// phase 2: point lights
"for (int i = 0; i < NR_POINT_LIGHTS; i++)\n"
"	result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);\n"

// phase 3: spot light
"result += CalcSpotLight(spotLight, norm, FragPos, viewDir);\n"

"FragColor = vec4(result, 1.0);\n"

"}\n"

"vec3 CalcDirLight(DirLight dir_light, vec3 normal, vec3 viewer_dir)"
"{\n"

	"vec3 light_direction = normalize(-dir_light.direction_);\n"

	// diffuse
	"float diffuse_value = max(dot(light_direction, normal), 0.0f);\n"

	// specular
	"vec3 reflect_direction = reflect(-light_direction, normal);\n"
	"float specular_value = pow(max(dot(viewer_dir, reflect_direction), 0.0f), material.shininess_);\n"

	"vec3 ambient = dir_light.ambient_ * vec3(texture(material.diffuse_, TexCoords));\n"
	"vec3 diffuse = dir_light.diffuse_ * diffuse_value * vec3(texture(material.diffuse_, TexCoords));\n"
	"vec3 specular = dir_light.specular_ * specular_value * vec3(texture(material.specular_, TexCoords));\n"

	"return (ambient + diffuse + specular);\n"
"}\n"

"vec3 CalcPointLight(PointLight point_light, vec3 normal, vec3 frag_pos, vec3 viewer_dir)\n"
"{\n"
	"vec3 light_direction = normalize(point_light.position_ - frag_pos);\n"

	// diffuse
	"float diffuse_value = max(dot(light_direction, normal), 0.0f);\n"

	// specular
	"vec3 reflect_direction = reflect(-light_direction, normal);\n"
	"float specular_value = pow(max(dot(viewer_dir, reflect_direction), 0.0), material.shininess_);\n"

	// attenuation
	"float distance = length(point_light.position_ - frag_pos);\n"
	"float attenuation_value = 1.0f / (point_light.constant_ + point_light.linear_ * distance + point_light.quadratic_ * (distance * distance));\n"

	"vec3 ambient = point_light.ambient_ * vec3(texture(material.diffuse_, TexCoords));\n"
	"vec3 diffuse = point_light.diffuse_ * diffuse_value * vec3(texture(material.diffuse_, TexCoords));\n"
	"vec3 specular = point_light.specular_ * specular_value * vec3(texture(material.specular_, TexCoords));\n"

	"ambient *= attenuation_value;\n"
	"diffuse *= attenuation_value;\n"
	"specular *= attenuation_value;\n"
	"return (ambient + diffuse + specular);\n"
"}\n"

// calculates the color when using a spot light.
"vec3 CalcSpotLight(SpotLight spot_light, vec3 normal, vec3 frag_pos, vec3 viewer_dir)\n"
"{\n"
	"vec3 light_direction = normalize(spot_light.position_ - frag_pos);\n"

	// diffuse shading
	"float diff = max(dot(normal, light_direction), 0.0);\n"

	// specular shading
	"vec3 reflect_direction = reflect(-light_direction, normal);\n"
	"float spec = pow(max(dot(viewer_dir, reflect_direction), 0.0), material.shininess_);\n"

	// attenuation
	"float distance = length(spot_light.position_ - frag_pos);\n"
	"float attenuation = 1.0f / (spot_light.constant_ + spot_light.linear_ * distance + spot_light.quadratic_ * (distance * distance));\n"

	// spotlight intensity
	"float theta = dot(light_direction, normalize(-spot_light.direction_));\n"
	"float epsilon = spot_light.cutoff_ - spot_light.outer_cutoff_;\n"
	"float intensity = clamp((theta - spot_light.outer_cutoff_) / epsilon, 0.0, 1.0);\n"

	// combine results
	"vec3 ambient = spot_light.ambient_ * vec3(texture(material.diffuse_, TexCoords));\n"
	"vec3 diffuse = spot_light.diffuse_ * diff * vec3(texture(material.diffuse_, TexCoords));\n"
	"vec3 specular = spot_light.specular_ * spec * vec3(texture(material.specular_, TexCoords));\n"

	"ambient *= attenuation * intensity;\n"
	"diffuse *= attenuation * intensity;\n"
	"specular *= attenuation * intensity;\n"

	"return (ambient + diffuse + specular);\n"
"}\n"
};

static constexpr const char *lampVertexShaderSource{
	"#version 330 core\n"
	"layout (location = 0) in vec3 aPos;\n"
	"uniform mat4 model;\n"
	"uniform mat4 view;\n"
	"uniform mat4 projection;\n"
	"void main()\n"
	"{\n"
	"	gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
	"}" };

static constexpr const char *lampFragmentShaderSource{
	"#version 330 core\n"
	"out vec4 FragColor;\n"
	"void main()\n"
	"{\n"
	"    FragColor = vec4(1.0);\n" // set alle 4 vector values to 1.0
	"}" };

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
static void processInput(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);

	float camera_speed{ speed * delta_time };

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
	{
		cameraPos += (cameraFront * camera_speed);
	}

	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
	{
		cameraPos -= camera_speed * cameraFront;
	}

	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
	{
		cameraPos -= cameraRight * camera_speed;
	}

	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		cameraPos += cameraRight * camera_speed;
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
	// make sure the viewport matches the new window dimensions; note that width and
	// height will be significantly larger than specified on retina displays.
	glViewport(0, 0, width, height);
}

// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
static void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
	if (firstMouse)
	{
		lastX = xpos;
		lastY = ypos;
		firstMouse = false;
	}

	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
	lastX = xpos;
	lastY = ypos;

	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;

	updateCameraVectors();
}

// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
	if (field_of_view >= 1.0f && field_of_view <= 45.0f)
	{
		field_of_view -= yoffset;
	}

	if (field_of_view <= 1.f)
	{
		field_of_view = 1.0f;
	}

	if (field_of_view >= 45.0f)
	{
		field_of_view = 45.0f;
	}
}

int main()
{
	// glfw: initialize and configure
	// ------------------------------
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// glfw window creation
	// --------------------
	GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}

	glfwMakeContextCurrent(window);

	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	// tell GLFW to capture our mouse
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	// glad: load all OpenGL function pointers
	// ---------------------------------------
	if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// configure global opengl state
	// -----------------------------
	glEnable(GL_DEPTH_TEST);

	// notice that: count camera front/up/right vectors.
	updateCameraVectors();

	// cube shaders.
	GLuint cubeVertexShader{};
	GLuint cubeFragmentShader{};

	cubeVertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(cubeVertexShader, 1, &cubeVertexShaderSource, nullptr);
	glCompileShader(cubeVertexShader);

	GLint success{};
	glGetShaderiv(cubeVertexShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetShaderInfoLog(cubeVertexShader, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	cubeFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(cubeFragmentShader, 1, &cubeFragmentShaderSource, nullptr);
	glCompileShader(cubeFragmentShader);

	success = 0;
	glGetShaderiv(cubeFragmentShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetShaderInfoLog(cubeFragmentShader, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	GLuint cubeProgramId{ glCreateProgram() };
	glAttachShader(cubeProgramId, cubeVertexShader);
	glAttachShader(cubeProgramId, cubeFragmentShader);
	glLinkProgram(cubeProgramId);

	success = 0;
	glGetProgramiv(cubeProgramId, GL_LINK_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetProgramInfoLog(cubeProgramId, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	// lamp shaders.
	GLuint lampVertexShader{};
	GLuint lampFragmentShader{};

	lampVertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(lampVertexShader, 1, &lampVertexShaderSource, nullptr);
	glCompileShader(lampVertexShader);

	success = 0;
	glGetShaderiv(lampVertexShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetShaderInfoLog(lampVertexShader, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	lampFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(lampFragmentShader, 1, &lampFragmentShaderSource, nullptr);
	glCompileShader(lampFragmentShader);

	success = 0;
	glGetShaderiv(lampFragmentShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetShaderInfoLog(lampFragmentShader, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	GLuint lampProgramId{ glCreateProgram() };
	glAttachShader(lampProgramId, lampVertexShader);
	glAttachShader(lampProgramId, lampFragmentShader);
	glLinkProgram(lampProgramId);

	success = 0;
	glGetProgramiv(lampProgramId, GL_LINK_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetProgramInfoLog(lampProgramId, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	// set up vertex data (and buffer(s)) and configure vertex attributes
	// ------------------------------------------------------------------
	const float vertices[]{
		// positions          // normals           // texture coords
-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,
 0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  0.0f,
 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
-0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  1.0f,
-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,

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

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

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

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

-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,
 0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  1.0f,
 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
-0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f,
-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f
	};

	// positions all containers
	const 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) };

	// positions of the point lights
	const glm::vec3 pointLightPositions[]{
		glm::vec3(0.7f,  0.2f,  2.0f),
		glm::vec3(2.3f, -3.3f, -4.0f),
		glm::vec3(-4.0f,  2.0f, -12.0f),
		glm::vec3(0.0f,  0.0f, -3.0f)
	};

	GLuint VBO{};
	GLuint cubeVAO{};
	glGenVertexArrays(1, &cubeVAO);
	glGenBuffers(1, &VBO);

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

	GLint offset{ 0 };
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
	glEnableVertexAttribArray(0);

	offset = 3 * sizeof(float);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
	glEnableVertexAttribArray(1);

	offset = 6 * sizeof(float);
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
	glEnableVertexAttribArray(2);

	GLuint lampVAO{};
	glGenVertexArrays(1, &lampVAO);

	glBindVertexArray(lampVAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);

	offset = 0;
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
	glEnableVertexAttribArray(0);

	GLuint textureID{};
	glGenTextures(1, &textureID);

	int width{}, height{}, nrComponents{};
	unsigned char *data{ stbi_load("C:\\Users\\shihua\\source\\repos\\opengl_demo\\lightcaster-flashlight\\lightcaster-flashlight\\image\\container2.png",
								  &width, &height, &nrComponents, 0) };

	if (data)
	{
		GLenum format{};
		if (nrComponents == 1)
			format = GL_RED;
		else if (nrComponents == 3)
			format = GL_RGB;
		else if (nrComponents == 4)
			format = GL_RGBA;

		glBindTexture(GL_TEXTURE_2D, textureID);
		glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);

		// set texture wrapper.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

		// set texture filter.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		stbi_image_free(data);
	}
	else
	{
		std::cerr << "the path of image is wrong." << std::endl;
		stbi_image_free(data);
	}

	data = nullptr;

	GLuint textureID2{};
	glGenTextures(1, &textureID2);
	width = 0;
	height = 0;
	nrComponents = 0;

	data = stbi_load("C:\\Users\\shihua\\source\\repos\\opengl_demo\\lightcaster-flashlight\\lightcaster-flashlight\\image\\container2_specular.png",
		&width, &height, &nrComponents, 0);

	if (data)
	{
		GLenum format{};
		if (nrComponents == 1)
			format = GL_RED;
		else if (nrComponents == 3)
			format = GL_RGB;
		else if (nrComponents == 4)
			format = GL_RGBA;

		glBindTexture(GL_TEXTURE_2D, textureID2);
		glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);

		// set texture wrapper.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

		// set texture filter.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		stbi_image_free(data);
	}
	else
	{
		std::cerr << "the path of image is wrong." << std::endl;
		stbi_image_free(data);
	}

	data = nullptr;

	glUseProgram(cubeProgramId);
	GLint texture1Id{ glGetUniformLocation(cubeProgramId, "material.diffuse_") };
	glUniform1i(texture1Id, 0);
	GLint texture2Id{ glGetUniformLocation(cubeProgramId, "material.specular_") };
	glUniform1i(texture2Id, 1);

	while (!glfwWindowShouldClose(window))
	{
		// per-frame time logic
		float currentFrame = glfwGetTime();
		delta_time = currentFrame - last_frame;
		last_frame = currentFrame;

		glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// input
		// -----
		processInput(window);

		glUseProgram(cubeProgramId);

		// bind textures to specify uniform.
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, textureID);

		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, textureID2);

		GLint viewPosLoc{ glGetUniformLocation(cubeProgramId, "cameraPos") };
		glUniform3fv(viewPosLoc, 1, &cameraPos[0]);

		// directional light
		GLint dirLightDirectionLoc{ glGetUniformLocation(cubeProgramId, "dirLight.direction_") };
		glUniform3f(dirLightDirectionLoc, -0.2f, -1.0f, -0.3f);

		GLint dirLightAmbientLoc{ glGetUniformLocation(cubeProgramId, "dirLight.ambient_") };
		glUniform3f(dirLightAmbientLoc, 0.05f, 0.05f, 0.05f);

		GLint dirLightDiffuseLoc{ glGetUniformLocation(cubeProgramId, "dirLight.diffuse_") };
		glUniform3f(dirLightDiffuseLoc, 0.4f, 0.4f, 0.4f);

		GLint dirLightSpecularLoc{ glGetUniformLocation(cubeProgramId, "dirLight.specular_") };
		glUniform3f(dirLightSpecularLoc, 0.5f, 0.5f, 0.5f);

		// point light 1
		glUniform3fv(glGetUniformLocation(cubeProgramId, "pointLights[0].position_"), 1, &pointLightPositions[0][0]);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[0].ambient_"), 0.05f, 0.05f, 0.05f);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[0].diffuse_"), 0.8f, 0.8f, 0.8f);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[0].specular_"), 1.0f, 1.0f, 1.0f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[0].constant_"), 1.0f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[0].linear_"), 0.09f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[0].quadratic_"), 0.032f);
		// point light 2
		glUniform3fv(glGetUniformLocation(cubeProgramId, "pointLights[1].position_"), 1, &pointLightPositions[1][0]);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[1].ambient_"), 0.05f, 0.05f, 0.05f);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[1].diffuse_"), 0.8f, 0.8f, 0.8f);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[1].specular_"), 1.0f, 1.0f, 1.0f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[1].constant_"), 1.0f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[1].linear_"), 0.09f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[1].quadratic_"), 0.032);
		// point light 3
		glUniform3fv(glGetUniformLocation(cubeProgramId, "pointLights[2].position_"), 1, &pointLightPositions[2][0]);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[2].ambient_"), 0.05f, 0.05f, 0.05f);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[2].diffuse_"), 0.8f, 0.8f, 0.8f);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[2].specular_"), 1.0f, 1.0f, 1.0f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[2].constant_"), 1.0f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[2].linear_"), 0.09f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[2].quadratic_"), 0.032f);
		// point light 4
		glUniform3fv(glGetUniformLocation(cubeProgramId, "pointLights[3].position_"), 1, &pointLightPositions[1][0]);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[3].ambient_"), 0.05f, 0.05f, 0.05f);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[3].diffuse_"), 0.8f, 0.8f, 0.8f);
		glUniform3f(glGetUniformLocation(cubeProgramId, "pointLights[3].specular_"), 1.0f, 1.0f, 1.0f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[3].constant_"), 1.0f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[3].linear_"), 0.09f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "pointLights[3].quadratic_"), 0.32f);
		// spotLight
		glUniform3fv(glGetUniformLocation(cubeProgramId, "spotLight.position_"), 1, &cameraPos[0]);
		glUniform3fv(glGetUniformLocation(cubeProgramId, "spotLight.direction_"), 1, &cameraFront[0]);
		glUniform3f(glGetUniformLocation(cubeProgramId, "spotLight.ambient_"), 0.0f, 0.0f, 0.0f);
		glUniform3f(glGetUniformLocation(cubeProgramId, "spotLight.diffuse_"), 1.0f, 1.0f, 1.0f);
		glUniform3f(glGetUniformLocation(cubeProgramId, "spotLight.specular_"), 1.0f, 1.0f, 1.0f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "spotLight.constant_"), 1.0f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "spotLight.linear_"), 0.09f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "spotLight.quadratic_"), 0.032f);
		glUniform1f(glGetUniformLocation(cubeProgramId, "spotLight.cutoff_"), std::cos(glm::radians(12.5f)));
		glUniform1f(glGetUniformLocation(cubeProgramId, "spotLight.outer_cutoff_"), std::cos(glm::radians(15.0f)));

		GLint materialShininessLoc{ glGetUniformLocation(cubeProgramId, "material.shininess_") };
		glUniform1f(materialShininessLoc, 32.0f);

		// view/projection transformations
		glm::mat4 projection{ glm::perspective(glm::radians(field_of_view), static_cast<float>(WIDTH) / static_cast<float>(HEIGHT), 0.1f, 100.0f) };
		glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "projection"), 1, GL_FALSE, &projection[0][0]);

		// camera/view transformation
		glm::mat4 view{ 1.0f }; // make sure to initialize matrix to identity matrix first
		view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
		glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "view"), 1, GL_FALSE, &view[0][0]);

		// model
		glm::mat4 model{ 1.0f };
		glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "model"), 1, GL_FALSE, &model[0][0]);

		// render containers
		glBindVertexArray(cubeVAO);
		for (unsigned int i = 0; i < 10; i++)
		{
			// calculate the model matrix for each object and pass it to shader before drawing
			glm::mat4 model{ 1.0f };
			model = glm::translate(model, cubePositions[i]);
			float angle{ 20.0f * i };
			model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
			glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "model"), 1, GL_FALSE, &model[0][0]);

			glDrawArrays(GL_TRIANGLES, 0, 36);
		}

		// lamp
		glUseProgram(lampProgramId);
		glUniformMatrix4fv(glGetUniformLocation(lampProgramId, "projection"), 1, GL_FALSE, &projection[0][0]);
		glUniformMatrix4fv(glGetUniformLocation(lampProgramId, "view"), 1, GL_FALSE, &view[0][0]);

		// we now draw as many light bulbs as we have point lights.
		glBindVertexArray(lampVAO);
		for (std::size_t i = 0; i < 4; ++i)
		{
			model = glm::mat4{ 1.0f };
			model = glm::translate(model, pointLightPositions[i]);
			model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube
			glUniformMatrix4fv(glGetUniformLocation(lampProgramId, "model"), 1, GL_FALSE, &model[0][0]);
			glDrawArrays(GL_TRIANGLES, 0, 36);
		}

		// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
		// -------------------------------------------------------------------------------
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	// optional: de-allocate all resources once they've outlived their purpose:
	// ------------------------------------------------------------------------
	glDeleteVertexArrays(1, &cubeVAO);
	glDeleteVertexArrays(1, &lampVAO);
	glDeleteBuffers(1, &VBO);

	// glfw: terminate, clearing all previously allocated GLFW resources.
	// ------------------------------------------------------------------
	glfwTerminate();
	return 0;
}<file_sep>/cubemap_reflection/cubemap_reflection/main.cpp
#define GLM_FORCE_CXX14
#define GLM_FORCE_INTRINSICS
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

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

#include <vector>
#include <atomic>
#include <iostream>

#include "shader.hpp"
#include "stb_image/stb_image.h"

static  const int WIDTH{ 1280 };
static  const int HEIGHT{ 720 };

// lighting.
static const glm::vec3 light_pos{ 1.2f, 1.0f, 2.0f };

// camera
static glm::vec3 camera_pos{ 0.0f, 0.0f, 3.0f };
static glm::vec3 camera_front{};
static glm::vec3 camera_up{ 0.0f, 1.0f, 0.0f };
static glm::vec3 camera_right{};
static glm::vec3 world_up = camera_up;
static float delta_time{};
static float last_frame{};

//mouse and scroll
static float field_of_view{ 45.0f };
static std::atomic<bool> first_click{ true };
static float lastX{ WIDTH / 2.f };
static float lastY{ HEIGHT / 2.f };
static float pitch{ 0.0f };
static float yaw{ -90.f };
static float speed{ 2.5f };
static float sensitivity{ 0.05f };

static void update_camera_vectors()
{
	// Calculate the new Front vector
	glm::vec3 front{};
	front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	front.y = std::sin(glm::radians(pitch));
	front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	camera_front = glm::normalize(front);

	// Also re-calculate the Right and Up vector
	camera_right = glm::normalize(glm::cross(camera_front, world_up)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
	camera_up = glm::normalize(glm::cross(camera_right, camera_front));
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
static void process_input(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);

	float camera_speed{ speed * delta_time };

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
	{
		camera_pos += (camera_front * camera_speed);
	}

	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
	{
		camera_pos -= camera_speed * camera_front;
	}

	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
	{
		camera_pos -= camera_right * camera_speed;
	}

	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
	{
		camera_pos += camera_right * camera_speed;
	}
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
static void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
	// make sure the viewport matches the new window dimensions; note that width and
	// height will be significantly larger than specified on retina displays.
	glViewport(0, 0, width, height);
}


// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
static void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
	if (first_click.load())
	{
		lastX = xpos;
		lastY = ypos;
		first_click.store(false);
	}

	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
	lastX = xpos;
	lastY = ypos;

	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;

	update_camera_vectors();
}


// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
	if (field_of_view >= 1.0f && field_of_view <= 45.0f)
	{
		field_of_view -= yoffset;
	}

	if (field_of_view <= 1.f)
	{
		field_of_view = 1.0f;
	}

	if (field_of_view >= 45.0f)
	{
		field_of_view = 45.0f;
	}
}


static GLuint load_texture(const char * path)
{
	GLuint texture_id{};
	glGenTextures(1, &texture_id);

	int width{}, height{}, nrComponents{};
	unsigned char *data{ stbi_load(path, &width, &height, &nrComponents, 0) };

	if (data)
	{
		GLenum format{};
		if (nrComponents == 1)
			format = GL_RED;
		else if (nrComponents == 3)
			format = GL_RGB;
		else if (nrComponents == 4)
			format = GL_RGBA;

		glBindTexture(GL_TEXTURE_2D, texture_id);
		glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);

		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);

		stbi_image_free(data);
	}
	else
	{
		std::cout << __FUNCTION__ << "   " << __LINE__ << "  " << "Texture failed to load at path : " << path << std::endl;
		stbi_image_free(data);
	}

	data = nullptr;
	return texture_id;
}


// loads a cubemap texture from 6 individual texture faces
// order:
// +X (right)
// -X (left)
// +Y (top)
// -Y (bottom)
// +Z (front) 
// -Z (back)
// -------------------------------------------------------
static GLuint load_cubemap(const std::vector<std::string>& faces)
{
	GLuint texture_id{};
	glGenTextures(1, &texture_id);
	glBindTexture(GL_TEXTURE_CUBE_MAP, texture_id);

	int width{}, height{}, nrChannels{};
	for (std::size_t index = 0; index < faces.size(); ++index)
	{
		unsigned char *data{ stbi_load(faces[index].c_str(), &width, &height, &nrChannels, 0) };
		if (data)
		{
			glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + index, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
			stbi_image_free(data);
		}
		else
		{
			std::cout << "Cubemap texture failed to load at path: " << faces[index] << 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 texture_id;
}


int main()
{
	// glfw: initialize and configure
// ------------------------------
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// glfw window creation
	// --------------------
	GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}


	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	// tell GLFW to capture our mouse
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	// glad: load all OpenGL function pointers
	// ---------------------------------------
	if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// update camera parameters.
	update_camera_vectors();

	// configure global opengl state
	glEnable(GL_DEPTH_TEST);


	GLuint vertex_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\cubemap_reflection\\cubemap_reflection\\glsl\\vertex_shader.glsl", shader_type::vertex_shader) };
	GLuint fragment_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\cubemap_reflection\\cubemap_reflection\\glsl\\fragment_shader.glsl", shader_type::fragment_shader) };

	GLuint program_id{ glCreateProgram() };
	glAttachShader(program_id, vertex_shader_id);
	glAttachShader(program_id, fragment_shader_id);
	glLinkProgram(program_id);
	glDeleteShader(vertex_shader_id);
	glDeleteShader(fragment_shader_id);
	shader::checkout_shader_state(program_id, shader_type::program);


	GLuint skybox_vertex_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\cubemap_reflection\\cubemap_reflection\\glsl\\skybox_vertex_shader.glsl", shader_type::vertex_shader) };
	GLuint skybox_fragment_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\cubemap_reflection\\cubemap_reflection\\glsl\\skybox_fragment_shader.glsl", shader_type::fragment_shader) };

	GLuint skybox_program_id{ glCreateProgram() };
	glAttachShader(skybox_program_id, skybox_vertex_shader_id);
	glAttachShader(skybox_program_id, skybox_fragment_shader_id);
	glLinkProgram(skybox_program_id);
	glDeleteShader(skybox_vertex_shader_id);
	glDeleteShader(skybox_fragment_shader_id);
	shader::checkout_shader_state(skybox_program_id, shader_type::program);


	// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
	const float cube_vertices[]{
		// positions          // normals
		-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		 0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		 0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		-0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
		-0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

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

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

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

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

		-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
		 0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
		 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
		 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
		-0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
		-0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
	};

	const float skybox_vertices[]{
		// positions          
		-1.0f,  1.0f, -1.0f,
		-1.0f, -1.0f, -1.0f,
		 1.0f, -1.0f, -1.0f,
		 1.0f, -1.0f, -1.0f,
		 1.0f,  1.0f, -1.0f,
		-1.0f,  1.0f, -1.0f,

		-1.0f, -1.0f,  1.0f,
		-1.0f, -1.0f, -1.0f,
		-1.0f,  1.0f, -1.0f,
		-1.0f,  1.0f, -1.0f,
		-1.0f,  1.0f,  1.0f,
		-1.0f, -1.0f,  1.0f,

		 1.0f, -1.0f, -1.0f,
		 1.0f, -1.0f,  1.0f,
		 1.0f,  1.0f,  1.0f,
		 1.0f,  1.0f,  1.0f,
		 1.0f,  1.0f, -1.0f,
		 1.0f, -1.0f, -1.0f,

		-1.0f, -1.0f,  1.0f,
		-1.0f,  1.0f,  1.0f,
		 1.0f,  1.0f,  1.0f,
		 1.0f,  1.0f,  1.0f,
		 1.0f, -1.0f,  1.0f,
		-1.0f, -1.0f,  1.0f,

		-1.0f,  1.0f, -1.0f,
		 1.0f,  1.0f, -1.0f,
		 1.0f,  1.0f,  1.0f,
		 1.0f,  1.0f,  1.0f,
		-1.0f,  1.0f,  1.0f,
		-1.0f,  1.0f, -1.0f,

		-1.0f, -1.0f, -1.0f,
		-1.0f, -1.0f,  1.0f,
		 1.0f, -1.0f, -1.0f,
		 1.0f, -1.0f, -1.0f,
		-1.0f, -1.0f,  1.0f,
		 1.0f, -1.0f,  1.0f
	};


	GLuint cube_VAO{};
	GLuint cube_VBO{};

	glGenVertexArrays(1, &cube_VAO);
	glGenBuffers(1, &cube_VBO);

	glBindVertexArray(cube_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, cube_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertices), cube_vertices, GL_STATIC_DRAW);

	std::size_t offset{ 0 };
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 3 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), reinterpret_cast<void*>(offset));

	// notice that.
	glBindVertexArray(0);



	GLuint skybox_VAO{};
	GLuint skybox_VBO{};

	glGenVertexArrays(1, &skybox_VAO);
	glGenBuffers(1, &skybox_VBO);

	glBindVertexArray(skybox_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, skybox_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(skybox_vertices), skybox_vertices, GL_STATIC_DRAW);

	offset = 0;
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), reinterpret_cast<void*>(offset));

	// notice that
	glBindVertexArray(0);



	std::vector<std::string> skybox_textures
	{
		"C:\\Users\\shihua\\source\\repos\\opengl_demo\\cubemap\\cubemap\\image\\skybox\\right.jpg",
		"C:\\Users\\shihua\\source\\repos\\opengl_demo\\cubemap\\cubemap\\image\\skybox\\left.jpg",
		"C:\\Users\\shihua\\source\\repos\\opengl_demo\\cubemap\\cubemap\\image\\skybox\\top.jpg",
		"C:\\Users\\shihua\\source\\repos\\opengl_demo\\cubemap\\cubemap\\image\\skybox\\bottom.jpg",
		"C:\\Users\\shihua\\source\\repos\\opengl_demo\\cubemap\\cubemap\\image\\skybox\\front.jpg",
		"C:\\Users\\shihua\\source\\repos\\opengl_demo\\cubemap\\cubemap\\image\\skybox\\back.jpg"
	};

	GLuint skybox_texture_id{ load_cubemap(skybox_textures) };

	glUseProgram(program_id);
	shader::set_int(program_id, "skybox", 0);

	glUseProgram(skybox_program_id);
	shader::set_int(skybox_program_id, "skybox", 0);

	while (!glfwWindowShouldClose(window))
	{
		double current_time{ glfwGetTime() };
		delta_time = current_time - last_frame;
		last_frame = current_time;

		process_input(window);

		glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// draw scene as normal
		glUseProgram(program_id);
		glm::mat4 model{ 1.0f };
		glm::mat4 view{ glm::lookAt(camera_pos, camera_pos + camera_front, camera_up) };
		glm::mat4 projection{ glm::perspective(glm::radians(field_of_view), WIDTH * 1.0f / HEIGHT * 1.0f, 0.1f, 100.0f) };
		shader::set_mat4(program_id, "model", model);
		shader::set_mat4(program_id, "view", view);
		shader::set_mat4(program_id, "projection", projection);
		shader::set_vec3(program_id, "camera_position", camera_pos);

		//draw cube.
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_texture_id);
		glBindVertexArray(cube_VAO);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		//restore
		glBindVertexArray(cube_VAO);

		// draw skybox as last
		glDepthFunc(GL_LEQUAL);  // change depth function so depth test passes when values are equal to depth buffer's content
		glUseProgram(skybox_program_id);
		view = glm::mat4{ glm::mat3{glm::lookAt(camera_pos, camera_pos + camera_front, camera_up)} };
		shader::set_mat4(skybox_program_id, "view", view);
		shader::set_mat4(skybox_program_id, "projection", projection);

		glBindVertexArray(skybox_VAO);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_texture_id);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		//restore
		glBindVertexArray(0);
		glDepthFunc(GL_LESS); // set depth function back to default


		// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	// glfw: terminate, clearing all previously allocated GLFW resources.
	// ------------------------------------------------------------------
	glDeleteVertexArrays(1, &cube_VAO);
	glDeleteVertexArrays(1, &skybox_VAO);
	glDeleteBuffers(1, &cube_VBO);
	glDeleteBuffers(1, &cube_VBO);

	glfwTerminate();
	return 0;
}<file_sep>/hello_triangle/main.cpp
#include <iostream>

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

#include "stb_image/stb_image.h"

static constexpr int WIDTH{ 800 };
static constexpr int HEIGHT{ 600 };

static constexpr const char* vertexShaderSource
{
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"layout (location = 2) in vec2 aTexCoord;\n"
"out vec3 ourColor;\n"
"out vec2 texCoord;\n"
"void main()\n"
"{\n"
"	gl_Position = vec4(aPos, 1.0);\n"
"	ourColor = aColor;\n"
"	texCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
"}"
};

static constexpr const char* fragmentShaderSource
{
"#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"in vec2 texCoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
"	FragColor = texture(texture1, texCoord);\n"
"}"
};

static void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

static void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and 
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

        // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    if (window == nullptr)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // glad: load all OpenGL function pointers
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }


    // build and compile our shader program
    // ------------------------------------
    // vertex shader
    int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);
    // check for shader compile errors
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // fragment shader
    int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);
    // check for shader compile errors
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // link shaders
    int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    // check for linking errors
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }

    // after linking program, you can delete shaders.
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    const float 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 
    };
    constexpr GLuint indices[]{  
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
    };

    GLuint VAO{}; // vertex array object.
    GLuint VBO{}; // vertex buffer object.
    GLuint EBO{}; // element buffer object.

    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);

    // tell shader how to parse vertices.
    // position attribute
    GLint offset{};
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void*>(offset));
    glEnableVertexAttribArray(0);

    // color attribute
    offset = 3 * sizeof(float);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void*>(offset));
    glEnableVertexAttribArray(1);

    // texture coord attribute
    offset = 6 * sizeof(float);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void*>(offset));
    glEnableVertexAttribArray(2);

    // 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 (default 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, nrChannels;
    unsigned char *data{ stbi_load("/home/shihua/projects/learn_opengl/hello_triangle/image/container.jpg", &width, &height, &nrChannels, 0) };
    
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);

        // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // bind Texture
        // glBindTexture(GL_TEXTURE_2D, texture);

        // render container
        glUseProgram(shaderProgram);

        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);


        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    glfwTerminate();
    return 0;

}<file_sep>/exploding_object/exploding_object/shader.hpp
﻿#ifndef __SHADER_H__
#define __SHADER_H__

#include <glad/glad.h>
#include <glm/glm.hpp>

#include <iostream>
#include <sstream>
#include <fstream>
#include <cassert>

enum class shader_type
{
	vertex_shader,
	geometry_shader,
	fragment_shader,
	program
};

class shader final
{
public:
	shader() = default;
	shader(const shader&) = delete;
	shader& operator=(const shader&) = delete;

	static GLuint create(const std::basic_string<char>& glsl_file, shader_type type)
	{
		assert(!glsl_file.empty());
		std::basic_ifstream<char> file_reader{ glsl_file };
		assert(file_reader.is_open());

		std::basic_ostringstream<char> file_buffer_reader{};
		std::basic_filebuf<char>* file_buffer_ptr = file_reader.rdbuf();

		// read data which is in file.
		file_buffer_reader << file_buffer_ptr;

		GLuint shader_id{};

		if (type == shader_type::vertex_shader)
		{
			shader_id = glCreateShader(GL_VERTEX_SHADER);
		}

		if (type == shader_type::fragment_shader)
		{
			shader_id = glCreateShader(GL_FRAGMENT_SHADER);
		}

		if (type == shader_type::geometry_shader)
		{
			shader_id = glCreateShader(GL_GEOMETRY_SHADER);
		}

		std::basic_string<char> shader_source{ file_buffer_reader.str() };
		const char* c_shader_source_str{ shader_source.c_str() };
		glShaderSource(shader_id, 1, &c_shader_source_str, nullptr);
		glCompileShader(shader_id);

		if (!shader::checkout_shader_state(shader_id, type))
		{
			return 0;
		}

		return shader_id;
	}

	static void set_bool(GLuint id, const std::string &name, bool value) noexcept
	{
		glUniform1i(glGetUniformLocation(id, name.c_str()), (int)value);
	}

	static void set_int(GLuint id, const std::string &name, int value) noexcept
	{
		glUniform1i(glGetUniformLocation(id, name.c_str()), value);
	}

	static void set_float(GLuint id, const std::string &name, float value) noexcept
	{
		glUniform1f(glGetUniformLocation(id, name.c_str()), value);
	}

	static void set_vec2(GLuint id, const std::string &name, const glm::vec2 &value) noexcept
	{
		glUniform2fv(glGetUniformLocation(id, name.c_str()), 1, &value[0]);
	}

	static void set_vec2(GLuint id, const std::string &name, float x, float y) noexcept
	{
		glUniform2f(glGetUniformLocation(id, name.c_str()), x, y);
	}

	static void set_vec3(GLuint id, const std::string &name, const glm::vec3 &value) noexcept
	{
		glUniform3fv(glGetUniformLocation(id, name.c_str()), 1, &value[0]);
	}
	static void set_vec3(GLuint id, const std::string &name, float x, float y, float z) noexcept
	{
		glUniform3f(glGetUniformLocation(id, name.c_str()), x, y, z);
	}

	static void set_vec4(GLuint id, const std::string &name, const glm::vec4 &value) noexcept
	{
		glUniform4fv(glGetUniformLocation(id, name.c_str()), 1, &value[0]);
	}
	static void set_vec4(GLuint id, const std::string &name, float x, float y, float z, float w)noexcept
	{
		glUniform4f(glGetUniformLocation(id, name.c_str()), x, y, z, w);
	}

	static void set_mat2(GLuint id, const std::string &name, const glm::mat2 &mat) noexcept
	{
		glUniformMatrix2fv(glGetUniformLocation(id, name.c_str()), 1, GL_FALSE, &mat[0][0]);
	}

	static void set_mat3(GLuint id, const std::string &name, const glm::mat3 &mat) noexcept
	{
		glUniformMatrix3fv(glGetUniformLocation(id, name.c_str()), 1, GL_FALSE, &mat[0][0]);
	}

	static void set_mat4(GLuint id, const std::basic_string<char> &name, const glm::mat4 &mat)noexcept
	{
		glUniformMatrix4fv(glGetUniformLocation(id, name.c_str()), 1, GL_FALSE, &mat[0][0]);
	}

	static bool checkout_shader_state(GLuint id, shader_type type)
	{
		GLint success{};
		GLchar error_log[1024]{};

		if (type == shader_type::vertex_shader || type == shader_type::fragment_shader || type == shader_type::geometry_shader)
		{
			glGetShaderiv(id, GL_COMPILE_STATUS, &success);

			if (!success)
			{
				glGetShaderInfoLog(id, 1024, nullptr, error_log);
				std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << "\n" << error_log << std::endl;
				return false;
			}
		}

		if (type == shader_type::program)
		{
			glGetProgramiv(id, GL_LINK_STATUS, &success);
			if (!success)
			{
				glGetProgramInfoLog(id, 1024, nullptr, error_log);
				std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << "\n" << error_log << std::endl;
				return false;
			}
		}

		return true;
	}

};



#endif // !__SHADER_H__
<file_sep>/asteriods/asteriods/main.cpp
﻿#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/texture.h>
#include <assimp/mesh.h>


#define GLM_FORCE_CXX14
#define GLM_FORCE_INTRINSICS
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

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

#include <atomic>
#include <random>
#include <iostream>


#include "shader.hpp"
#include "model.hpp"



static constexpr const int WIDTH{ 800 };
static constexpr const int HEIGHT{ 600 };

// lighting.
static const glm::vec3 light_pos{ 1.2f, 1.0f, 2.0f };

// camera
static glm::vec3 camera_pos{ 0.0f, 0.0f, 3.0f };
static glm::vec3 camera_front{};
static glm::vec3 camera_up{ 0.0f, 1.0f, 0.0f };
static glm::vec3 camera_right{};
static glm::vec3 world_up = camera_up;
static float delta_time{};
static float last_frame{};

//mouse and scroll
static float field_of_view{ 45.0f };
static std::atomic<bool> first_click{ true };
static float lastX{ WIDTH / 2.f };
static float lastY{ HEIGHT / 2.f };
static float pitch{ 0.0f };
static float yaw{ -90.f };
static float speed{ 2.5f };
static float sensitivity{ 0.05f };

static void update_camera_vectors()
{
	// Calculate the new Front vector
	glm::vec3 front{};
	front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	front.y = std::sin(glm::radians(pitch));
	front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	camera_front = glm::normalize(front);

	// Also re-calculate the Right and Up vector
	// Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
	camera_right = glm::normalize(glm::cross(camera_front, world_up));
	camera_up = glm::normalize(glm::cross(camera_right, camera_front));
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
static void process_input(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);

	float camera_speed{ speed * delta_time };

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
	{
		camera_pos += (camera_front * camera_speed);
	}

	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
	{
		camera_pos -= camera_speed * camera_front;
	}

	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
	{
		camera_pos -= camera_right * camera_speed;
	}

	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
	{
		camera_pos += camera_right * camera_speed;
	}
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
static void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
	// make sure the viewport matches the new window dimensions; note that width and
	// height will be significantly larger than specified on retina displays.
	glViewport(0, 0, width, height);
}


// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
static void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
	if (first_click.load())
	{
		lastX = xpos;
		lastY = ypos;
		first_click.store(false);
	}

	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
	lastX = xpos;
	lastY = ypos;

	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;

	update_camera_vectors();
}


// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
	if (field_of_view >= 1.0f && field_of_view <= 45.0f)
	{
		field_of_view -= yoffset;
	}

	if (field_of_view <= 1.f)
	{
		field_of_view = 1.0f;
	}

	if (field_of_view >= 45.0f)
	{
		field_of_view = 45.0f;
	}
}

int main()
{
	// glfw: initialize and configure
// ------------------------------
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// glfw window creation
	// --------------------
	GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}


	glfwMakeContextCurrent(window);

	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	// tell GLFW to capture our mouse
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	// glad: load all OpenGL function pointers
	// ---------------------------------------
	if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// configure global opengl state
	glEnable(GL_DEPTH_TEST);

	GLuint asteriods_vertex_shader_id{ shader::create("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\asteriods\\asteriods\\glsl\\vertex_shader.glsl", shader_type::vertex_shader) };
	GLuint asteriods_fragment_shader_id{ shader::create("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\asteriods\\asteriods\\glsl\\fragment_shader.glsl", shader_type::fragment_shader) };

	GLuint asteriods_gl_program_id{ glCreateProgram() };
	glAttachShader(asteriods_gl_program_id, asteriods_vertex_shader_id);
	glAttachShader(asteriods_gl_program_id, asteriods_fragment_shader_id);
	glLinkProgram(asteriods_gl_program_id);
	shader::checkout_shader_state(asteriods_gl_program_id, shader_type::program);


	GLuint planet_vertex_shader_id{ shader::create("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\asteriods\\asteriods\\glsl\\vertex_shader.glsl", shader_type::vertex_shader) };
	GLuint planet_fragment_shader_id{ shader::create("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\asteriods\\asteriods\\glsl\\fragment_shader.glsl", shader_type::fragment_shader) };
	GLuint planet_gl_program_id{ glCreateProgram() };
	glAttachShader(planet_fragment_shader_id, planet_vertex_shader_id);
	glAttachShader(planet_fragment_shader_id, planet_fragment_shader_id);



	std::unique_ptr<model_loader> loaded_planet{ std::make_unique<model_loader>() };
	loaded_planet->load_model("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\asteriods\\asteriods\\model_file\\planet\\planet.obj");
	loaded_planet->load_vertices_data();

	std::unique_ptr<model_loader> loaded_rock{ std::make_unique<model_loader>() };
	loaded_rock->load_model("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\asteriods\\asteriods\\model_file\\planet\\rock.obj");
	loaded_rock->load_vertices_data();



	// generate a large list of semi-random model transformation matrices
	// ------------------------------------------------------------------
	std::size_t amount{ 1000 };
	std::unique_ptr<glm::mat4[]> model_matrices{ new glm::mat4[amount]{} };

	std::random_device random_device{};
	std::mt19937 generator{ random_device() };
	std::uniform_int_distribution<> distribution{ 0, static_cast<long int>(glfwGetTime()) };

	float radius{ 50.0 };
	float offset{ 2.5f };
	for (std::size_t index = 0; index < amount; ++index)
	{
		glm::mat4 model = glm::mat4(1.0f);
		// 1. translation: displace along circle with 'radius' in range [-offset, offset]
		float angle{ index * 1.0f / amount * 360.0f };
		float displacement{ (distribution(generator) % (int)(2 * offset * 100)) / 100.0f - offset };
		float x{ std::sin(angle) * radius + displacement };
		displacement = (distribution(generator) % (int)(2 * offset * 100)) / 100.0f - offset;
		float y{ displacement * 0.4f }; // keep height of asteroid field smaller compared to width of x and z
		displacement = (distribution(generator) % (int)(2 * offset * 100)) / 100.0f - offset;
		float z{ std::cos(angle) * radius + displacement };
		model = glm::translate(model, glm::vec3(x, y, z));

		// 2. scale: Scale between 0.05 and 0.25f
		float scale{ static_cast<float>((distribution(generator) % 20) / 100.0f + 0.05) };
		model = glm::scale(model, glm::vec3(scale));

		// 3. rotation: add random rotation around a (semi)randomly picked rotation axis vector
		float rotAngle = (rand() % 360);
		model = glm::rotate(model, rotAngle, glm::vec3(0.4f, 0.6f, 0.8f));

		// 4. now add to list of matrices
		model_matrices[index] = model;
	}


	// configure instanced array
	GLuint instanced_VBO{};
	glGenBuffers(1, &instanced_VBO);
	glBindBuffer(GL_ARRAY_BUFFER, instanced_VBO);
	glBufferData(GL_ARRAY_BUFFER, amount * sizeof(glm::mat4), model_matrices.get(), GL_STATIC_DRAW);

	const auto& meshes_in_rock{ loaded_rock->get_meshes() };
	auto rock_meshed_itr{ meshes_in_rock.cbegin() };
	for (; rock_meshed_itr != meshes_in_rock.cend(); ++rock_meshed_itr)
	{
		std::size_t mesh_VAO{ (*rock_meshed_itr)->get_VAO() };
		glBindVertexArray(mesh_VAO);

		glEnableVertexAttribArray(3);
		glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), reinterpret_cast<void*>(0));

		glEnableVertexAttribArray(4);
		glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), reinterpret_cast<void*>(sizeof(glm::mat4)));

		glEnableVertexAttribArray(5);
		glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), reinterpret_cast<void*>(2 * sizeof(glm::mat4)));

		glEnableVertexAttribArray(6);
		glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), reinterpret_cast<void*>(3 * sizeof(glm::mat4)));



		glBindVertexArray(0);
	}





	while (!glfwWindowShouldClose(window))
	{
		double current_time{ glfwGetTime() };
		delta_time = current_time - last_frame;
		last_frame = current_time;


		// process keyboard events.
		process_input(window);

		glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


		glm::mat4 projection{ glm::perspective(glm::radians(45.0f), (WIDTH / HEIGHT)*1.0f, 1.0f, 100.0f) };
		glm::mat4 view{ 1.0f }; // make sure to initialize matrix to identity matrix first
		view = glm::lookAt(camera_pos, camera_pos + camera_front, camera_up);

		glUseProgram(asteriods_gl_program_id);
		shader::set_mat4(asteriods_gl_program_id, "projection", projection);
		shader::set_mat4(asteriods_gl_program_id, "view", view);

		glUseProgram(planet_gl_program_id);
		shader::set_mat4(planet_gl_program_id, "projection", projection);
		shader::set_mat4(planet_gl_program_id, "view", view);

		// draw planet
		glm::mat4 model{ 1.0f };
		model = glm::translate(model, glm::vec3(0.0f, -3.0f, 0.0f));
		model = glm::scale(model, glm::vec3(4.0f, 4.0f, 4.0f));
		shader::set_mat4(planet_gl_program_id, "model", model);
		loaded_planet->draw(planet_gl_program_id);


		// draw asteriod
		glUseProgram(asteriods_gl_program_id);
		shader::set_int(asteriods_gl_program_id, "texture_diffuse_1", 0);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, loaded_planet->get_loaded_textures()[0].second.id_);

		std::size_t no_meshed_in_asteroid{ loaded_planet->get_loaded_textures().size() };
		for (auto meshed_in_rock_itr_beg = loaded_planet->get_meshes().cbegin(); 
			meshed_in_rock_itr_beg != loaded_planet->get_meshes().cend(); ++meshed_in_rock_itr_beg)
		{
			glBindVertexArray((*meshed_in_rock_itr_beg)->get_VAO());
			glDrawElementsInstanced(GL_TRIANGLES, (*meshed_in_rock_itr_beg)->get_indices().size(), GL_UNSIGNED_INT, 0, amount);

			glBindVertexArray(0);
		}



		// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
		// -------------------------------------------------------------------------------
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	// glfw: terminate, clearing all previously allocated GLFW resources.
	// ------------------------------------------------------------------
	glfwTerminate();
	return 0;
}
<file_sep>/asteriods/asteriods/model.hpp
#ifndef __MODEL_HPP__
#define __MODEL_HPP__

#include <glad/glad.h> 

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>

#include "mesh.hpp"
#include "stb_image/stb_image.h"

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <map>
#include <vector>
#include <list>
#include <cassert>


class model_loader final
{
private:
	std::list<std::shared_ptr<mesh>> meshes_;
	std::vector<std::pair<std::basic_string<char>, texture>> loaded_texture_{};

	std::basic_string<char> texture_file_dir_{};
public:
	model_loader() = default;
	model_loader(const model_loader&) = delete;
	model_loader& operator=(const model_loader&) = delete;


	const std::list<std::shared_ptr<mesh>>& get_meshes()const noexcept
	{
		return (this->meshes_);
	}

	const std::vector<std::pair<std::basic_string<char>, texture>>& get_loaded_textures()
	{
		return (this->loaded_texture_);
	}


	void load_model(const std::basic_string<char>& model_file)
	{
		assert(!model_file.empty());

		if (model_file.find_last_not_of('\\') == std::string::npos)
		{
			assert(false);
		}

		texture_file_dir_ = model_file.substr(0, model_file.find_last_of('\\'));

		Assimp::Importer importer{};
		const aiScene* scene = importer.ReadFile(model_file, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);

		if (!scene)
		{
			std::cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << std::endl;
			return;
		}

		// check for errors
		if (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
		{
			std::cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << std::endl;
			return;
		}

		// process ASSIMP's root node recursively
		process_node(scene->mRootNode, scene);
	}

	void load_vertices_data()
	{
		for (const auto& shared_mesh : meshes_)
		{
			shared_mesh->bind_VAO_VBO_EBO();
		}
	}


	// draw model.
	inline void draw(GLuint program_id)
	{
		glUseProgram(program_id);

		for (const auto& shared_mesh : meshes_)
		{
			shared_mesh->bind_texture(program_id);
		}
	}

private:

	// 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 process_node(aiNode *ai_node, const aiScene *ai_scene)
	{
		// process each mesh located at the current node
		for (std::size_t index = 0; index < ai_node->mNumMeshes; ++index)
		{
			// 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_ptr = ai_scene->mMeshes[ai_node->mMeshes[index]];
			meshes_.push_back(process_mesh(mesh_ptr, ai_scene));
		}
		// after we've processed all of the meshes (if any) we then recursively process each of the children nodes
		for (std::size_t index = 0; index < ai_node->mNumChildren; ++index)
		{
			process_node(ai_node->mChildren[index], ai_scene);
		}
	}


	std::shared_ptr<mesh> process_mesh(const aiMesh* const ai_mesh, const aiScene* const ai_scene)
	{
		std::vector<vertex> vertices{};
		std::vector<GLuint> indices{};
		std::list<texture> textures{};

		for (std::size_t index = 0; index < ai_mesh->mNumVertices; ++index)
		{
			vertex temp_vertex{};
			glm::vec3 position{};
			glm::vec3 normal{};

			// positions
			position.x = ai_mesh->mVertices[index].x;
			position.y = ai_mesh->mVertices[index].y;
			position.z = ai_mesh->mVertices[index].z;

			// normals
			normal.x = ai_mesh->mNormals[index].x;
			normal.y = ai_mesh->mNormals[index].y;
			normal.z = ai_mesh->mNormals[index].z;

			temp_vertex.position_ = position;
			temp_vertex.normal_ = normal;


			if (ai_mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
			{
				glm::vec2 texcoord{};
				// 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).
				texcoord.x = ai_mesh->mTextureCoords[0][index].x;
				texcoord.y = ai_mesh->mTextureCoords[0][index].y;
				temp_vertex.texcoord_ = texcoord;
			}
			else
			{
				temp_vertex.texcoord_ = glm::vec2{ 0, 0 };
			}

			// tangent
			glm::vec3 tangent{};
			tangent.x = ai_mesh->mTangents[index].x;
			tangent.y = ai_mesh->mTangents[index].y;
			tangent.z = ai_mesh->mTangents[index].z;
			temp_vertex.tangent_ = tangent;

			// bitangent
			glm::vec3 bitangent{};
			bitangent.x = ai_mesh->mBitangents[index].x;
			bitangent.y = ai_mesh->mBitangents[index].y;
			bitangent.z = ai_mesh->mBitangents[index].z;
			temp_vertex.bitangent_ = bitangent;

			vertices.push_back(temp_vertex);
		}

		// now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
		for (std::size_t index = 0; index < ai_mesh->mNumFaces; ++index)
		{
			aiFace face{ ai_mesh->mFaces[index] };

			for (std::size_t EBO_index = 0; EBO_index < face.mNumIndices; ++EBO_index)
			{
				indices.push_back(face.mIndices[EBO_index]);
			}
		}

		// process materials
		aiMaterial* material = ai_scene->mMaterials[ai_mesh->mMaterialIndex];

		// 1. diffuse maps
		std::vector<texture> diffuse_texture{ load_material_texture(material, aiTextureType_DIFFUSE, texture_type::diffuse_type) };
		textures.insert(textures.end(), diffuse_texture.begin(), diffuse_texture.end());

		// 2. specular maps
		std::vector<texture> specular_texture{ load_material_texture(material, aiTextureType_SPECULAR, texture_type::specular_type) };
		textures.insert(textures.end(), specular_texture.begin(), specular_texture.end());

		// 3. normal maps
		std::vector<texture> normal_texture{ load_material_texture(material, aiTextureType_HEIGHT,  texture_type::height_type) };
		textures.insert(textures.end(), normal_texture.begin(), normal_texture.end());

		// 4. height maps
		std::vector<texture> height_texture{ load_material_texture(material, aiTextureType_AMBIENT, texture_type::ambient_type) };
		textures.insert(textures.end(), height_texture.begin(), height_texture.end());

		std::shared_ptr<mesh> shared_mesh{ std::make_shared<mesh>() };
		shared_mesh->add_vertices(vertices);
		shared_mesh->add_indices(indices);
		shared_mesh->add_textures(textures);

		return shared_mesh;
	}


	std::vector<texture> load_material_texture(aiMaterial * const ai_material, aiTextureType type, texture_type the_type)
	{
		std::vector<texture> textures{};
		for (std::size_t index = 0; index < ai_material->GetTextureCount(type); ++index)
		{
			aiString texture_file_name{};
			ai_material->GetTexture(type, index, &texture_file_name);

			// check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
			bool skip{ false };
			for (std::size_t load_texture_index = 0; load_texture_index < loaded_texture_.size(); ++load_texture_index)
			{
				if (std::strcmp(loaded_texture_[load_texture_index].first.c_str(), texture_file_name.C_Str()) == 0)
				{
					textures.push_back(loaded_texture_[load_texture_index].second);
					skip = true; // a texture with the same filepath has already been loaded, continue to next one. (optimization)
					break;
				}
			}

			if (!skip)
			{   // if texture hasn't been loaded already, load it
				texture temp_texture{};
				temp_texture.id_ = model_loader::load_texture_from_file(texture_file_name.C_Str(), texture_file_dir_);
				temp_texture.type_ = the_type;
				textures.push_back(temp_texture);
				loaded_texture_.emplace_back(std::basic_string<char>{texture_file_name.C_Str()}, temp_texture);  // store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures.
			}
		}
		return textures;
	}

	// gamma unused temporarily.
	static GLuint load_texture_from_file(const std::basic_string<char>& file_name, const std::basic_string<char>& file_path, bool gamma = false)
	{
		std::basic_string<char> file_path_name{ file_path + '\\' + file_name };

		GLuint texture_id{};
		glGenTextures(1, &texture_id);

		int width{}, height{}, nrComponents{};
		unsigned char *data{ stbi_load(file_path_name.c_str(), &width, &height, &nrComponents, 0) };

		if (data)
		{
			GLenum format{};
			if (nrComponents == 1)
				format = GL_RED;
			else if (nrComponents == 3)
				format = GL_RGB;
			else if (nrComponents == 4)
				format = GL_RGBA;

			glBindTexture(GL_TEXTURE_2D, texture_id);
			glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
			glGenerateMipmap(GL_TEXTURE_2D);

			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);

			stbi_image_free(data);
		}
		else
		{
			std::cout << "Texture failed to load at path: " << file_path << std::endl;
			stbi_image_free(data);
		}

		return texture_id;
	}


};


#endif // !__MODEL_HPP__
<file_sep>/geometry_shader/geometry_shader/main.cpp
﻿
#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

#include "shader.hpp"

// settings
const unsigned int SCR_WIDTH = 1280;
const unsigned int SCR_HEIGHT = 720;

int main()
{
	// glfw: initialize and configure
	// ------------------------------
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif //__APPLE__


		// glfw window creation
	// --------------------
	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", nullptr, nullptr);
	glfwMakeContextCurrent(window);

	// glad: load all OpenGL function pointers
	// ---------------------------------------
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// configure global opengl state
	// -----------------------------
	glEnable(GL_DEPTH_TEST);


	GLuint vertex_shader_id{ shader::create("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\geometry_shader\\geometry_shader\\glsl\\vertex_shader.glsl", shader_type::vertex_shader) };
	GLuint geometry_shader_id{ shader::create("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\geometry_shader\\geometry_shader\\glsl\\geometry_shader.glsl", shader_type::geometry_shader) };
	GLuint fragment_shader_id{ shader::create("C:\\Users\\y\\Documents\\Visual Studio 2017\\Projects\\opengl_demo\\geometry_shader\\geometry_shader\\glsl\\fragment_shader.glsl", shader_type::fragment_shader) };

	GLuint program_id{ glCreateProgram() };
	glAttachShader(program_id, vertex_shader_id);
	glAttachShader(program_id, geometry_shader_id);
	glAttachShader(program_id, fragment_shader_id);
	glLinkProgram(program_id);
	glDeleteShader(vertex_shader_id);
	glDeleteShader(fragment_shader_id);
	glDeleteShader(geometry_shader_id);
	shader::checkout_shader_state(program_id, shader_type::program);

	const float points[]{
	-0.5f,  0.5f, 1.0f, 0.0f, 0.0f, // top-left
	 0.5f,  0.5f, 0.0f, 1.0f, 0.0f, // top-right
	 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // bottom-right
	-0.5f, -0.5f, 1.0f, 1.0f, 0.0f  // bottom-left
	};

	GLuint VAO{};
	GLuint VBO{};

	glGenVertexArrays(1, &VAO);
	glBindVertexArray(VAO);

	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof points, points, GL_STATIC_DRAW);


	GLuint offset{ 0 };
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 2 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	glBindVertexArray(0);

	while (!glfwWindowShouldClose(window))
	{
		glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glUseProgram(program_id);


		glBindVertexArray(VAO);
		glDrawArrays(GL_POINTS, 0, 4);

		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);

	glfwTerminate();
	return 0;
}
<file_sep>/framebuffer_primary/framebuffer_primary/main.cpp
#define GLM_FORCE_CXX14
#define GLM_FORCE_INTRINSICS
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

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

#include <vector>
#include <atomic>
#include <iostream>

#include "shader.hpp"
#include "stb_image/stb_image.h"

static  const int WIDTH{ 800 };
static  const int HEIGHT{ 600 };

// lighting.
static const glm::vec3 light_pos{ 1.2f, 1.0f, 2.0f };

// camera
static glm::vec3 camera_pos{ 0.0f, 0.0f, 3.0f };
static glm::vec3 camera_front{};
static glm::vec3 camera_up{ 0.0f, 1.0f, 0.0f };
static glm::vec3 camera_right{};
static glm::vec3 world_up = camera_up;
static float delta_time{};
static float last_frame{};

//mouse and scroll
static float field_of_view{ 45.0f };
static std::atomic<bool> first_click{ true };
static float lastX{ WIDTH / 2.f };
static float lastY{ HEIGHT / 2.f };
static float pitch{ 0.0f };
static float yaw{ -90.f };
static float speed{ 2.5f };
static float sensitivity{ 0.05f };

static void update_camera_vectors()
{
	// Calculate the new Front vector
	glm::vec3 front{};
	front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	front.y = std::sin(glm::radians(pitch));
	front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	camera_front = glm::normalize(front);

	// Also re-calculate the Right and Up vector
	camera_right = glm::normalize(glm::cross(camera_front, world_up)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
	camera_up = glm::normalize(glm::cross(camera_right, camera_front));
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
static void process_input(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);

	float camera_speed{ speed * delta_time };

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
	{
		camera_pos += (camera_front * camera_speed);
	}

	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
	{
		camera_pos -= camera_speed * camera_front;
	}

	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
	{
		camera_pos -= camera_right * camera_speed;
	}

	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
	{
		camera_pos += camera_right * camera_speed;
	}
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
static void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
	// make sure the viewport matches the new window dimensions; note that width and
	// height will be significantly larger than specified on retina displays.
	glViewport(0, 0, width, height);
}


// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
static void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
	if (first_click.load())
	{
		lastX = xpos;
		lastY = ypos;
		first_click.store(false);
	}

	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
	lastX = xpos;
	lastY = ypos;

	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;

	update_camera_vectors();
}


// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
	if (field_of_view >= 1.0f && field_of_view <= 45.0f)
	{
		field_of_view -= yoffset;
	}

	if (field_of_view <= 1.f)
	{
		field_of_view = 1.0f;
	}

	if (field_of_view >= 45.0f)
	{
		field_of_view = 45.0f;
	}
}


static GLuint load_texture(const char * path)
{
	GLuint texture_id{};
	glGenTextures(1, &texture_id);

	int width{}, height{}, nrComponents{};
	unsigned char *data{ stbi_load(path, &width, &height, &nrComponents, 0) };

	if (data)
	{
		GLenum format{};
		if (nrComponents == 1)
			format = GL_RED;
		else if (nrComponents == 3)
			format = GL_RGB;
		else if (nrComponents == 4)
			format = GL_RGBA;

		glBindTexture(GL_TEXTURE_2D, texture_id);
		glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);

		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);

		stbi_image_free(data);
	}
	else
	{
		std::cout << __FUNCTION__ << "   " << __LINE__ << "  " << "Texture failed to load at path : " << path << std::endl;
		stbi_image_free(data);
	}

	data = nullptr;
	return texture_id;
}


int main()
{
	// glfw: initialize and configure
// ------------------------------
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// glfw window creation
	// --------------------
	GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}


	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	// tell GLFW to capture our mouse
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	// glad: load all OpenGL function pointers
	// ---------------------------------------
	if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// update camera parameters.
	update_camera_vectors();

	// configure global opengl state
	glEnable(GL_DEPTH_TEST);


	GLuint vertex_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\framebuffer_primary\\framebuffer_primary\\glsl\\vertex_shader.glsl", shader_type::vertex_shader) };
	GLuint fragment_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\framebuffer_primary\\framebuffer_primary\\glsl\\fragment_shader.glsl", shader_type::fragment_shader) };

	GLuint program_id{ glCreateProgram() };
	glAttachShader(program_id, vertex_shader_id);
	glAttachShader(program_id, fragment_shader_id);
	glLinkProgram(program_id);
	glDeleteShader(vertex_shader_id);
	glDeleteShader(fragment_shader_id);
	shader::checkout_shader_state(program_id, shader_type::program);


	GLuint quad_vertex_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\framebuffer_primary\\framebuffer_primary\\glsl\\quad_framebuffer_vertex_shader.glsl", shader_type::vertex_shader) };
	GLuint quad_fragment_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\framebuffer_primary\\framebuffer_primary\\glsl\\quad_framebuffer_fragment_shader.glsl", shader_type::fragment_shader) };

	GLuint quad_program_id{ glCreateProgram() };
	glAttachShader(quad_program_id, quad_vertex_shader_id);
	glAttachShader(quad_program_id, quad_fragment_shader_id);
	glLinkProgram(quad_program_id);
	glDeleteShader(quad_vertex_shader_id);
	glDeleteShader(quad_fragment_shader_id);
	shader::checkout_shader_state(quad_program_id, shader_type::program);



	// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
	const float cubes_vertices[]{
		// positions          // texture Coords
		-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
	};

	const float floor_vertices[]{
		// positions          // texture Coords 
		 5.0f, -0.5f,  5.0f,  2.0f, 0.0f,
		-5.0f, -0.5f,  5.0f,  0.0f, 0.0f,
		-5.0f, -0.5f, -5.0f,  0.0f, 2.0f,

		 5.0f, -0.5f,  5.0f,  2.0f, 0.0f,
		-5.0f, -0.5f, -5.0f,  0.0f, 2.0f,
		 5.0f, -0.5f, -5.0f,  2.0f, 2.0f
	};


	const float quad_vertices[]{ // vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
		// positions   // texCoords
		-1.0f,  1.0f,  0.0f, 1.0f,
		-1.0f, -1.0f,  0.0f, 0.0f,
		 1.0f, -1.0f,  1.0f, 0.0f,

		-1.0f,  1.0f,  0.0f, 1.0f,
		 1.0f, -1.0f,  1.0f, 0.0f,
		 1.0f,  1.0f,  1.0f, 1.0f
	};


	GLuint cubes_VAO{};
	GLuint cubes_VBO{};

	glGenVertexArrays(1, &cubes_VAO);
	glGenBuffers(1, &cubes_VBO);

	glBindVertexArray(cubes_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, cubes_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(cubes_vertices), cubes_vertices, GL_STATIC_DRAW);

	std::size_t offset{ 0 };
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 3 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	// notice that.
	glBindVertexArray(0);



	GLuint floor_VAO{};
	GLuint floor_VBO{};

	glGenVertexArrays(1, &floor_VAO);
	glGenBuffers(1, &floor_VBO);

	glBindVertexArray(floor_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, floor_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(floor_vertices), floor_vertices, GL_STATIC_DRAW);

	offset = 0;
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 3 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	// notice that
	glBindVertexArray(0);


	// transparent VAO
	GLuint quad_VAO{}, quad_VBO{};
	glGenVertexArrays(1, &quad_VAO);
	glGenBuffers(1, &quad_VBO);

	glBindVertexArray(quad_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, quad_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(quad_vertices), quad_vertices, GL_STATIC_DRAW);

	offset = 0;
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 2 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast<void*>(offset));

	// notice that.
	glBindVertexArray(0);


	GLuint wall_texture_id{ load_texture("C:\\Users\\shihua\\source\\repos\\opengl_demo\\framebuffer_primary\\framebuffer_primary\\image\\container.jpg") };
	GLuint floor_texture_id{ load_texture("C:\\Users\\shihua\\source\\repos\\opengl_demo\\framebuffer_primary\\framebuffer_primary\\image\\metal.png") };


	glUseProgram(program_id);
	shader::set_int(program_id, "texture_1", 0);


	glUseProgram(quad_program_id);
	shader::set_int(quad_fragment_shader_id, "screen_texture", 0);

	// generate framebuffer.
	GLuint framebuffer_id{};
	glGenFramebuffers(1, &framebuffer_id);
	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);

	GLuint color_texture_buffer_id{};
	glGenTextures(1, &color_texture_buffer_id);
	glBindTexture(GL_TEXTURE_2D, color_texture_buffer_id);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_texture_buffer_id, 0);

	GLuint render_buffer_id{};
	glGenRenderbuffers(1, &render_buffer_id);
	glBindRenderbuffer(GL_RENDERBUFFER, render_buffer_id);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, WIDTH, HEIGHT); // use a single renderbuffer object for both a depth AND stencil buffer.
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, render_buffer_id); // now actually attach it

		// now that we actually created the framebuffer and added all attachments we want to check if it is actually complete now
	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
	}
	glBindFramebuffer(GL_FRAMEBUFFER, 0); // set our defined framebuffer as default.


	while (!glfwWindowShouldClose(window))
	{
		double current_time{ glfwGetTime() };
		delta_time = current_time - last_frame;
		last_frame = current_time;

		process_input(window);

		// bind to framebuffer and draw scene as we normally would to color texture 
		glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
		glEnable(GL_DEPTH_TEST); // enable depth testing;
													// disable depth testing before render screen-space quad.

		glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


		glUseProgram(program_id);
		glm::mat4 model{ 1.0f };
		glm::mat4 view{ glm::lookAt(camera_pos, camera_pos + camera_front, camera_up) };
		glm::mat4 projection{ glm::perspective(glm::radians(field_of_view), WIDTH * 1.0f / HEIGHT * 1.0f, 0.1f, 100.0f) };
		shader::set_mat4(program_id, "view", view);
		shader::set_mat4(program_id, "projection", projection);

		// cubes
		glBindVertexArray(cubes_VAO);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, wall_texture_id);
		model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f));
		shader::set_mat4(program_id, "model", model);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		model = glm::mat4{ 1.0f };
		model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f));
		shader::set_mat4(program_id, "model", model);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		// floor
		glBindVertexArray(floor_VAO);
		glBindTexture(GL_TEXTURE_2D, floor_texture_id);
		shader::set_mat4(program_id, "model", model);
		glDrawArrays(GL_TRIANGLES, 0, 6);

		glBindVertexArray(0);

		// now bind back to default framebuffer and draw a quad plane with the attached framebuffer color texture
		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glDisable(GL_DEPTH_TEST); // disable depth test so screen-space quad isn't discarded due to depth test.
		// clear all relevant buffers
		glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // set clear color to white (not really necessery actually, since we won't be able to see behind the quad anyways)
		glClear(GL_COLOR_BUFFER_BIT);


		glUseProgram(quad_program_id);
		glBindVertexArray(quad_VAO);
		glBindTexture(GL_TEXTURE_2D, color_texture_buffer_id);	// use the color attachment texture as the texture of the quad plane
		glDrawArrays(GL_TRIANGLES, 0, 6);


		// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	// glfw: terminate, clearing all previously allocated GLFW resources.
	// ------------------------------------------------------------------
	glDeleteVertexArrays(1, &cubes_VAO);
	glDeleteVertexArrays(1, &floor_VAO);
	glDeleteVertexArrays(1, &quad_VAO);
	glDeleteBuffers(1, &cubes_VBO);
	glDeleteBuffers(1, &floor_VBO);
	glDeleteBuffers(1, &quad_VBO);

	glfwTerminate();
	return 0;
}<file_sep>/README.md
## demos for learning OpenGL.

 these projects for learning opengl(glfw+glad).

- if you want to run this demo, must change the address of pictures to yours.

[![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu)
[![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE)
<file_sep>/hello_opengl/main.cpp

#include <iostream>

extern "C"
{
#include <glad/glad.h>
#include <GLFW/glfw3.h>
}

static constexpr int WIDTH{ 800 };
static constexpr int HEIGHT{ 600 };

static void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    std::cout << "call back." << std::endl;
    glViewport(0, 0, width, height);
}

static void process_input(GLFWwindow* window)
{
    if(glfwGetKey(window, GLFW_KEY_ENTER) == GLFW_PRESS)
    {
        std::cout << "you pressed ENTER!" << std::endl;
        glfwSetWindowShouldClose(window, true);
    }
}


int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    GLFWwindow* window{ glfwCreateWindow(800, 600, "Learn OpenGL", nullptr, nullptr) };

    if(!window)
    {
        std::cout << "failed to create a window!" << std::endl;
        glfwTerminate();

        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    //glad: load all opengl functions.
    if(!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
    {
        std::cout << "failed to load functions!" << std::endl;

        return -1;
    }

    while(!glfwWindowShouldClose(window))
    {
        process_input(window);

        glClearColor(0.2, 0.3, 0.3, 0);
        glClear(GL_COLOR_BUFFER_BIT);
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate(); //release resource.

    return 0;
}
<file_sep>/mesh/mesh/mesh.hpp
#ifndef  __BASIC_TYPE_H__
#define __BASIC_TYPE_H__

#include <glad/glad.h> // holds all OpenGL type declarations

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>

#include <list>
#include <string>
#include <vector>
#include <cstddef>

enum class texture_type
{
	ambient_type,
	diffuse_type,
	specular_type,
	height_type
};

struct vertex
{
	glm::vec3 position_;
	glm::vec3 normal_;
	glm::vec2 texcoord_;

	glm::vec3 tangent_;
	glm::vec3 bitangent_;
};


struct texture
{
	std::size_t id_;
	texture_type type_;
};



class mesh final
{
private:
	std::vector<vertex> vertices_;
	std::vector<GLuint> indices_;
	std::list<texture> textures_;

	GLuint VAO_;
	GLuint VBO_;
	GLuint EBO_;

public:
	mesh() = default;
	mesh(const mesh&) = delete;
	mesh& operator=(const mesh&) = delete;

	inline void add_vertices(const std::vector<vertex>& vertices)noexcept
	{
		this->vertices_ = vertices;
	}

	inline const std::vector<vertex>& get_vertices(const std::vector<vertex>& vertices)const noexcept
	{
		return this->vertices_;
	}

	inline void add_indices(const std::vector<GLuint>& indices)noexcept
	{
		this->indices_ = indices;
	}

	inline const std::vector<GLuint>& get_indices()const noexcept
	{
		return this->indices_;
	}

	inline void add_textures(const std::list<texture>& textures)noexcept
	{
		this->textures_ = textures;
	}

	const const std::list<texture> get_textures()const noexcept
	{
		return this->textures_;
	}

	const std::size_t& get_VAO()const noexcept
	{
		return this->VAO_;
	}

	const std::size_t& get_VBO()const noexcept
	{
		return this->VBO_;
	}

	const std::size_t& get_EBO()const noexcept
	{
		return this->EBO_;
	}

	void bind_texture(std::size_t program_id)
	{
		std::size_t number_of_textures{ this->textures_.size() };
		auto texture_itr_beg{ this->textures_.cbegin() };

		std::size_t diffuse_no{ 1 };
		std::size_t specular_no{ 1 };
		std::size_t ambient_no{ 1 };
		std::size_t height_no{ 1 };

		for (std::size_t index = 0; index < number_of_textures; ++index)
		{
			const texture& ref_texture = *texture_itr_beg;

			if (ref_texture.type_ == texture_type::ambient_type)
			{
				glUniform1i(glGetUniformLocation(program_id, ("ambient_texture_" + std::to_string(ambient_no++)).c_str()), index);
			}

			if (ref_texture.type_ == texture_type::diffuse_type)
			{
				glUniform1i(glGetUniformLocation(program_id, ("diffuse_texture_" + std::to_string(diffuse_no++)).c_str()), index);
			}

			if (ref_texture.type_ == texture_type::specular_type)
			{
				glUniform1i(glGetUniformLocation(program_id, ("specular_texture_" + std::to_string(specular_no++)).c_str()), index);
			}

			if (ref_texture.type_ == texture_type::height_type)
			{
				glUniform1i(glGetUniformLocation(program_id, ("height_texture_" + std::to_string(height_no++)).c_str()), index);

			}

			glActiveTexture(GL_TEXTURE0 + index);
			glBindTexture(GL_TEXTURE_2D, ref_texture.id_);

			++texture_itr_beg;
		}

		// draw mesh
		glBindVertexArray(VAO_);
		glDrawElements(GL_TRIANGLES, indices_.size(), GL_UNSIGNED_INT, 0);

		// always good practice to set everything back to defaults once configured.
		glBindVertexArray(0);
		glActiveTexture(GL_TEXTURE0);

	}

	void bind_VAO_VBO_EBO()
	{
		glGenVertexArrays(1, &(VAO_));
		glGenBuffers(1, &VBO_);
		glGenBuffers(1, &EBO_);

		glBindVertexArray(VAO_);

		glBindBuffer(GL_ARRAY_BUFFER, VBO_);
		glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(vertex), &vertices_[0], GL_STATIC_DRAW);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO_);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_.size() * sizeof(GLuint), &indices_[0], GL_STATIC_DRAW);

		std::size_t offset{ 0 };
		glEnableVertexAttribArray(0);
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), reinterpret_cast<void*>(offset));

		offset = offsetof(vertex, normal_);
		glEnableVertexAttribArray(1);
		glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), reinterpret_cast<void*>(offset));

		offset = offsetof(vertex, texcoord_);
		glEnableVertexAttribArray(2);
		glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), reinterpret_cast<void*>(offset));

		offset = offsetof(vertex, tangent_);
		glEnableVertexAttribArray(3);
		glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), reinterpret_cast<void*>(offset));

		offset = offsetof(vertex, bitangent_);
		glEnableVertexAttribArray(4);
		glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), reinterpret_cast<void*>(offset));

		//notice here:
		glBindVertexArray(0);
	}
};





#endif // __BASIC_TYPE_H__

<file_sep>/lightcaster-flashlight/lightcaster-flashlight/main.cpp
#define GLM_FORCE_CXX14
#define GLM_FORCE_INTRINSICS
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

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

#include <iostream>

#include "stb_image/stb_image.h"

static constexpr const int WIDTH{ 800 };
static constexpr const int HEIGHT{ 600 };

// lighting.
static const glm::vec3 light_pos{ 1.2f, 1.0f, 2.0f };

// camera
static glm::vec3 cameraPos{ 0.0f, 0.0f, 3.0f };
static glm::vec3 cameraFront{};
static glm::vec3 cameraUp{ 0.0f, 1.0f, 0.0f };
static glm::vec3 cameraRight{};
static glm::vec3 worldUp = cameraUp;
static float delta_time{};
static float last_frame{};

//mouse and scroll
static float field_of_view{ 45.0f };
static bool firstMouse{ true };
static float lastX{ WIDTH / 2.f };
static float lastY{ HEIGHT / 2.f };
static float pitch{ 0.0f };
static float yaw{ -90.f };
static float speed{ 2.5f };
static float sensitivity{ 0.05f };

static void updateCameraVectors()
{
	// Calculate the new Front vector
	glm::vec3 front{};
	front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	front.y = std::sin(glm::radians(pitch));
	front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	cameraFront = glm::normalize(front);

	// Also re-calculate the Right and Up vector
	cameraRight = glm::normalize(glm::cross(cameraFront, worldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
	cameraUp = glm::normalize(glm::cross(cameraRight, cameraFront));
}

static constexpr const char *cubeVertexShaderSource{
	"#version 330 core\n"
	"layout (location = 0) in vec3 aPos;\n"
	"layout (location = 1) in vec3 aNormal;\n"
	"layout (location = 2) in vec2 aTexCoords;\n"

	"out vec3 FragPos;\n"
	"out vec3 Normal;\n"
	"out vec2 TexCoords;\n"

	"uniform mat4 model;\n"
	"uniform mat4 view;\n"
	"uniform mat4 projection;\n"

	"void main()\n"
	"{\n"
	"    FragPos = vec3(model * vec4(aPos, 1.0));\n"
	"    TexCoords = aTexCoords;\n"

	// must use Normal Matrix, it can keep NU Scale right.
	"    Normal = mat3(transpose(inverse(model))) * aNormal;\n" //transpose(inverse(model)) creatr Normal Matrix.

	"    gl_Position = projection * view * vec4(FragPos, 1.0);\n"
	"}" };
static constexpr const char *cubeFragmentShaderSource{
	"#version 330 core\n"
	"out vec4 FragColor;\n"

	"in vec3 Normal;\n"
	"in vec3 FragPos;\n"
	"in vec2 TexCoords;\n"

	"struct Material\n"
	"{\n"
	"    sampler2D diffuse_;\n" //ambient color is the same with diffuse color, ususally.
	"    sampler2D specular_;\n"
	"    float shininess_;\n"
	"};\n"

	"struct Light\n"
	"{\n"
	// why here are camera_xxx?
	//we are simulating flashlight, so camera is light source.
	"    vec3  camera_position_;\n"
	"    vec3  camera_front_;\n"
	"     float  cutoff_;\n"
	"	  float outer_cutoff_;\n"

	"    vec3 ambient_;\n"
	"    vec3 diffuse_;\n"
	"    vec3 specular_;\n"

	"   float constant_;\n"
	"   float linear_;\n"
	"   float quadratic_;\n"
	"};\n"

	"uniform Material material;\n"
	"uniform Light light;\n"

	"uniform vec3 cameraPos;\n" //camera pos.

	"void main()\n"
	"{\n"

	// ambient
	"    vec3 ambientVec = light.ambient_ * texture(material.diffuse_, TexCoords).rgb;\n"

	// diffuse
	"    vec3 normalVec = normalize(Normal);\n"
	"    vec3 lightDir = normalize(light.camera_position_ - FragPos);\n"
	"    float diffuseValue = max(dot(normalVec, lightDir), 0.0);\n"
	"    vec3 diffuseVec = light.diffuse_  * diffuseValue * texture(material.diffuse_, TexCoords).rgb; \n"

	// specular
	"    vec3 viewDir = normalize(cameraPos - FragPos);\n"
	"    vec3 reflectDir = reflect(-lightDir, normalVec);\n"
	"    float specularValue = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess_);\n"
	"    vec3 specularVec = light.specular_ * specularValue * texture(material.specular_, TexCoords).rgb;\n"

	// flashlight( soft edge )
	"	 float thea = dot(lightDir, -light.camera_front_);\n"
	"	 float epsilon = light.cutoff_ - light.outer_cutoff_;\n"
	"	 float intensity = clamp((thea - light.outer_cutoff_) / epsilon, 0.0, 1.0);\n"
	"	 diffuseVec *= intensity;\n"
	"	 specularVec *= intensity;\n"

	// attenuation
	"	 float distance = length(light.camera_position_ - FragPos);\n"
	"	 float attenuation = 1.0 / (light.constant_ + light.linear_ * distance + light.quadratic_ * (distance * distance));\n"
	"	 ambientVec *= attenuation;\n"
	"	 diffuseVec *= attenuation;\n"
	"	 specularVec *= attenuation;\n"


	// here ambient need not attenuationValue.
	"    vec3 finalColor = diffuseVec + specularVec  + ambientVec;\n"
	"    FragColor = vec4(finalColor, 1.0);\n"
	"}" };

static constexpr const char *lampVertexShaderSource{
	"#version 330 core\n"
	"layout (location = 0) in vec3 aPos;\n"
	"uniform mat4 model;\n"
	"uniform mat4 view;\n"
	"uniform mat4 projection;\n"
	"void main()\n"
	"{\n"
	"	gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
	"}" };

static constexpr const char *lampFragmentShaderSource{
	"#version 330 core\n"
	"out vec4 FragColor;\n"
	"void main()\n"
	"{\n"
	"    FragColor = vec4(1.0);\n" // set alle 4 vector values to 1.0
	"}" };

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
static void processInput(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);

	float camera_speed{ speed * delta_time };

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
	{
		cameraPos += (cameraFront * camera_speed);
	}

	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
	{
		cameraPos -= camera_speed * cameraFront;
	}

	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
	{
		cameraPos -= cameraRight * camera_speed;
	}

	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		cameraPos += cameraRight * camera_speed;
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
	// make sure the viewport matches the new window dimensions; note that width and
	// height will be significantly larger than specified on retina displays.
	glViewport(0, 0, width, height);
}

// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
static void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
	if (firstMouse)
	{
		lastX = xpos;
		lastY = ypos;
		firstMouse = false;
	}

	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
	lastX = xpos;
	lastY = ypos;

	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;

	updateCameraVectors();
}

// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
	if (field_of_view >= 1.0f && field_of_view <= 45.0f)
	{
		field_of_view -= yoffset;
	}

	if (field_of_view <= 1.f)
	{
		field_of_view = 1.0f;
	}

	if (field_of_view >= 45.0f)
	{
		field_of_view = 45.0f;
	}
}

int main()
{
	// glfw: initialize and configure
	// ------------------------------
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// glfw window creation
	// --------------------
	GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}

	glfwMakeContextCurrent(window);

	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	// tell GLFW to capture our mouse
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	// glad: load all OpenGL function pointers
	// ---------------------------------------
	if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// configure global opengl state
	// -----------------------------
	glEnable(GL_DEPTH_TEST);

	// notice that: count camera front/up/right vectors.
	updateCameraVectors();

	// cube shaders.
	GLuint cubeVertexShader{};
	GLuint cubeFragmentShader{};

	cubeVertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(cubeVertexShader, 1, &cubeVertexShaderSource, nullptr);
	glCompileShader(cubeVertexShader);

	GLint success{};
	glGetShaderiv(cubeVertexShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetShaderInfoLog(cubeVertexShader, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	cubeFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(cubeFragmentShader, 1, &cubeFragmentShaderSource, nullptr);
	glCompileShader(cubeFragmentShader);

	success = 0;
	glGetShaderiv(cubeFragmentShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetShaderInfoLog(cubeFragmentShader, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	GLuint cubeProgramId{ glCreateProgram() };
	glAttachShader(cubeProgramId, cubeVertexShader);
	glAttachShader(cubeProgramId, cubeFragmentShader);
	glLinkProgram(cubeProgramId);

	success = 0;
	glGetProgramiv(cubeProgramId, GL_LINK_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetProgramInfoLog(cubeProgramId, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	// lamp shaders.
	GLuint lampVertexShader{};
	GLuint lampFragmentShader{};

	lampVertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(lampVertexShader, 1, &lampVertexShaderSource, nullptr);
	glCompileShader(lampVertexShader);

	success = 0;
	glGetShaderiv(lampVertexShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetShaderInfoLog(lampVertexShader, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	lampFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(lampFragmentShader, 1, &lampFragmentShaderSource, nullptr);
	glCompileShader(lampFragmentShader);

	success = 0;
	glGetShaderiv(lampFragmentShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetShaderInfoLog(lampFragmentShader, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	GLuint lampProgramId{ glCreateProgram() };
	glAttachShader(lampProgramId, lampVertexShader);
	glAttachShader(lampProgramId, lampFragmentShader);
	glLinkProgram(lampProgramId);

	success = 0;
	glGetProgramiv(lampProgramId, GL_LINK_STATUS, &success);
	if (!success)
	{
		GLchar infoLog[1024]{};
		glGetProgramInfoLog(lampProgramId, 1024, nullptr, infoLog);
		std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
			<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
	}

	// set up vertex data (and buffer(s)) and configure vertex attributes
	// ------------------------------------------------------------------
	const float vertices[]{
		// positions          // normals           // texture coords
		-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
		0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
		0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
		0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
		-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
		-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,

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

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

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

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

		-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
		0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
		0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
		0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
		-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
		-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f };

	// positions all containers
	const 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{};
	GLuint cubeVAO{};
	glGenVertexArrays(1, &cubeVAO);
	glGenBuffers(1, &VBO);

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

	GLint offset{ 0 };
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
	glEnableVertexAttribArray(0);

	offset = 3 * sizeof(float);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
	glEnableVertexAttribArray(1);

	offset = 6 * sizeof(float);
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
	glEnableVertexAttribArray(2);

	GLuint lampVAO{};
	glGenVertexArrays(1, &lampVAO);

	glBindVertexArray(lampVAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);

	offset = 0;
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
	glEnableVertexAttribArray(0);

	GLuint textureID{};
	glGenTextures(1, &textureID);

	int width{}, height{}, nrComponents{};
	unsigned char *data{ stbi_load("C:\\Users\\shihua\\source\\repos\\opengl_demo\\lightcaster-flashlight\\lightcaster-flashlight\\image\\container2.png",
								  &width, &height, &nrComponents, 0) };

	if (data)
	{
		GLenum format{};
		if (nrComponents == 1)
			format = GL_RED;
		else if (nrComponents == 3)
			format = GL_RGB;
		else if (nrComponents == 4)
			format = GL_RGBA;

		glBindTexture(GL_TEXTURE_2D, textureID);
		glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);

		// set texture wrapper.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

		// set texture filter.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		stbi_image_free(data);
	}
	else
	{
		std::cerr << "the path of image is wrong." << std::endl;
		stbi_image_free(data);
	}

	data = nullptr;

	GLuint textureID2{};
	glGenTextures(1, &textureID2);
	width = 0;
	height = 0;
	nrComponents = 0;

	data = stbi_load("C:\\Users\\shihua\\source\\repos\\opengl_demo\\lightcaster-flashlight\\lightcaster-flashlight\\image\\container2_specular.png",
		&width, &height, &nrComponents, 0);

	if (data)
	{
		GLenum format{};
		if (nrComponents == 1)
			format = GL_RED;
		else if (nrComponents == 3)
			format = GL_RGB;
		else if (nrComponents == 4)
			format = GL_RGBA;

		glBindTexture(GL_TEXTURE_2D, textureID2);
		glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);

		// set texture wrapper.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

		// set texture filter.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		stbi_image_free(data);
	}
	else
	{
		std::cerr << "the path of image is wrong." << std::endl;
		stbi_image_free(data);
	}

	data = nullptr;

	glUseProgram(cubeProgramId);
	GLint texture1Id{ glGetUniformLocation(cubeProgramId, "material.diffuse_") };
	glUniform1i(texture1Id, 0);
	GLint texture2Id{ glGetUniformLocation(cubeProgramId, "material.specular_") };
	glUniform1i(texture2Id, 1);

	while (!glfwWindowShouldClose(window))
	{
		// per-frame time logic
		float currentFrame = glfwGetTime();
		delta_time = currentFrame - last_frame;
		last_frame = currentFrame;

		glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// input
		// -----
		processInput(window);

		glUseProgram(cubeProgramId);

		// bind textures to specify uniform.
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, textureID);

		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, textureID2);

		GLint viewPosLoc{ glGetUniformLocation(cubeProgramId, "cameraPos") };
		glUniform3fv(viewPosLoc, 1, &cameraPos[0]);

		glm::vec3 lightAmbientColor{ 0.2f, 0.2f, 0.2f };
		glm::vec3 lightDiffuseColor{ 0.5f, 0.5f, 0.5f };
		glm::vec3 lightSpecularColor{ 1.0f, 1.0f, 1.0f };

		GLint cameraPosLoc{ glGetUniformLocation(cubeProgramId, "light.camera_position_") };
		glUniform3fv(cameraPosLoc, 1, &cameraPos[0]);

		GLint cameraDirectionLoc{ glGetUniformLocation(cubeProgramId, "light.camera_front_") };
		glUniform3fv(cameraDirectionLoc, 1, &cameraFront[0]);

		GLint cutOffLoc{ glGetUniformLocation(cubeProgramId, "light.cutoff_") };
		glUniform1f(cutOffLoc, std::cos(glm::radians(12.5f)));

		GLint outerCutOffLoc{ glGetUniformLocation(cubeProgramId, "light.outer_cutoff_") };
		glUniform1f(outerCutOffLoc, std::cos(glm::radians(17.5f)));

		GLint lightAmbientLoc{ glGetUniformLocation(cubeProgramId, "light.ambient_") };
		glUniform3f(lightAmbientLoc, 0.1f, 0.1f, 0.1f);

		GLint lightDiffuseLoc{ glGetUniformLocation(cubeProgramId, "light.diffuse_") };
		glUniform3f(lightDiffuseLoc, 0.8f, 0.8f, 0.8f);

		GLint lightSpecularLoc{ glGetUniformLocation(cubeProgramId, "light.specular_") };
		glUniform3f(lightSpecularLoc, 1.0, 1.0, 1.0);

		GLint lightConstantLoc{ glGetUniformLocation(cubeProgramId, "light.constant_") };
		glUniform1f(lightConstantLoc, 1.0f);

		GLint lightLinearLoc{ glGetUniformLocation(cubeProgramId, "light.linear_") };
		glUniform1f(lightLinearLoc, 0.09f);

		GLint lightQuadraticLoc{ glGetUniformLocation(cubeProgramId, "light.quadratic_") };
		glUniform1f(lightQuadraticLoc, 0.032f);

		GLint materialShininessLoc{ glGetUniformLocation(cubeProgramId, "material.shininess_") };
		glUniform1f(materialShininessLoc, 32.0f);

		// view/projection transformations
		glm::mat4 projection{ glm::perspective(glm::radians(field_of_view), static_cast<float>(WIDTH) / static_cast<float>(HEIGHT), 0.1f, 100.0f) };
		glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "projection"), 1, GL_FALSE, &projection[0][0]);

		// camera/view transformation
		glm::mat4 view{ 1.0f }; // make sure to initialize matrix to identity matrix first
		view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
		glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "view"), 1, GL_FALSE, &view[0][0]);

		// model
		glm::mat4 model{ 1.0f };
		glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "model"), 1, GL_FALSE, &model[0][0]);

		// render containers
		glBindVertexArray(cubeVAO);
		for (unsigned int i = 0; i < 10; i++)
		{
			// calculate the model matrix for each object and pass it to shader before drawing
			glm::mat4 model{ 1.0f };
			model = glm::translate(model, cubePositions[i]);
			float angle{ 20.0f * i };
			model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
			glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "model"), 1, GL_FALSE, &model[0][0]);

			glDrawArrays(GL_TRIANGLES, 0, 36);
		}

		// // also draw the lamp object
		// glUseProgram(lampProgramId);
		// glUniformMatrix4fv(glGetUniformLocation(lampProgramId, "projection"), 1, GL_FALSE, &projection[0][0]);
		// glUniformMatrix4fv(glGetUniformLocation(lampProgramId, "view"), 1, GL_FALSE, &view[0][0]);
		// model = glm::mat4(1.0f);
		// model = glm::translate(model, light_pos);
		// model = glm::scale(model, glm::vec3(0.3f)); // a smaller lamp cube
		// glUniformMatrix4fv(glGetUniformLocation(lampProgramId, "model"), 1, GL_FALSE, &model[0][0]);

		// glBindVertexArray(lampVAO);
		// glDrawArrays(GL_TRIANGLES, 0, 36);

		// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
		// -------------------------------------------------------------------------------
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	// optional: de-allocate all resources once they've outlived their purpose:
	// ------------------------------------------------------------------------
	glDeleteVertexArrays(1, &cubeVAO);
	glDeleteVertexArrays(1, &lampVAO);
	glDeleteBuffers(1, &VBO);

	// glfw: terminate, clearing all previously allocated GLFW resources.
	// ------------------------------------------------------------------
	glfwTerminate();
	return 0;
}<file_sep>/lightcasters-flashlight/main.cpp
#define GLM_FORCE_CXX14
#define GLM_FORCE_INTRINSICS
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

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

#include <iostream>

#include "stb_image/stb_image.h"

static constexpr const int WIDTH{800};
static constexpr const int HEIGHT{600};

// lighting.
static const glm::vec3 light_pos{1.2f, 1.0f, 2.0f};

// camera
static glm::vec3 cameraPos{0.0f, 0.0f, 3.0f};
static glm::vec3 cameraFront{};
static glm::vec3 cameraUp{0.0f, 1.0f, 0.0f};
static glm::vec3 cameraRight{};
static glm::vec3 worldUp = cameraUp;
static float delta_time{};
static float last_frame{};

//mouse and scroll
static float field_of_view{45.0f};
static bool firstMouse{true};
static float lastX{WIDTH / 2.f};
static float lastY{HEIGHT / 2.f};
static float pitch{0.0f};
static float yaw{-90.f};
static float speed{2.5f};
static float sensitivity{0.05f};

static void updateCameraVectors()
{
  // Calculate the new Front vector
  glm::vec3 front{};
  front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
  front.y = std::sin(glm::radians(pitch));
  front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));
  cameraFront = glm::normalize(front);

  // Also re-calculate the Right and Up vector
  cameraRight = glm::normalize(glm::cross(cameraFront, worldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
  cameraUp = glm::normalize(glm::cross(cameraRight, cameraFront));
}

static constexpr const char *cubeVertexShaderSource{
    "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "layout (location = 1) in vec3 aNormal;\n"
    "layout (location = 2) in vec2 aTexCoords;\n"

    "out vec3 FragPos;\n"
    "out vec3 Normal;\n"
    "out vec2 TexCoords;\n"

    "uniform mat4 model;\n"
    "uniform mat4 view;\n"
    "uniform mat4 projection;\n"

    "void main()\n"
    "{\n"
    "    FragPos = vec3(model * vec4(aPos, 1.0));\n"
    "    TexCoords = aTexCoords;\n"

    // must use Normal Matrix, it can keep NU Scale right.
    "    Normal = mat3(transpose(inverse(model))) * aNormal;\n" //transpose(inverse(model)) creatr Normal Matrix.

    "    gl_Position = projection * view * vec4(FragPos, 1.0);\n"
    "}"};
static constexpr const char *cubeFragmentShaderSource{
    "#version 330 core\n"
    "out vec4 FragColor;\n"

    "in vec3 Normal;\n"
    "in vec3 FragPos;\n"
    "in vec2 TexCoords;\n"

    "struct Material\n"
    "{\n"
    "    sampler2D diffuse_;\n" //ambient color is the same with diffuse color, ususally.
    "    sampler2D specular_;\n"
    "    float shininess_;\n"
    "};\n"

    "struct Light\n"
    "{\n"
    // why here are camera_xxx?
    //we are simulating flashlight, so camera is light source.
    "    vec3  camera_position_;\n"
    "    vec3  camera_front_;\n"
    "     float  cutoff_;\n"

    "    vec3 ambient_;\n"
    "    vec3 diffuse_;\n"
    "    vec3 specular_;\n"

    "   float constant_;\n"
    "   float linear_;\n"
    "   float quadratic_;\n"
    "};\n"

    "uniform Material material;\n"
    "uniform Light light;\n"

    "uniform vec3 cameraPos;\n" //camera pos.

    "void main()\n"
    "{\n"

    "   vec3 lightDir = normalize(light.camera_position_ - FragPos);\n"

    // check if lighting is inside the spotlight cone
    "   float thea = dot(lightDir, normalize(-light.camera_front_));\n"

    "if(thea > light.cutoff_)\n"
    "{\n"

    // ambient
    "    vec3 ambientVec = light.ambient_ * texture(material.diffuse_, TexCoords).rgb;\n"

    // diffuse
    "    vec3 normalVec = normalize(Normal);\n"
    "    vec3 lightDir = normalize(light.camera_position_ - FragPos);\n"
    "    float diffuseValue = max(dot(normalVec, lightDir), 0.0);\n"
    "    vec3 diffuseVec = light.diffuse_  * diffuseValue * texture(material.diffuse_, TexCoords).rgb; \n"

    // specular
    "    vec3 viewDir = normalize(cameraPos - FragPos);\n"
    "    vec3 reflectDir = reflect(-lightDir, normalVec);\n"
    "    float specularValue = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess_);\n"
    "    vec3 specularVec = light.specular_ * specularValue * texture(material.specular_, TexCoords).rgb;\n"

    "    float distance = length(light.camera_position_ - FragPos);\n"
    "    float attenuationValue = 1.0 / (light.constant_ + light.linear_ * distance + light.quadratic_ * (distance * distance));\n"

    // here ambient need not attenuationValue.
    "    vec3 finalColor = (diffuseVec + specularVec)*attenuationValue  + ambientVec;\n"
    "    FragColor = vec4(finalColor, 1.0);\n"

    "}else{\n"

    // else, use ambient light so scene isn't completely dark outside the spotlight.
    " FragColor = vec4(light.ambient_ * texture(material.diffuse_, TexCoords).rgb, 1.0);\n "

    "}\n"

    "}"};

static constexpr const char *lampVertexShaderSource{
    "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "uniform mat4 model;\n"
    "uniform mat4 view;\n"
    "uniform mat4 projection;\n"
    "void main()\n"
    "{\n"
    "	gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
    "}"};

static constexpr const char *lampFragmentShaderSource{
    "#version 330 core\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "{\n"
    "    FragColor = vec4(1.0);\n" // set alle 4 vector values to 1.0
    "}"};

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
static void processInput(GLFWwindow *window)
{
  if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    glfwSetWindowShouldClose(window, true);

  float camera_speed{speed * delta_time};

  if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
  {
    cameraPos += (cameraFront * camera_speed);
  }

  if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
  {
    cameraPos -= camera_speed * cameraFront;
  }

  if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
  {
    cameraPos -= cameraRight * camera_speed;
  }

  if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
    cameraPos += cameraRight * camera_speed;
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
  // make sure the viewport matches the new window dimensions; note that width and
  // height will be significantly larger than specified on retina displays.
  glViewport(0, 0, width, height);
}

// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
static void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
  if (firstMouse)
  {
    lastX = xpos;
    lastY = ypos;
    firstMouse = false;
  }

  float xoffset = xpos - lastX;
  float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
  lastX = xpos;
  lastY = ypos;

  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;

  updateCameraVectors();
}

// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
  if (field_of_view >= 1.0f && field_of_view <= 45.0f)
  {
    field_of_view -= yoffset;
  }

  if (field_of_view <= 1.f)
  {
    field_of_view = 1.0f;
  }

  if (field_of_view >= 45.0f)
  {
    field_of_view = 45.0f;
  }
}

int main()
{
  // glfw: initialize and configure
  // ------------------------------
  glfwInit();
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  // glfw window creation
  // --------------------
  GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
  if (window == nullptr)
  {
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();
    return -1;
  }

  glfwMakeContextCurrent(window);

  glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
  glfwSetCursorPosCallback(window, mouse_callback);
  glfwSetScrollCallback(window, scroll_callback);

  // tell GLFW to capture our mouse
  glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

  // glad: load all OpenGL function pointers
  // ---------------------------------------
  if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
  {
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
  }

  // configure global opengl state
  // -----------------------------
  glEnable(GL_DEPTH_TEST);

  // notice that: count camera front/up/right vectors.
  updateCameraVectors();

  // cube shaders.
  GLuint cubeVertexShader{};
  GLuint cubeFragmentShader{};

  cubeVertexShader = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(cubeVertexShader, 1, &cubeVertexShaderSource, nullptr);
  glCompileShader(cubeVertexShader);

  GLint success{};
  glGetShaderiv(cubeVertexShader, GL_COMPILE_STATUS, &success);
  if (!success)
  {
    GLchar infoLog[1024]{};
    glGetShaderInfoLog(cubeVertexShader, 1024, nullptr, infoLog);
    std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
              << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
  }

  cubeFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(cubeFragmentShader, 1, &cubeFragmentShaderSource, nullptr);
  glCompileShader(cubeFragmentShader);

  success = 0;
  glGetShaderiv(cubeFragmentShader, GL_COMPILE_STATUS, &success);
  if (!success)
  {
    GLchar infoLog[1024]{};
    glGetShaderInfoLog(cubeFragmentShader, 1024, nullptr, infoLog);
    std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
              << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
  }

  GLuint cubeProgramId{glCreateProgram()};
  glAttachShader(cubeProgramId, cubeVertexShader);
  glAttachShader(cubeProgramId, cubeFragmentShader);
  glLinkProgram(cubeProgramId);

  success = 0;
  glGetProgramiv(cubeProgramId, GL_LINK_STATUS, &success);
  if (!success)
  {
    GLchar infoLog[1024]{};
    glGetProgramInfoLog(cubeProgramId, 1024, nullptr, infoLog);
    std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
              << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
  }

  // lamp shaders.
  GLuint lampVertexShader{};
  GLuint lampFragmentShader{};

  lampVertexShader = glCreateShader(GL_VERTEX_SHADER);
  glShaderSource(lampVertexShader, 1, &lampVertexShaderSource, nullptr);
  glCompileShader(lampVertexShader);

  success = 0;
  glGetShaderiv(lampVertexShader, GL_COMPILE_STATUS, &success);
  if (!success)
  {
    GLchar infoLog[1024]{};
    glGetShaderInfoLog(lampVertexShader, 1024, nullptr, infoLog);
    std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
              << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
  }

  lampFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
  glShaderSource(lampFragmentShader, 1, &lampFragmentShaderSource, nullptr);
  glCompileShader(lampFragmentShader);

  success = 0;
  glGetShaderiv(lampFragmentShader, GL_COMPILE_STATUS, &success);
  if (!success)
  {
    GLchar infoLog[1024]{};
    glGetShaderInfoLog(lampFragmentShader, 1024, nullptr, infoLog);
    std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
              << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
  }

  GLuint lampProgramId{glCreateProgram()};
  glAttachShader(lampProgramId, lampVertexShader);
  glAttachShader(lampProgramId, lampFragmentShader);
  glLinkProgram(lampProgramId);

  success = 0;
  glGetProgramiv(lampProgramId, GL_LINK_STATUS, &success);
  if (!success)
  {
    GLchar infoLog[1024]{};
    glGetProgramInfoLog(lampProgramId, 1024, nullptr, infoLog);
    std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: "
              << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
  }

  // set up vertex data (and buffer(s)) and configure vertex attributes
  // ------------------------------------------------------------------
  const float vertices[]{
      // positions          // normals           // texture coords
      -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
      0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
      0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
      0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
      -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
      -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,

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

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

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

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

      -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
      0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
      0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
      0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
      -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
      -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f};

  // positions all containers
  const 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{};
  GLuint cubeVAO{};
  glGenVertexArrays(1, &cubeVAO);
  glGenBuffers(1, &VBO);

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

  GLint offset{0};
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
  glEnableVertexAttribArray(0);

  offset = 3 * sizeof(float);
  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
  glEnableVertexAttribArray(1);

  offset = 6 * sizeof(float);
  glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
  glEnableVertexAttribArray(2);

  GLuint lampVAO{};
  glGenVertexArrays(1, &lampVAO);

  glBindVertexArray(lampVAO);
  glBindBuffer(GL_ARRAY_BUFFER, VBO);

  offset = 0;
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void *>(offset));
  glEnableVertexAttribArray(0);

  GLuint textureID{};
  glGenTextures(1, &textureID);

  int width{}, height{}, nrComponents{};
  unsigned char *data{stbi_load("/home/shihua/projects/learn_opengl/lightmapping/image/container2.png",
                                &width, &height, &nrComponents, 0)};

  if (data)
  {
    GLenum format{};
    if (nrComponents == 1)
      format = GL_RED;
    else if (nrComponents == 3)
      format = GL_RGB;
    else if (nrComponents == 4)
      format = GL_RGBA;

    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);

    // set texture wrapper.
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // set texture filter.
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    stbi_image_free(data);
  }
  else
  {
    std::cerr << "the path of image is wrong." << std::endl;
    stbi_image_free(data);
  }

  data = nullptr;

  GLuint textureID2{};
  glGenTextures(1, &textureID2);
  width = 0;
  height = 0;
  nrComponents = 0;

  data = stbi_load("/home/shihua/projects/learn_opengl/lightmapping/image/container2_specular.png",
                   &width, &height, &nrComponents, 0);

  if (data)
  {
    GLenum format{};
    if (nrComponents == 1)
      format = GL_RED;
    else if (nrComponents == 3)
      format = GL_RGB;
    else if (nrComponents == 4)
      format = GL_RGBA;

    glBindTexture(GL_TEXTURE_2D, textureID2);
    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);

    // set texture wrapper.
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // set texture filter.
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    stbi_image_free(data);
  }
  else
  {
    std::cerr << "the path of image is wrong." << std::endl;
    stbi_image_free(data);
  }

  data = nullptr;

  glUseProgram(cubeProgramId);
  GLint texture1Id{glGetUniformLocation(cubeProgramId, "material.diffuse_")};
  glUniform1i(texture1Id, 0);
  GLint texture2Id{glGetUniformLocation(cubeProgramId, "material.specular_")};
  glUniform1i(texture2Id, 1);

  while (!glfwWindowShouldClose(window))
  {
    // per-frame time logic
    float currentFrame = glfwGetTime();
    delta_time = currentFrame - last_frame;
    last_frame = currentFrame;

    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // input
    // -----
    processInput(window);

    glUseProgram(cubeProgramId);

    // bind textures to specify uniform.
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureID);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, textureID2);

    GLint viewPosLoc{glGetUniformLocation(cubeProgramId, "cameraPos")};
    glUniform3fv(viewPosLoc, 1, &cameraPos[0]);

    glm::vec3 lightAmbientColor{0.2f, 0.2f, 0.2f};
    glm::vec3 lightDiffuseColor{0.5f, 0.5f, 0.5f};
    glm::vec3 lightSpecularColor{1.0f, 1.0f, 1.0f};

    GLint cameraPosLoc{glGetUniformLocation(cubeProgramId, "light.camera_position_")};
    glUniform3fv(cameraPosLoc, 1, &cameraPos[0]);

    GLint cameraDirectionLoc{glGetUniformLocation(cubeProgramId, "light.camera_front_")};
    glUniform3fv(cameraDirectionLoc, 1, &cameraFront[0]);

    GLint cutOffLoc{glGetUniformLocation(cubeProgramId, "light.cutoff_")};
    glUniform1f(cutOffLoc, std::cos(glm::radians(12.5f)));

    GLint lightAmbientLoc{glGetUniformLocation(cubeProgramId, "light.ambient_")};
    glUniform3f(lightAmbientLoc, 0.1f, 0.1f, 0.1f);

    GLint lightDiffuseLoc{glGetUniformLocation(cubeProgramId, "light.diffuse_")};
    glUniform3f(lightDiffuseLoc, 0.8f, 0.8f, 0.8f);

    GLint lightSpecularLoc{glGetUniformLocation(cubeProgramId, "light.specular_")};
    glUniform3f(lightSpecularLoc, 1.0, 1.0, 1.0);

    GLint lightConstantLoc{glGetUniformLocation(cubeProgramId, "light.constant_")};
    glUniform1f(lightConstantLoc, 1.0f);

    GLint lightLinearLoc{glGetUniformLocation(cubeProgramId, "light.linear_")};
    glUniform1f(lightLinearLoc, 0.09f);

    GLint lightQuadraticLoc{glGetUniformLocation(cubeProgramId, "light.quadratic_")};
    glUniform1f(lightQuadraticLoc, 0.032f);

    GLint materialShininessLoc{glGetUniformLocation(cubeProgramId, "material.shininess_")};
    glUniform1f(materialShininessLoc, 32.0f);

    // view/projection transformations
    glm::mat4 projection{glm::perspective(glm::radians(field_of_view), static_cast<float>(WIDTH) / static_cast<float>(HEIGHT), 0.1f, 100.0f)};
    glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "projection"), 1, GL_FALSE, &projection[0][0]);

    // camera/view transformation
    glm::mat4 view{1.0f}; // make sure to initialize matrix to identity matrix first
    view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
    glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "view"), 1, GL_FALSE, &view[0][0]);

    // model
    glm::mat4 model{1.0f};
    glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "model"), 1, GL_FALSE, &model[0][0]);

    // render containers
    glBindVertexArray(cubeVAO);
    for (unsigned int i = 0; i < 10; i++)
    {
      // calculate the model matrix for each object and pass it to shader before drawing
      glm::mat4 model{1.0f};
      model = glm::translate(model, cubePositions[i]);
      float angle{20.0f * i};
      model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
      glUniformMatrix4fv(glGetUniformLocation(cubeProgramId, "model"), 1, GL_FALSE, &model[0][0]);

      glDrawArrays(GL_TRIANGLES, 0, 36);
    }

    // // also draw the lamp object
    // glUseProgram(lampProgramId);
    // glUniformMatrix4fv(glGetUniformLocation(lampProgramId, "projection"), 1, GL_FALSE, &projection[0][0]);
    // glUniformMatrix4fv(glGetUniformLocation(lampProgramId, "view"), 1, GL_FALSE, &view[0][0]);
    // model = glm::mat4(1.0f);
    // model = glm::translate(model, light_pos);
    // model = glm::scale(model, glm::vec3(0.3f)); // a smaller lamp cube
    // glUniformMatrix4fv(glGetUniformLocation(lampProgramId, "model"), 1, GL_FALSE, &model[0][0]);

    // glBindVertexArray(lampVAO);
    // glDrawArrays(GL_TRIANGLES, 0, 36);

    // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
    // -------------------------------------------------------------------------------
    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  // optional: de-allocate all resources once they've outlived their purpose:
  // ------------------------------------------------------------------------
  glDeleteVertexArrays(1, &cubeVAO);
  glDeleteVertexArrays(1, &lampVAO);
  glDeleteBuffers(1, &VBO);

  // glfw: terminate, clearing all previously allocated GLFW resources.
  // ------------------------------------------------------------------
  glfwTerminate();
  return 0;
}<file_sep>/illumination/camera.cpp

#include "camera.h"

const float YAW{-90.0f};
const float PITCH{0.0f};
const float SPEED{2.5f};
const float SENSITIVITY{0.05f};
const float ZOOM{45.0f};<file_sep>/blending/blending/main.cpp
#define GLM_FORCE_CXX14
#define GLM_FORCE_INTRINSICS
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

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

#include <vector>
#include <atomic>
#include <iostream>

#include "shader.hpp"
#include "stb_image/stb_image.h"

static  const int WIDTH{ 1280 };
static  const int HEIGHT{ 720 };

// lighting.
static const glm::vec3 light_pos{ 1.2f, 1.0f, 2.0f };

// camera
static glm::vec3 camera_pos{ 0.0f, 0.0f, 3.0f };
static glm::vec3 camera_front{};
static glm::vec3 camera_up{ 0.0f, 1.0f, 0.0f };
static glm::vec3 camera_right{};
static glm::vec3 world_up = camera_up;
static float delta_time{};
static float last_frame{};

//mouse and scroll
static float field_of_view{ 45.0f };
static std::atomic<bool> first_click{ true };
static float lastX{ WIDTH / 2.f };
static float lastY{ HEIGHT / 2.f };
static float pitch{ 0.0f };
static float yaw{ -90.f };
static float speed{ 2.5f };
static float sensitivity{ 0.05f };

static void update_camera_vectors()
{
	// Calculate the new Front vector
	glm::vec3 front{};
	front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	front.y = std::sin(glm::radians(pitch));
	front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	camera_front = glm::normalize(front);

	// Also re-calculate the Right and Up vector
	camera_right = glm::normalize(glm::cross(camera_front, world_up)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
	camera_up = glm::normalize(glm::cross(camera_right, camera_front));
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
static void process_input(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);

	float camera_speed{ speed * delta_time };

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
	{
		camera_pos += (camera_front * camera_speed);
	}

	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
	{
		camera_pos -= camera_speed * camera_front;
	}

	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
	{
		camera_pos -= camera_right * camera_speed;
	}

	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
	{
		camera_pos += camera_right * camera_speed;
	}
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
static void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
	// make sure the viewport matches the new window dimensions; note that width and
	// height will be significantly larger than specified on retina displays.
	glViewport(0, 0, width, height);
}


// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
static void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
	if (first_click.load())
	{
		lastX = xpos;
		lastY = ypos;
		first_click.store(false);
	}

	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
	lastX = xpos;
	lastY = ypos;

	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;

	update_camera_vectors();
}


// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset)
{
	if (field_of_view >= 1.0f && field_of_view <= 45.0f)
	{
		field_of_view -= yoffset;
	}

	if (field_of_view <= 1.f)
	{
		field_of_view = 1.0f;
	}

	if (field_of_view >= 45.0f)
	{
		field_of_view = 45.0f;
	}
}


static GLuint load_texture(const char * path)
{
	GLuint texture_id{};
	glGenTextures(1, &texture_id);

	int width{}, height{}, nrComponents{};
	unsigned char *data{ stbi_load(path, &width, &height, &nrComponents, 0) };

	if (data)
	{
		GLenum format{};
		if (nrComponents == 1)
			format = GL_RED;
		else if (nrComponents == 3)
			format = GL_RGB;
		else if (nrComponents == 4)
			format = GL_RGBA;

		glBindTexture(GL_TEXTURE_2D, texture_id);
		glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);

		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);

		stbi_image_free(data);
	}
	else
	{
		std::cout << __FUNCTION__ << "   " << __LINE__ << "  " << "Texture failed to load at path : " << path << std::endl;
		stbi_image_free(data);
	}

	data = nullptr;
	return texture_id;
}


int main()
{
	// glfw: initialize and configure
// ------------------------------
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// glfw window creation
	// --------------------
	GLFWwindow *window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
	if (window == nullptr)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}


	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	// tell GLFW to capture our mouse
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

	// glad: load all OpenGL function pointers
	// ---------------------------------------
	if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// update camera parameters.
	update_camera_vectors();

	// configure global opengl state
	glEnable(GL_DEPTH_TEST);

	GLuint vertex_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\blending\\blending\\glsl\\vertex_shader.glsl", shader_type::vertex_shader) };
	GLuint fragment_shader_id{ shader::create("C:\\Users\\shihua\\source\\repos\\opengl_demo\\blending\\blending\\glsl\\fragment_shader.glsl", shader_type::fragment_shader) };

	GLuint program_id{ glCreateProgram() };
	glAttachShader(program_id, vertex_shader_id);
	glAttachShader(program_id, fragment_shader_id);
	glLinkProgram(program_id);
	glDeleteShader(vertex_shader_id);
	glDeleteShader(fragment_shader_id);
	shader::checkout_shader_state(program_id, shader_type::program);


	const float floor_vertices[]{
		// positions          // texture Coords (note we set these higher than 1 (together with GL_REPEAT as texture wrapping mode). this will cause the floor texture to repeat)
		 5.0f, -0.5f,  5.0f,  2.0f, 0.0f,
		-5.0f, -0.5f,  5.0f,  0.0f, 0.0f,
		-5.0f, -0.5f, -5.0f,  0.0f, 2.0f,

		 5.0f, -0.5f,  5.0f,  2.0f, 0.0f,
		-5.0f, -0.5f, -5.0f,  0.0f, 2.0f,
		 5.0f, -0.5f, -5.0f,  2.0f, 2.0f
	};

	float cubes_vertices[]{
		// positions          // texture Coords
		-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
	};

	const float grass_vertices[]{
		// positions         // texture Coords (swapped y coordinates because texture is flipped upside down)
		0.0f,  0.5f,  0.0f,  0.0f,  0.0f,
		0.0f, -0.5f,  0.0f,  0.0f,  1.0f,
		1.0f, -0.5f,  0.0f,  1.0f,  1.0f,

		0.0f,  0.5f,  0.0f,  0.0f,  0.0f,
		1.0f, -0.5f,  0.0f,  1.0f,  1.0f,
		1.0f,  0.5f,  0.0f,  1.0f,  0.0f
	};


	GLuint cube_VAO{};
	GLuint cube_VBO{};

	glGenVertexArrays(1, &cube_VAO);
	glGenBuffers(1, &cube_VBO);

	glBindVertexArray(cube_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, cube_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(cubes_vertices), cubes_vertices, GL_STATIC_DRAW);

	std::size_t offset{ 0 };
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 3 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	// notice that.
	glBindVertexArray(0);

	GLuint floor_VAO{};
	GLuint floor_VBO{};

	glGenVertexArrays(1, &floor_VAO);
	glGenBuffers(1, &floor_VBO);

	glBindVertexArray(floor_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, floor_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(floor_vertices), floor_vertices, GL_STATIC_DRAW);

	offset = 0;
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 3 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	// notice that
	glBindVertexArray(0);


	GLuint grass_VAO{};
	GLuint grass_VBO{};

	glGenVertexArrays(1, &grass_VAO);
	glGenBuffers(1, &grass_VBO);

	glBindVertexArray(grass_VAO);
	glBindBuffer(GL_ARRAY_BUFFER, grass_VBO);

	glBufferData(GL_ARRAY_BUFFER, sizeof(grass_vertices), grass_vertices, GL_STATIC_DRAW);

	offset = 0;
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	offset = 3 * sizeof(float);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(offset));

	// notice that:
	glBindVertexArray(0);


	GLuint wall_texture_id{ load_texture("C:\\Users\\shihua\\source\\repos\\opengl_demo\\depth_test\\depth_test\\image\\marble.jpg") };
	GLuint floor_texture_id{ load_texture("C:\\Users\\shihua\\source\\repos\\opengl_demo\\depth_test\\depth_test\\image\\metal.png") };
	GLuint grass_texture_id{ load_texture("C:\\Users\\shihua\\source\\repos\\opengl_demo\\blending\\blending\\image\\grass.png") };

	glUseProgram(program_id);
	shader::set_int(program_id, "texture_1", 0);


	// grass locations.
	std::vector<glm::vec3> vegetation_locations
	{
		glm::vec3(-1.5f, 0.0f, -0.48f),
		glm::vec3(1.5f, 0.0f, 0.51f),
		glm::vec3(0.0f, 0.0f, 0.7f),
		glm::vec3(-0.3f, 0.0f, -2.3f),
		glm::vec3(0.5f, 0.0f, -0.6f)
	};


	while (!glfwWindowShouldClose(window))
	{
		double current_time{ glfwGetTime() };
		delta_time = current_time - last_frame;
		last_frame = current_time;

		process_input(window);

		glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glUseProgram(program_id);
		glm::mat4 model{ 1.0f };
		glm::mat4 view{ glm::lookAt(camera_pos, camera_pos + camera_front, camera_up) };
		glm::mat4 projection{ glm::perspective(glm::radians(field_of_view), WIDTH*1.0f / HEIGHT * 1.0f, 0.1f, 100.0f) };
		shader::set_mat4(program_id, "view", view);
		shader::set_mat4(program_id, "projection", projection);

		// draw floor
		glBindVertexArray(floor_VAO);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, floor_texture_id);
		model = glm::mat4{ 1.0f };
		shader::set_mat4(program_id, "model", model);
		glDrawArrays(GL_TRIANGLES, 0, 6);

		// draw cubes
		glBindVertexArray(cube_VAO);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, wall_texture_id);

		// draw first cube.
		model = glm::mat4{ 1.0f };
		model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f));
		shader::set_mat4(program_id, "model", model);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		//draw second cube.
		model = glm::mat4{ 1.0f };
		model = glm::translate(model, glm::vec3{ 2.0f, 0.0f, 0.0f });
		shader::set_mat4(program_id, "model", model);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		// vegetation
		glBindVertexArray(grass_VAO);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, grass_texture_id);
		for (std::size_t index = 0; index < vegetation_locations.size(); ++index)
		{
			model = glm::mat4{ 1.0f };
			model = glm::translate(model, vegetation_locations[index]);
			shader::set_mat4(program_id, "model", model);
			glDrawArrays(GL_TRIANGLES, 0, 6);
		}



		// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	// glfw: terminate, clearing all previously allocated GLFW resources.
	// ------------------------------------------------------------------
	glDeleteVertexArrays(1, &cube_VAO);
	glDeleteVertexArrays(1, &floor_VAO);
	glDeleteVertexArrays(1, &grass_VAO);

	glDeleteBuffers(1, &cube_VBO);
	glDeleteBuffers(1, &floor_VBO);
	glDeleteBuffers(1, &floor_VBO);

	glfwTerminate();
	return 0;
}