← index
libmx2/libmx/gl_pyramid/example.cpp
Source: libmx2/libmx/gl_pyramid/example.cpp
#include"mx.hpp"

#include"argz.hpp"


#ifdef __EMSCRIPTEN__

#include <emscripten/emscripten.h>

#include <GLES3/gl3.h>

#endif


#include"gl.hpp"

#include"loadpng.hpp"


#define CHECK_GL_ERROR() \

    { GLenum err = glGetError(); \
      if (err != GL_NO_ERROR) \
        printf("OpenGL Error: %d at %s:%d\n", err, __FILE__, __LINE__); }


GLfloat positions[] = {
    // Base (square)

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

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

    // Front face

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

    // Right face

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

    // Back face

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

    // Left face

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

GLfloat texCoords[] = {
    // Base (square)

    0.0f, 0.0f,
    1.0f, 0.0f,
    1.0f, 1.0f,

    0.0f, 0.0f,
    1.0f, 1.0f,
    0.0f, 1.0f,

    // Front face

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

    // Right face

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

    // Back face

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

    // Left face

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

GLfloat normals[] = {
    // Base (square)

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

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

    // Front face

    0.0f,  0.707f,  0.707f,
    0.0f,  0.707f,  0.707f,
    0.0f,  0.707f,  0.707f,

    // Right face

    0.707f,  0.707f,  0.0f,
    0.707f,  0.707f,  0.0f,
    0.707f,  0.707f,  0.0f,

    // Back face

    0.0f,  0.707f, -0.707f,
    0.0f,  0.707f, -0.707f,
    0.0f,  0.707f, -0.707f,

    // Left face

    -0.707f,  0.707f,  0.0f,
    -0.707f,  0.707f,  0.0f,
    -0.707f,  0.707f,  0.0f,
};

class Pyramid : public gl::GLObject {
public:
    gl::ShaderProgram shaderProgram;
    GLuint texture;
    GLuint positionVBO, normalVBO, texCoordVBO;
    GLuint VAO;

    Pyramid() = default;
    virtual ~Pyramid() override {
        glDeleteVertexArrays(1, &VAO);
        glDeleteBuffers(1, &positionVBO);
        glDeleteBuffers(1, &normalVBO);
        glDeleteBuffers(1, &texCoordVBO);
        glDeleteTextures(1, &texture);
    }

    std::vector<GLfloat> cubeData;

    virtual void load(gl::GLWindow *win) override {
        
        glGenVertexArrays(1, &VAO);
        CHECK_GL_ERROR();
        glBindVertexArray(VAO);
        CHECK_GL_ERROR();

        glGenBuffers(1, &positionVBO);
        CHECK_GL_ERROR();
        glBindBuffer(GL_ARRAY_BUFFER, positionVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
        CHECK_GL_ERROR();
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); // Location = 0

        glEnableVertexAttribArray(0);
        CHECK_GL_ERROR();

        glGenBuffers(1, &normalVBO);
        CHECK_GL_ERROR();
        glBindBuffer(GL_ARRAY_BUFFER, normalVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(normals), normals, GL_STATIC_DRAW);
        CHECK_GL_ERROR();
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); // Location = 1

        glEnableVertexAttribArray(1);
        CHECK_GL_ERROR();

        glGenBuffers(1, &texCoordVBO);
        CHECK_GL_ERROR();
        glBindBuffer(GL_ARRAY_BUFFER, texCoordVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW);
        CHECK_GL_ERROR();
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); // Location = 2

        glEnableVertexAttribArray(2);
        CHECK_GL_ERROR();

        glBindVertexArray(0);
        CHECK_GL_ERROR();

        if (!shaderProgram.loadProgram(win->util.getFilePath("data/tri.vert"), win->util.getFilePath("data/tri.frag"))) {
            throw mx::Exception("Failed to load shader program");
        }
        shaderProgram.useProgram();
        CHECK_GL_ERROR();

        glm::mat4 model = glm::mat4(1.0f); // Identity matrix

        glm::mat4 view = glm::lookAt(
            glm::vec3(0.0f, 0.0f, 5.0f),  // Camera position

            glm::vec3(0.0f, 0.0f, 0.0f),  // Look at origin

            glm::vec3(0.0f, 1.0f, 0.0f)   // Up vector

        );
        glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)win->w / win->h, 0.1f, 100.0f);
        shaderProgram.setUniform("model", model);
        shaderProgram.setUniform("view", view);
        shaderProgram.setUniform("projection", projection);
        CHECK_GL_ERROR();

        glGenTextures(1, &texture);
        CHECK_GL_ERROR();
        glBindTexture(GL_TEXTURE_2D, texture);
        CHECK_GL_ERROR();
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        CHECK_GL_ERROR();

        SDL_Surface *surface = png::LoadPNG(win->util.getFilePath("data/bg.png").c_str());
        if (!surface) {
            mx::system_err << "Error: loading PNG.\n";
            exit(EXIT_FAILURE);
        }
        SDL_Surface *converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA32, 0);
        SDL_FreeSurface(surface);
        surface = converted;
        surface = mx::Texture::flipSurface(surface);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
        CHECK_GL_ERROR();
        SDL_FreeSurface(surface);
        glBindTexture(GL_TEXTURE_2D, 0);
        CHECK_GL_ERROR();
    }

    virtual void draw(gl::GLWindow *win) override {
        glm::vec3 cameraPos(0.0f, 0.0f, 5.0f);
        glm::vec3 cameraTarget(0.0f, 0.0f, 0.0f);
        glm::vec3 cameraUp(0.0f, 1.0f, 0.0f);
        float aspectRatio = (float)win->w / (float)win->h;

        static float rotationAngle = 0.0f; // Rotation angle in degrees

    #ifdef __EMSCRIPTEN__
        static Uint32 lastTime = emscripten_get_now(); 
        float currentTime = emscripten_get_now();
        float deltaTime = (currentTime - lastTime) / 1000.0f;
        lastTime = currentTime;
    #else
        static Uint32 lastTime = SDL_GetTicks(); 
        Uint32 currentTime = SDL_GetTicks();
        float deltaTime = (currentTime - lastTime) / 1000.0f;
        lastTime = currentTime;
    #endif
        rotationAngle += deltaTime * 50.0f; 
        shaderProgram.useProgram();

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture);
        shaderProgram.setUniform("texture1", 0);

        glBindVertexArray(VAO);

        glm::mat4 modelMatrix = glm::mat4(1.0f);
        modelMatrix = glm::rotate(modelMatrix, glm::radians(rotationAngle), glm::vec3(1.0f, 1.0f, 0.0f)); // Rotate around X-axis

        glm::mat4 viewMatrix = glm::lookAt(cameraPos, cameraTarget, cameraUp);
        glm::mat4 projectionMatrix = glm::perspective(glm::radians(45.0f), aspectRatio, 0.1f, 100.0f);

        glm::vec3 lightPos(2.0f, 4.0f, 1.0f);
        glm::vec3 viewPos = cameraPos; // Viewer position is the camera position

        glm::vec3 lightColor(1.0f, 1.0f, 1.0f);

        shaderProgram.setUniform("model", modelMatrix);
        shaderProgram.setUniform("view", viewMatrix);
        shaderProgram.setUniform("projection", projectionMatrix);
        shaderProgram.setUniform("lightPos", lightPos);
        shaderProgram.setUniform("viewPos", viewPos);
        shaderProgram.setUniform("lightColor", lightColor);

        int vertexCount = sizeof(positions) / (sizeof(GLfloat) * 3);
        glDrawArrays(GL_TRIANGLES, 0, vertexCount);
        glBindVertexArray(0);
    }
    virtual void event(gl::GLWindow *window, SDL_Event &e) override {
    }
private:
    
};

class MainWindow : public gl::GLWindow {
public:
    MainWindow(std::string path, int tw, int th) : gl::GLWindow("OpenGL Example", tw, th) {
        setPath(path);
        setObject(new Pyramid());
		object->load(this);
    }
    
    ~MainWindow() override {}
    
    virtual void event(SDL_Event &e) override {}

    virtual void draw() override {
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glEnable(GL_DEPTH_TEST);
        glViewport(0, 0, w, h);

        object->draw(this);
        swap();
    }
private:
};

MainWindow *main_w = nullptr;

void eventProc() {
    main_w->proc();
}

int main(int argc, char **argv) {
#ifdef __EMSCRIPTEN__

    MainWindow main_window("/", 960, 720);
    main_w =&main_window;
    emscripten_set_main_loop(eventProc, 0, 1);
#else

    Argz<std::string> parser(argc, argv);    
    parser.addOptionSingle('h', "Display help message")
          .addOptionSingleValue('p', "assets path")
          .addOptionDoubleValue('P', "path", "assets path")
          .addOptionSingleValue('r',"Resolution WidthxHeight")
          .addOptionDoubleValue('R',"resolution", "Resolution WidthxHeight");
    Argument<std::string> arg;
    std::string path;
    int value = 0;
    int tw = 960, th = 720;
    try {
        while((value = parser.proc(arg)) != -1) {
            switch(value) {
                case 'h':
                case 'v':
                    parser.help(std::cout);
                    exit(EXIT_SUCCESS);
                    break;
                case 'p':
                case 'P':
                    path = arg.arg_value;
                break;
                case 'r':
                case 'R': {
                    auto pos = arg.arg_value.find("x");
                    if(pos == std::string::npos)  {
                        mx::system_err << "Error invalid resolution use WidthxHeight\n";
                        mx::system_err.flush();
                        exit(EXIT_FAILURE);
                    }
                    std::string left, right;
                    left = arg.arg_value.substr(0, pos);
                    right = arg.arg_value.substr(pos+1);
                    tw = atoi(left.c_str());
                    th = atoi(right.c_str());
                }
                break;
            }
        }
    } catch (const ArgException<std::string>& e) {
        mx::system_err << e.text() << "\n";
    }
    if(path.empty()) {
        mx::system_out << "mx: No path provided trying default current directory.\n";
        path = ".";
    }
    try {
        MainWindow main_window(path, tw, th);
        main_window.loop();
    } catch(const mx::Exception &e) {
        mx::system_err << "mx: Exception: " << e.text() << "\n";
        mx::system_err.flush();
        exit(EXIT_FAILURE);
    } 
#endif

    return 0;
}