Create a Game Engine: Part VI Rendering Triangle

In this part we are going to create the Triangle class which inherits the Model class discussed in the previous tutorial. We will have a Create method where we copy all the code required to create the triangle from this tutorial. Of course we need to adjust a few things to work. I won’t explain VBOs and VAOs all over again, there are a few tutorials back on this subject.

Now, with this modular structure it’s easy for us to add new models to our program and we don’t have to modify the engine to support new types. I will actually add a square just like the triangle to show how easy it is. In later chapters we will use this Create method to load more complex models from files and render them just like the triangle.

engine_triangle_modelsManager

First let’s add a new header file called Triangle.h in the Models folder and make this class  inherit the Model class


#pragma once
#include "Model.h"
namespace Rendering
{
  namespace Models
  {
    class Triangle : public Model
    {
      public:
        Triangle();
       ~Triangle();

        void Create();
        virtual void Update() override final;
        virtual void Draw() override final;
    };
  }
}

And now let’s see the Triangle.cpp file which implement these methods:

#include "Triangle.h"
using namespace Rendering;
using namespace Models;

Triangle::Triangle()
{
}

Triangle::~Triangle()
{
 //is going to be deleted in Models.cpp (inheritance)
}

void Triangle::Create()
{
  //this is just copy past from
 // http://in2gpu.com/2014/12/19/create-triangle-opengl-part-iii-attributes/
	GLuint vao;
	GLuint vbo;

	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	std::vector<VertexFormat> vertices;
	vertices.push_back(VertexFormat(glm::vec3(0.25, -0.25, 0.0),
		glm::vec4(1, 0, 0, 1)));
	vertices.push_back(VertexFormat(glm::vec3(-0.25, -0.25, 0.0),
		glm::vec4(0, 1, 0, 1)));
	vertices.push_back(VertexFormat(glm::vec3(0.25, 0.25, 0.0),
		glm::vec4(0, 0, 1, 1)));

	glGenBuffers(1, &vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(VertexFormat) * 3, &vertices[0], GL_STATIC_DRAW);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexFormat),
                           (void*)0);
	glEnableVertexAttribArray(1);
        // you can use offsetof to get the offset of an attribute
	glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(VertexFormat),
                           (void*)(offsetof(VertexFormat, VertexFormat::color)));
	glBindVertexArray(0);

        //here we assign the values
	this->vao = vao;
	this->vbos.push_back(vbo);

}

void Triangle::Update()
{
  //for triangle there is nothing to update for now
}

void Triangle::Draw()
{
 	glUseProgram(program);
	glBindVertexArray(vao);
	glDrawArrays(GL_TRIANGLES, 0, 3);
}
With our new engine, please delete GameModels.h and GameModels.cpp (used in the beginning to create the triangle) because we don’t need them anymore. 

As you might guess, we need a place where we can keep our models and loop over them when we render the scene. Let’s call this class ModelsManager. This manager will communicate with ShaderManager because we need to pass the shader program to our models and it will be instanced in SceneManager. Here we will have a map structure to hold our models and Draw and Update methods used to loop over the models.

So let’s add Models_Manager.h and Models_Manager.cpp in the Managers folder. So far your project should look like this (notice the triangle.cpp and triangle.h in the Models folder):

folder_strucutre_models

Triangle and Models_Manager

 

Now let’s see the code for Models_Manager.h:


#pragma once
#include <map>
#include "Shader_Manager.h"
#include "../Rendering/IGameObject.h"
#include "../Rendering/Models/Triangle.h"

using namespace Rendering;
namespace Managers
{
  class Models_Manager
   {
     public:
         Models_Manager();
        ~Models_Manager();

         void Draw();
         void Update();
         void DeleteModel(const std::string& gameModelName);
         const IGameObject& GetModel(const std::string& gameModelName) const;

     private:
        std::map<std::string, IGameObject*> gameModelList;
   };
}

Note!!! As you can see we are using a map container to store our models. For little games that’s fine (it’s more elegant) but if you have a huge game with lots of models to render this can be a problem from a performance point of view and map should be avoided and replaced with a vector. STL map wasn’t build for fast iterations. You can always profile and see what’s the bottleneck. I wrote more about this here: http://in2gpu.com/2015/03/09/iterating-over-stl-map-stl-vector-stl-list-and-vector/

We will create the triangle object right in the constructor, but if you want you can create a specific method for this. As I said earlier, in Draw and Update we loop over our objects and draw/update them. Delete will delete a specific model and GetModel will return a model. Here is the code for Models_Manager.cpp note that I used some C++11 syntax to iterate over the map.


#include "Models_Manager.h"

using namespace Managers;
using namespace Rendering;

Models_Manager::Models_Manager()
{
 //triangle game object
  Models::Triangle* triangle = new Models::Triangle();
  triangle->SetProgram(Shader_Manager::GetShader("colorShader"));
  triangle->Create();
  gameModelList["triangle"] = triangle;

}

Models_Manager::~Models_Manager()
{
  //auto -it's a map iterator
  for (auto model: gameModelList)
  {
    delete model.second;
  }
  gameModelList.clear();
}

void Models_Manager::DeleteModel(const std::string& gameModelName)
{
  IGameObject* model = gameModelList[gameModelName];
  model->Destroy();
  gameModelList.erase(gameModelName);
}

const IGameObject& Models_Manager::GetModel(const std::string& gameModelName) const
{
  return (*gameModelList.at(gameModelName));
}

void Models_Manager::Update()
{
   //auto -it's a map iterator
  for (auto model: gameModelList)
  {
    model.second->Update();
   }
}

void Models_Manager::Draw()
{
   //auto -it's a map iterator
   for (auto model : gameModelList)
   {
     model.second->Draw();
   }
}

The last thing we have to do in order to see that this basic game engine architecture works it is to link Models_Manager with Scene_Manager created in the previous tutorial. So we have to to create a Models_Manager object in Scene_Manager and call Draw and Update methods.

First let’s create the object in Scene_Manager.h:


#pragma once
#include "Shader_Manager.h"
#include "Models_Manager.h" //don't forget to include
#include "../Core/Init/IListener.h"

  class Scene_Manager : public Core::IListener
 {
   ....
   private:
    Managers::Shader_Manager* shader_manager;
    Managers::Models_Manager* models_manager;
 };
  ....

And now let’s see Scene_Manager.cpp


Scene_Manager::Scene_Manager()
{
  ....
  //just after shader_manager->CreateProgram call
  // create the Models_Manager
  models_manager = new Models_Manager();
}

Scene_Manager::~Scene_Manager()
{
  delete shader_manager;
  delete models_manager;
}

void Scene_Manager::notifyBeginFrame()
{
   //Our triangle is static and doesn't update anything
   //but we need to call the Update function every frame
   // for future models (including the current triangle)
   models_manager->Update();
}

void Scene_Manager::notifyDisplayFrame()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glClearColor(0.0, 0.0, 0.0, 1.0);

  models_manager->Draw();
}
.....

If everything is fine you should end up with our old triangle on a black screen.

Triangle OpenGL

Render triangle with the new architecture

In the next part of this Chapter and engine series I will add a new model to the scene (a quad/square) to test this architecture.


blog comments powered by Disqus