#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <algorithm>

class Camera{
    // euler Angles
    float yaw;
    float pitch;


    glm::vec3 forward;
    glm::vec3 up;
    glm::vec3 right;
    glm::vec3 WorldUp;


    glm::mat4 proj;
    glm::vec3 location; 

    void updateCameraVectors()
    {
        // calculate the new Front vector
        forward.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
        forward.y = sin(glm::radians(pitch));
        forward.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
        forward = glm::normalize(forward);
        // also re-calculate the Right and Up vector
        right = glm::normalize(glm::cross(forward, WorldUp));  // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
        up = glm::normalize(glm::cross(right, forward));
    }
public:

	Camera() {}
    Camera(float screenWidth, float screenHeight, float fov = 100.0f, float near = 0.1f, float far = 10'000.0f)
    {
        SetProjection(screenWidth, screenWidth, fov, near, far);
        location = glm::vec3(0.0f);

        WorldUp = glm::vec3(0, 1, 0);
        yaw = -90;
        pitch = 0;
        updateCameraVectors();
    }

    void Move(glm::vec3 deltaLocation) {
        location += deltaLocation.x * right;
        location += deltaLocation.y * up;
        location += deltaLocation.z * -forward;
    }
    void Rotate(glm::vec3 deltaRotation) {
        yaw += -deltaRotation.y;
        pitch += deltaRotation.x;

        if (pitch > 89.0f)
            pitch = 89.0f;
        if (pitch < -89.0f)
            pitch = -89.0f;
        
        // update Front, Right and Up Vectors using the updated Euler angles
        updateCameraVectors();
    }

    void SetProjection(float screenWidth, float screenHeight, float fov,  float near = 0.1f, float far = 10'000.0f) {
        proj = glm::perspective(glm::degrees(10.0f), screenWidth / screenHeight, near, far);

    }

    glm::mat4 GetCameraMatrix() {        
        return proj * glm::lookAt(location, location + forward*100.0f, glm::vec3(0, 1, 0));
    }

};

#include <glad/glad.h> 
#include <GLFW/glfw3.h> 
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

#include <string>
#include <iostream>
#include <vector>

#include "Shader.h"
#include "VertexArray.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"

#include "Actor.h"

#include "Camera.h"

#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"



void drawActor(Actor& actor, VertexBuffer& vb, IndexBuffer& ib);
void processInput(GLFWwindow* window);
void size_callback(GLFWwindow* window, int width, int height);
void cursor_enter_callback(GLFWwindow* window, int entered);
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos);
GLFWwindow* create_GLFWwindow(float width, float height, std::string title);
void init_glad();


Camera camera; 
Shader shader; 

float SCR_WIDTH = 640, SCR_HEIGHT = 480;
std::string SCR_TITLE = "My 3D App";

glm::mat4 proj, view, model;
float currentFrame, lastFrame, deltaTime;


int main() {
    GLFWwindow* window = create_GLFWwindow(SCR_WIDTH, SCR_HEIGHT, SCR_TITLE);
    init_glad();

    shader.Compile();
    shader.Bind();


    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                      DEFINE VERTICES                     <|
    // |>----------<>----------<>----------<>----------<>----------<|

    std::vector<Actor> actors;
    for(int i =0;i<10;i++)
        for (int j = 0; j < 10; j++) {
            Actor actor(50);
            actor.setLocation(200 * i, 100 * j, 0);
            actors.push_back(actor);
        }


    unsigned int layout[]{ 3, GL_FLOAT,2,GL_FLOAT };

    VertexArray va; va.Bind();
    VertexBuffer vb;// (actor1.getPoints().data(), sizeof(float) * actor1.getPoints().size());
    IndexBuffer ib;// (actor1.getIndicies().data(), sizeof(unsigned int) * actor1.getIndicies().size());

    vb.setLayout(layout,sizeof(layout));
    va.Unbind();
        

    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                         TEXTURE                          <|
    // |>----------<>----------<>----------<>----------<>----------<|

    // load and create a texture 
    unsigned int texture1;
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1);
    // 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;
    stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.
    // The FileSystem::getPath(...) is part of the GitHub repository so we can find files on any IDE/platform; replace it with your own image path.
    unsigned char* data = stbi_load("res/horse-face.png", &width, &height, &nrChannels, 0);
    if (data) {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 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);
    shader.SetUniform1i("texture1", 0);

    // enable blending 
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                          CAMERA                          <|
    // |>----------<>----------<>----------<>----------<>----------<|
    camera = Camera(35, SCR_WIDTH, SCR_HEIGHT);



    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                          ImGUI                           <|
    // |>----------<>----------<>----------<>----------<>----------<|

    ImGui::CreateContext();
    ImGui::StyleColorsDark();
    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init("#version 440");
    //ImGui variables: 
    //bool show_demo_window = true;
    //bool show_another_window = false;
    float clear_color[]{ 0.45f, 0.55f, 0.60f, 1.00f };




    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                       RENDER LOOP                        <|
    // |>----------<>----------<>----------<>----------<>----------<|

    glEnable(GL_DEPTH_TEST);

    va.Bind();

    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);


    while (!glfwWindowShouldClose(window)) {
        // per-frame time logic
        // --------------------
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        processInput(window);
        
        // clear
        glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); 

        for(auto& i :actors)
            drawActor(i,vb,ib);


        // render imgui
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();
        ImGui::NewFrame();

        ImGui::Begin("test");
        ImGui::ColorEdit4("bg color", clear_color);
        ImGui::End();

        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());



        glfwSwapBuffers(window);
        glfwPollEvents();
    }

}


void drawActor(Actor& actor, VertexBuffer& vb, IndexBuffer& ib) {
    vb.SetBufferData(actor.getPoints().data(), sizeof(float) * actor.getPoints().size());
    ib.SetBufferData(actor.getIndicies().data(), sizeof(unsigned int) * actor.getIndicies().size());

    model = actor.modelMatrix();
    glm::mat4 mvp = camera.GetCameraMatrix() * model;
    shader.setUniformMat4f("u_MVP", mvp);

    glDrawElements(GL_TRIANGLES, actor.getIndicies().size(), GL_UNSIGNED_INT, 0);
}


void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{

    //if (key == GLFW_KEY_RIGHT && action == GLFW_PRESS);

    if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
    
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        if (GLFW_CURSOR_DISABLED == glfwGetInputMode(window, GLFW_CURSOR))
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        else
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

}

void processInput(GLFWwindow* window){

    float cameraSpeed = static_cast<float>(250 * deltaTime);
    glm::vec3 deltaLocation = glm::vec3(0.0f);


    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
        deltaLocation += glm::vec3(0, 0, -1); // to forward
    else if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
        deltaLocation += glm::vec3(0, 0, 1); // to backward

    if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
        deltaLocation += glm::vec3(1, 0, 0); // to right
    else if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
        deltaLocation += glm::vec3(-1, 0, 0); // to left 

    if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
        deltaLocation += glm::vec3(0, 1, 0); // to up 
    else if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)
        deltaLocation += glm::vec3(0, -1, 0); // to down 

    camera.Move(deltaLocation * cameraSpeed);
    glm::mat4 mvp = camera.GetCameraMatrix() * model;
    shader.setUniformMat4f("u_MVP", mvp);
}


void size_callback(GLFWwindow* window, int width, int height) {
    //std::cout << "New width: " << width << "\theight: " << height << std::endl;

    //camera.SetProjection(width, height,10);
    //glm::mat4 mvp = camera.GetCameraMatrix() * model;
    //shader.setUniformMat4f("u_MVP", mvp);

    //glViewport(0, 0, width, height);
}

GLFWwindow* create_GLFWwindow(float width, float height, std::string title){
    // |>----------<>----------<>----------<>----------<>----------<|
    // |>                    CREATE GLFW WINDOW                    <|
    // |>----------<>----------<>----------<>----------<>----------<|

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWmonitor* MyMonitor = glfwGetPrimaryMonitor(); // The primary monitor.. Later Occulus?..

    const GLFWvidmode* mode = glfwGetVideoMode(MyMonitor);
    SCR_WIDTH = mode->width;
    SCR_HEIGHT = mode->height;
    std::cout << "Monitor width: " << SCR_WIDTH << "\theight: " << SCR_HEIGHT << std::endl;




    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, title.c_str(), glfwGetPrimaryMonitor(), NULL);
    if (window == NULL) {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        //return EXIT_FAILURE;
    }
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1); 

    glfwSetFramebufferSizeCallback(window, size_callback); // we bind callback to our size_callback func. 
    glfwSetCursorPosCallback(window, cursor_position_callback);
    glfwSetCursorEnterCallback(window, cursor_enter_callback);
    glfwSetKeyCallback(window, key_callback);


    //https://www.glfw.org/docs/3.3/input_guide.html#cursor_standard
    //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);


    return window;
}





// |>----------<>----------<>----------<>----------<>----------<|
// |>                          MOUSE                           <|
// |>----------<>----------<>----------<>----------<>----------<|
static double lastX = 0, lastY = 0;
void cursor_enter_callback(GLFWwindow* window, int entered){
    if (entered){
        glfwGetCursorPos(window, &lastX, &lastY);
        
    }
    else{
        // The cursor left the content area of the window
    }
}
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) {

    if (GLFW_CURSOR_NORMAL == glfwGetInputMode(window, GLFW_CURSOR))
        return;

    //std::cout << lastX-xpos << "-" << lastY-ypos << std::endl;
    double mouseSensitivity = 0.2;
    double deltaX = (lastX - xpos) * mouseSensitivity;
    double deltaY = (lastY - ypos) * mouseSensitivity;
    camera.Rotate(glm::vec3(deltaY, deltaX,  0));
    lastX = xpos;
    lastY = ypos;

    //static double lastX, lastY;
}


void init_glad(){
    // |>----------<>----------<>----------<>----------<>----------<|
    // |>               LOAD OPENGL FUNCTION POINTERS              <|
    // |>----------<>----------<>----------<>----------<>----------<|

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cout << "Failed to initialize GLAD" << std::endl;
        exit(EXIT_FAILURE);
    }

}



Comments

Leave a Reply

Your email address will not be published. Required fields are marked *