I come from a C# background and recently have been trying to improve my C++ and OpenGL. I've hacked together a basic 3d engine and am looking for feedback as to how to improve it.
One bit I'm most unsure about is the render loop for multiple objects, I've set it up like this and would love some feedback on it. For instance I've no idea if my use of pointers is optimal.
scene.hpp:
namespace r3d
{
class scene
{
public:
scene(int width, int height);
void update();
void exit();
void add_object(r3d::game_object *);
void add_light(r3d::light *);
GLFWwindow * window;
r3d::camera * main_camera;
};
}
This is the main loop in my scene.cpp file:
void scene::update()
{
time->update();
// render scene loop
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
behaviour * script;
for(auto it = game_objects.begin(); it != game_objects.end(); ++it)
{
if(!(*it)->enabled) { continue; }
script = (behaviour *)(*it)->get_component(constants::BEHAVIOUR);
if(script)
{
script->update();
}
}
mesh_renderer * render_obj;
for(auto it = game_objects.begin(); it != game_objects.end(); ++it)
{
if(!(*it)->enabled) { continue; }
render_obj = (mesh_renderer *)(*it)->get_component(constants::RENDER_OBJECT);
// use our shader
render_obj->material->shader->bind();
// set camera uniforms
main_camera->set_uniforms(render_obj->material->shader, (*it)->get_transform());
// set light uniforms
lights.front()->set_uniforms(render_obj->material->shader);
// bind texture
render_obj->material->bind();
// bind buffers and draw elements
render_obj->render();
}
glfwSwapBuffers(window);
glfwPollEvents();
}
shader.hpp:
namespace r3d
{
class shader
{
public:
enum id { UNLIT_VERT_COLOR, UNLIT_TEXTURE, DIFFUSE };
shader(id);
shader(std::string, std::string);
void bind();
id shader_id;
GLuint program;
GLuint mvp_matrix;
GLuint view_matrix;
GLuint model_matrix;
GLuint texture_sampler;
GLuint light_world_pos;
GLuint light_color;
GLuint light_intensity;
private:
void init(std::string, std::string);
};
}
shader.cpp:
shader::shader(std::string vert, std::string frag)
{
init(vert, frag);
}
void shader::init(std::string vert, std::string frag)
{
program = load_shader(vert.c_str(), frag.c_str());
// Get handles for our uniforms
mvp_matrix = glGetUniformLocation(program, "MVP");
view_matrix = glGetUniformLocation(program, "V");
model_matrix = glGetUniformLocation(program, "M");
texture_sampler = glGetUniformLocation(program, "Sampler");
light_world_pos = glGetUniformLocation(program, "LightPosition_worldspace");
light_color = glGetUniformLocation(program, "LightColor");
light_intensity = glGetUniformLocation(program, "LightPower");
glUseProgram(program);
}
void shader::bind()
{
glUseProgram(program);
}
camera.hpp:
namespace r3d
{
class camera : public game_object
{
public:
camera();
camera(int w, int h) : width(w), height(h) {};
void set_uniforms(r3d::shader *, glm::mat4);
float fov = 45;
float near = 0.1f;
float far = 100.0f;
int width = 1024;
int height = 768;
private:
glm::mat4 projection;
glm::mat4 view;
};
}
camera.cpp:
using namespace r3d;
void camera::set_uniforms(r3d::shader * shader, glm::mat4 model)
{
view = glm::lookAt(position, position + forward, up);
projection = glm::perspective(glm::radians(fov), (float)width / (float)height, near, far);
glm::mat4 mvp = projection * view * model;
glUniformMatrix4fv(shader->mvp_matrix, 1, GL_FALSE, &mvp[0][0]);
glUniformMatrix4fv(shader->model_matrix, 1, GL_FALSE, &model[0][0]);
glUniformMatrix4fv(shader->view_matrix, 1, GL_FALSE, &view[0][0]);
}
light.hpp:
namespace r3d
{
class light
{
public:
light();
light(glm::vec3, glm::vec3, float);
void set_uniforms(r3d::shader *);
glm::vec3 position;
glm::vec3 color;
float intensity;
};
}
light.cpp:
light::light(glm::vec3 position, glm::vec3 color, float intensity)
{
this->position = position;
this->color = color;
this->intensity = intensity;
}
void light::set_uniforms(r3d::shader * shader)
{
glUniform3f(shader->light_world_pos, position.x, position.y, position.z);
glUniform3f(shader->light_color, color.x, color.y, color.z);
glUniform1f(shader->light_intensity, intensity);
}
mesh_renderer.hpp:
namespace r3d
{
class mesh_renderer : public component
{
public:
mesh_renderer();
mesh_renderer(std::string, r3d::material *);
void render();
std::vector<unsigned short> indices;
std::vector<glm::vec3> vertices;
std::vector<glm::vec2> uvs;
std::vector<glm::vec3> normals;
GLuint vertex_array_object;
GLuint vertex_buffer;
GLuint uv_buffer;
GLuint normal_buffer;
GLuint indices_buffer;
r3d::material * material;
};
}
mesh_renderer.cpp:
using namespace r3d;
mesh_renderer::mesh_renderer(std::string model_path, r3d::material * material)
{
name = "mesh_renderer";
this->material = material;
// Read file
load_mesh(model_path.c_str(), indices, vertices, uvs, normals);
// Vertex Array Object
glGenVertexArrays(1, &vertex_array_object);
glBindVertexArray(vertex_array_object);
// Vertex buffer
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
// UV buffer
glGenBuffers(1, &uv_buffer);
glBindBuffer(GL_ARRAY_BUFFER, uv_buffer);
glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(glm::vec2), &uvs[0], GL_STATIC_DRAW);
// Normal buffer
glGenBuffers(1, &normal_buffer);
glBindBuffer(GL_ARRAY_BUFFER, normal_buffer);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals[0], GL_STATIC_DRAW);
// Indices buffer
glGenBuffers(1, &indices_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW);
// Remove bindings
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
printf("Add mesh_renderer: %s [indices: %lu]\n", model_path.c_str(), indices.size());
}
void mesh_renderer::render()
{
// vao
glBindVertexArray(vertex_array_object);
// vertex buffer
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// uv buffer
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, uv_buffer);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
// normal buffer
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, normal_buffer);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, (void*)0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glBindVertexArray(0);
}
Full source code is here if anyone wants to look at/review any further:
https://github.com/ryanw3bb/r3d
Any feedback or comments would be hugely appreciated! :)