1
\$\begingroup\$

To learn OpenGL, I am writing a little wrapper using the (almost) latest C++ standard. I would like a review on the shape and vertices class, what I did wrong, what I can improve, and what I can change.

vertices.hpp

#pragma once

#include <cassert>
#include <vector>

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

namespace pck
{

class Vertices
{
    public:
        Vertices(std::initializer_list<GLfloat> p);
        Vertices(std::initializer_list<GLfloat> p, std::initializer_list<GLfloat> c);
        Vertices(std::initializer_list<GLfloat> p, std::initializer_list<GLfloat> c, std::initializer_list<GLfloat> t);

        int vertices_number() { return vertices_number_; };
        GLfloat* data() { return vertices_.data(); };
        size_t size() { return vertices_.size(); };
        GLfloat* positions() { return positions_.data(); };
        size_t positions_size() { return positions_.size(); };
        GLfloat* colors() { return colors_.data(); };
        size_t colors_size() { return colors_.size(); };
        GLfloat* textures() { return textures_.data(); };
        size_t textures_size() { return textures_.size(); };

    private:
        void init();

        int vertices_number_;
        std::vector<GLfloat> positions_;
        std::vector<GLfloat> colors_;
        std::vector<GLfloat> textures_;
        std::vector<GLfloat> vertices_;

};

}

vertices.cpp

#include <pck/vertices.hpp>
#include <iostream>

using namespace pck;

Vertices::Vertices(std::initializer_list<GLfloat> p) :
    positions_(p)
{
    init();
}


Vertices::Vertices(std::initializer_list<GLfloat> p, std::initializer_list<GLfloat> c) :
    positions_(p), colors_(c)
{
    init();
}

Vertices::Vertices(std::initializer_list<GLfloat> p, std::initializer_list<GLfloat> c, std::initializer_list<GLfloat> t) :
    positions_(p), colors_(c), textures_(t)
{
    init();
}

void
Vertices::init()
{
    assert(positions_.size() % 3 == 0 && "Error positions size");
    assert(colors_.size() % 3 == 0 && "Error colors size");
    assert(textures_.size() % 2 == 0 && "Error textures size");

    for(size_t i = 0; i < (positions_.size() / 3); i++)
    {
        for(size_t j = 0; j < 3; j++)
            vertices_.push_back(positions_[i * (positions_.size() / (positions_.size() / 3)) + j]);

        for(size_t k = 0; k < 3; k++)
        {
            if(colors_.size() != 0)
                vertices_.push_back(colors_[i * (colors_.size() / ((colors_.size() / 3))) + k]);
            else
                vertices_.push_back(0);
        }

        for(size_t l = 0; l < 2; l++)
        {
            if(textures_.size() != 0)
                vertices_.push_back(textures_[i * (textures_.size() / (textures_.size() / 2)) + l]);
            else
                vertices_.push_back(0);
        }
    }

    vertices_number_ = positions_.size() / (positions_.size() / 3);
}

drawable.hpp

#pragma once

namespace pck
{

class Drawable
{
    public:
        virtual void draw() = 0;
};

}

shape.hpp

#pragma once

#include <string>
#include <vector>

#define GLEW_STATIC
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <SOIL/SOIL.h>

#include <pck/drawable.hpp>
#include <pck/program.hpp>
#include <pck/vertices.hpp>

namespace pck
{

class Shape : public Drawable
{
    public:
        Shape(Vertices* v);
        Shape(Vertices* v, std::initializer_list<GLuint> i);
        ~Shape();

        void set_program(Program* program);
        void set_texture(std::string path);

        void draw();

    private:
        Vertices* vertices_;
        std::vector<GLuint> indices_;
        int vertices_number_;
        GLuint VBO_;
        GLuint VAO_;
        GLuint EBO_;
        Program* program_ = nullptr;
        int tex_width_;
        int tex_height_;
        unsigned char* image_ = nullptr;
        GLuint texture_;
};

}

shape.cpp

#include <pck/shape.hpp>

using namespace pck;

Shape::Shape(Vertices* v) :
        vertices_number_(v->vertices_number()), vertices_(v)
{
    glGenVertexArrays(1, &VAO_);
    glGenBuffers(1, &VBO_);

    glBindVertexArray(VAO_);

    glBindBuffer(GL_ARRAY_BUFFER, VBO_);
    glBufferData(GL_ARRAY_BUFFER, vertices_->size() * sizeof(GLfloat), vertices_->data(), GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0);

}

Shape::Shape(Vertices *v, std::initializer_list<GLuint> i) :
    vertices_number_(indices_.size()), vertices_(v), indices_(i)
{
    glGenVertexArrays(1, &VAO_);
    glGenBuffers(1, &VBO_);
    glGenBuffers(1, &EBO_);

    glBindVertexArray(VAO_);

    glBindBuffer(GL_ARRAY_BUFFER, VBO_);
    glBufferData(GL_ARRAY_BUFFER, vertices_->size() * sizeof(GLfloat), vertices_->data(), GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO_);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_.size() * sizeof(GLfloat), indices_.data(), GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindVertexArray(0);
}

Shape::~Shape()
{
    delete program_;
    glDeleteVertexArrays(1, &VAO_);
    glDeleteBuffers(1, &VBO_);
    glDeleteBuffers(1, &EBO_);
}

void
Shape::set_program(Program* program)
{
    program_ = program;
}

void
Shape::set_texture(std::string path)
{
    glBindVertexArray(VAO_);

    glGenTextures(1, &texture_);
    glBindTexture(GL_TEXTURE_2D, texture_);

    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);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    image_ = SOIL_load_image(path.c_str(), &tex_width_, &tex_height_, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_width_, tex_height_, 0, GL_RGB, GL_UNSIGNED_BYTE, image_);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image_);
    glBindTexture(GL_TEXTURE_2D, 0);
}

void
Shape::draw()
{
    if(texture_ != 0)
        glBindTexture(GL_TEXTURE_2D, texture_);

    if(program_ != nullptr)
        program_->use();

    glBindVertexArray(VAO_);

    if(EBO_ == 0)
        glDrawArrays(GL_TRIANGLES, 0, vertices_number_);
    else
       glDrawElements(GL_TRIANGLES, vertices_number_, GL_UNSIGNED_INT, 0);

    glBindVertexArray(0);
}

An example of utilisation; a square made from 2 triangles with a texture:

#include <iostream>
#include <vector>

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

#include <pck/shape.hpp>
#include <pck/shader.hpp>
#include <pck/program.hpp>
#include <pck/window.hpp>

const GLuint WIDTH = 800, HEIGHT = 600;

int main()
{
    pck::Window window("Pancake Texture Example", WIDTH, HEIGHT);

    pck::Program* prog = new pck::Program(pck::VertShader("texture.vs"), pck::FragShader("texture.fs"));

    pck::Vertices* v = new pck::Vertices({
        // Positions
         0.5f,  0.5f, 0.0f,
         0.5f, -0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        -0.5f,  0.5f, 0.0f
    }, {
        // Colors
         1.0f, 0.0f, 0.0f,
         0.0f, 1.0f, 0.0f,
         0.0f, 0.0f, 1.0f,
         1.0f, 1.0f, 0.0f
    }, {
        // Texture coords
         1.0f, 1.0f,
         1.0f, 0.0f,
         0.0f, 0.0f,
         0.0f, 1.0f
    });

    pck::Shape* s = new pck::Shape(v, {
        0, 1, 3,
        1, 2, 3
    });

    s->set_program(prog);
    s->set_texture("crate.jpg");

    bool quit = false;
    SDL_Event e;

    while(!quit)
    {
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        while( SDL_PollEvent(&e) != 0 )
        {
             if(e.type == SDL_QUIT)
             {
                 quit = true;
             }
             else if(e.type == SDL_KEYDOWN)
             {
                 if(e.key.keysym.sym == SDLK_ESCAPE)
                     quit = true;
             }
        }

        s->draw();

        window.render();
    }

    return 0;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.