2
\$\begingroup\$

I'm coding a wrapper for OpenGL 3.3, and I have some questions about the design of my Shape class. To be short, It's a class that englobes a VBO, a VAO, and an EBO if some indices are given in the constructor.

The problem is that the class is too heavy. Half of the time, not all the code is used, and the draw function is really not optimized.

I'm looking for ideas to improve my shape class, and use a single class to make a shape with vertices, or with verticies and indices.

Here's the code

shape.hpp

#pragma once

#include <initializer_list>
#include <iostream>
#include <memory>
#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/camera.hpp>
#include <pck/color.hpp>
#include <pck/drawable.hpp>
#include <pck/material.hpp>
#include <pck/program.hpp>
#include <pck/vertices.hpp>

namespace pck
{
class Light;

class Shape : public Drawable
{
    public:
        Shape(glm::vec3 p, std::shared_ptr<Vertices> v, Color c);
        Shape(glm::vec3 p, std::shared_ptr<Vertices> v, std::initializer_list<GLuint> i, Color c);
        ~Shape();

        void set_program(std::unique_ptr<Program> p);
        void set_texture(std::string p);
        void set_color(Color c);
        void set_VBO(GLuint* v); 

        void attach_light(std::shared_ptr<Light> l);

        void draw();
        virtual void update() = 0;

        glm::vec3 position;
    protected:
        std::shared_ptr<Vertices> vertices;
        std::vector<GLuint> indices;
        int vertices_number = 0;
        GLuint* VBO = nullptr;
        GLuint VAO = 0;
        GLuint EBO = 0;
        std::unique_ptr<Program> program;
        int tex_width = 0;
        int tex_height = 0;
        unsigned char* image = nullptr;
        GLuint texture = 0;
        Color color;
        std::shared_ptr<Light> light;
        Material material = Material::Basic;
};

}

shape.cpp

#include <pck/shape.hpp>
#include <pck/light.hpp>

namespace pck
{

Shape::Shape(glm::vec3 p, std::shared_ptr<Vertices> v, Color c) :
    position(p), vertices(v), vertices_number(v->vertices_number()), color(c)
{
    // Dirty hack ON
    GLuint tmp = 0;
    VBO = &tmp;
    // Dirty hack OFF

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

    pck::VertShader vs(std::string(R"(  
        #version 330 core
        layout (location = 0) in vec3 position;
        layout (location = 1) in vec3 normal;

        uniform mat4 model;
        uniform mat4 view;
        uniform mat4 projection;

        out vec3 vs_normal;
        out vec3 vs_frag_pos;

        void main()
        {
            gl_Position = projection * view * model * vec4(position, 1.0f);
            vs_normal = normal;
            vs_frag_pos = vec3(model * vec4(position, 1.0f));
        }
    )"));

    pck::FragShader fs(std::string(R"( 
        #version 330 core

        uniform vec3 object_color;
        uniform vec3 light_color;
        uniform vec3 light_pos; 
        uniform vec3 view_pos;

        in vec3 vs_normal; 
        in vec3 vs_frag_pos;

        out vec4 fs_color;

        void main()
        {
            float ambient_strength = 0.1f;
            vec3 ambient = ambient_strength * light_color;

            vec3 norm = normalize(vs_normal);
            vec3 light_dir = normalize(light_pos - vs_frag_pos);  
            float diff = max(dot(norm, light_dir), 0.0);
            vec3 diffuse = diff * light_color;

            float specular_strength = 0.5f;
            vec3 view_dir = normalize(view_pos - vs_frag_pos);
            vec3 reflect_dir = reflect(-light_dir, norm);  
            float spec = pow(max(dot(view_dir, reflect_dir), 0.0), 32);
            vec3 specular = specular_strength * spec * light_color;  

            vec3 result = (ambient + diffuse + specular) * object_color;

            fs_color = vec4(result, 1.0f);
        }
    )"));

    program.reset(new Program(vs, fs));   
}

Shape::Shape(glm::vec3 p, std::shared_ptr<Vertices> v, std::initializer_list<GLuint> i, Color c) :
    position(p), vertices(v), indices(i), vertices_number(indices.size()), color(c) 
{
    // Dirty hack ON
    GLuint tmp = 0;
    VBO = &tmp;
    // Dirty hack OFF

    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, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

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

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

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindVertexArray(0);

    pck::VertShader vs(std::string(R"(  
        #version 330 core
        layout (location = 0) in vec3 position;
        layout (location = 1) in vec3 normal;

        uniform mat4 model;
        uniform mat4 view;
        uniform mat4 projection;

        out vec3 vs_normal;
        out vec3 vs_frag_pos;

        void main()
        {
            gl_Position = projection * view * model * vec4(position, 1.0f);
            vs_normal = normal;
            vs_frag_pos = vec3(model * vec4(position, 1.0f));
        }
    )"));

    pck::FragShader fs(std::string(R"( 
        #version 330 core

        uniform vec3 object_color;
        uniform vec3 light_color;
        uniform vec3 light_pos; 
        uniform vec3 view_pos;

        in vec3 vs_normal; 
        in vec3 vs_frag_pos;

        out vec4 fs_color;

        void main()
        {
            float ambient_strength = 0.1f;
            vec3 ambient = ambient_strength * light_color;

            vec3 norm = normalize(vs_normal);
            vec3 light_dir = normalize(light_pos - vs_frag_pos);  
            float diff = max(dot(norm, light_dir), 0.0);
            vec3 diffuse = diff * light_color;

            float specular_strength = 0.5f;
            vec3 view_dir = normalize(view_pos - vs_frag_pos);
            vec3 reflect_dir = reflect(-light_dir, norm);  
            float spec = pow(max(dot(view_dir, reflect_dir), 0.0), 32);
            vec3 specular = specular_strength * spec * light_color;  

            vec3 result = (ambient + diffuse + specular) * object_color;

            fs_color = vec4(result, 1.0f);
        }
    )"));

    program.reset(new Program(vs, fs));    
}

Shape::~Shape()
{
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, VBO);
    glDeleteBuffers(1, &EBO);
}

void
Shape::set_program(std::unique_ptr<Program> p)
{
    program = std::move(p);
}

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::set_color(Color c)
{
    color = c;
}

void
Shape::set_VBO(GLuint* v)
{
    VBO = v;
}

void
Shape::attach_light(std::shared_ptr<Light> l)
{
    light = l;
}

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

    program->use();

    if(light)
    {
        GLint object_color_loc = glGetUniformLocation(program->get(), "object_color");
        GLint light_color_loc  = glGetUniformLocation(program->get(), "light_color");
        glUniform3fv(object_color_loc, 1, glm::value_ptr(color.get_vec3()));
        //glUniform3f(object_color_loc, color.r, color.g, color.b);
        GLint light_pos_loc = glGetUniformLocation(program->get(), "light_pos");
        glUniform3f(light_pos_loc, light->position.x, light->position.y, light->position.z);         
        GLint view_pos_loc = glGetUniformLocation(program->get(), "view_pos");
        glUniform3f(view_pos_loc, pck::Global::camera->x(), pck::Global::camera->y(), pck::Global::camera->z());  

        Color c = light->get_color();
        glUniform3f(light_color_loc, c.r, c.g, c.b);
    }

    update();

    glBindVertexArray(VAO);

    if(EBO == 0)
        glDrawArrays(GL_TRIANGLES, 0, vertices_number);
    else
        glDrawElements(GL_TRIANGLES, vertices_number, GL_UNSIGNED_INT, 0);

    glBindVertexArray(0);
}

}

drawable.hpp

#pragma once

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

namespace pck
{

class Drawable
{
    public:
        virtual void set_VBO(GLuint*) = 0;

        virtual void draw() = 0;
        virtual void update() = 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.