2

I've been trying to write something that will let me easily manage OpenGL using classes.

I took an approach of having a Drawable class [which shapes/etc would inherit from, and override a draw function], and then using a Controller class to iterate through a table of Drawable classes and draw them all. The only issue that I've noticed that the draw() method is being called from the Drawable class, instead of the Rectangle class. ??

    class Drawable {
        public:
            void draw();
    };
    class Rectangle : public Drawable {
        public:
            void draw();
    };
    class Controller {
        public:
            Drawable ents[200];
            int ent_count;
            void frame();
            void append(Drawable item); // this will add an object onto the list
            Controller();
    };
    void Drawable::draw() {
        // this is the default drawing function, which should be overridden
    }
    void Rectangle::draw() {
        // gl functions here
    }
    void Controller::frame() {
        for(int i=0;i<ent_count,i++) {
            ents[i].draw(); // draw all entities on the list
        }
        // here, a timer would loop back the frame() function
    }
    void Controller::append(Drawable item) {
         ents[ent_count++]=item;
    }
    int main(void) {
         Controller main_controller; // create a controller
         Rectangle rect; // create a rectangle
         main_controller.append(rect); // insert rectangle into controller list
         main_controller.frame(); // start the frame loop
    }

[if there are minor typing errors in that, it is because it was written as a summary of the method.] This method that I've tried to use has not been very successful, and I'm pretty sure it has to do with inheritance. Any ideas? Entire source code:

#include <iostream>
#include <GL/glfw.h>
#include <GL/gl.h>
class Drawable {
      public:
             int x,y;
             void draw();
             void frame();
             void create();
             void destroy(); 
};
void Drawable::create() {

}
void Drawable::draw() {
}
class Rectangle : public Drawable {
      public:
      int w,h;
      unsigned short r,g,b;
      Rectangle(int x,int y, int w, int h, unsigned short r, unsigned short g, unsigned short b);
      void draw();
};
void Rectangle::draw() {
     glColor3ub(r,g,b);
     glBegin(GL_QUADS);
     glVertex2i(x,y);
     glVertex2i(x+w,y);
     glVertex2i(x+w,y+h);
     glVertex2i(x,y+h);
     glEnd();
}
Rectangle::Rectangle(int x,int y, int w, int h, unsigned short r, unsigned short g, unsigned short b) {
                         this->x=x;
                         this->y=y;
                         this->w=w;
                         this->r=r;
                         this->g=g;
                         this->b=b;
}
class Controller {
public:
       Controller(int w,int h,int fsaa,bool fs,bool vsync,const char* title);
       bool running;
       int frame_limit;
       Drawable entity[200];
       int entity_count;
       void fev();
       void begin();
       void bind();
       void append(Drawable item);
      };
Controller::Controller(int w,int h,int fsaa,bool fs,bool vsync,const char* title) {
                          int fullscreen= (fs ? GLFW_FULLSCREEN : GLFW_WINDOW);
                          bool window=glfwOpenWindow(w,h,0,0,0,0,10,10,fullscreen);
                          glfwSetWindowTitle(title);
                          frame_limit=120;
                          entity_count=0;
                          std::cout << (window ? "Successfully initialized a window.\n" : "Error initializing window!\n");
       }
void Controller::begin() {
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     glOrtho(0,640,480,0,0,5);
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     glClearColor(0.4f,0.4f,0.4f,1.0f);
     running=true;
     fev();
}
void Controller::append(Drawable item) {
     entity[entity_count++]=item;
}
void Controller::fev() {
     glClear(GL_COLOR_BUFFER_BIT);
     for (int i=0;i<entity_count;++i) {
          entity[i].draw();
          }
     glfwSwapBuffers();
     if (frame_limit>0) {
        glfwSleep(1000/frame_limit*0.001);
     }
     if (running) {
        if (glfwGetKey(GLFW_KEY_ESC) || !glfwGetWindowParam(GLFW_OPENED)) {
           running=false;
        }
        fev();
     } else {
       std::cout << "terminated!";
}
}
int main(void) {
    glfwInit();
    Controller main(640,480,0,false,false,"WindTitle");
    Rectangle rect(50,50,50,50,50,50,50);
    main.append(rect);
    main.begin();
}
jtst
  • 312
  • 4
  • 16
  • You might want to consider looking at existing C++ wrappers, like OGLplus: http://oglplus.org/ – paulsm4 Jan 02 '13 at 00:21
  • 1
    As a note, remember that OpenGL is a finite state machine. So you will have to make sure all of your class methods/classes leave that machine in what you consider a "safe" state (or "expected" state) – RageD Jan 02 '13 at 00:22
  • Try making draw() virtual. – ApproachingDarknessFish Jan 02 '13 at 00:24
  • It had no effect when I made the draw() virtual both in Rectangle and in Drawable. – jtst Jan 02 '13 at 00:25
  • When posting a question about errors, it's always helpful to actually include the errors you get. It should be complete and unedited. It will also be good if you could point out in the posted source the lines the errors are about. – Some programmer dude Jan 02 '13 at 00:25
  • Well, to be honest, there are no errors, it's just that the draw() method in the Drawable object is called instead of that in Rectangle. – jtst Jan 02 '13 at 00:26
  • 1
    And a helpful tip: Please don't use raw arrays, use [`std::vector`](http://en.cppreference.com/w/cpp/container/vector). It will make your life much easier in the long run, – Some programmer dude Jan 02 '13 at 00:27
  • You say it has been "giving you errors left and right"? And the problem with `Drawable::draw` being called is because it's not `virtual`. – Some programmer dude Jan 02 '13 at 00:28
  • Fixed the question, and as I previously replied, making Drawable::draw() virtual had no effect, as well as Rectangle::draw() virtual. – jtst Jan 02 '13 at 00:29
  • You'd only need to make Drawable::draw() virtual here. Scratching this out would bring you farther from the solution. – E_net4 Jan 02 '13 at 00:30

3 Answers3

4

I've been trying to write something that will let me easily manage OpenGL using classes.

Newbies often try this. But OpenGL really doesn't translate well into OOP. The problem is, that it's a finite state machine and to map properly to OOP you'd have to to a lot of state tracking between the different classes and instances.

I myself tried at least 3 times to abstract OpenGL into a OOP scheme. It always broke in some way.

Which is not to say that you can not use OOP with OpenGL. You can't just map OpenGL concepts 1:1 into classes.


Regarding your actual problem: Use virtual functions.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • I'm afraid this answer is focusing too much on a different matter and missing the main root of the problem. Even though there is much to talk about whether an OO approach to OpenGL is feasible or a good idea. – E_net4 Jan 02 '13 at 00:46
  • Wouldn't it be space-consuming to write complex games/other graphical applications without a sort of base-engine like this? It'd be very difficult to manage dynamic objects, and the code would get very long, and very cluttered. – jtst Jan 02 '13 at 00:50
  • @metredigm There are plenty of game and graphics libraries out there that you can use without making your own. But they all usually create a full layer of abstraction over OpenGL, rather than making an OO version of it. – E_net4 Jan 02 '13 at 00:54
  • 1
    True, but I like to make my own things, even if they are inefficient. I just find it fun to try and tackle things like this. – jtst Jan 02 '13 at 00:58
  • @metredigm: Of course most game engines are written in C++ and make heavy use of classes. But the render pipelines are surprisingly flat normally. I recommend looking at the Doom3 source code. – datenwolf Jan 02 '13 at 01:05
3

As others have mentioned, it would be best to try to use some of the existing wrappers.

That said, you need to use pointers for your list of entities. You are having issues with slicing.

As some of the comments mentioned, you need to make Drawable::draw() virtual so calling draw() on a Drawable will call through to the child implementation. That said, because you are adding your Drawables to a an list of objects, instead of a list of pointers to objects, the objects are being sliced. This means that they are being converted from the child type back into Drawable objects, removing the extra information about specific types.

So instead of this:

Drawable ents[200];

You should do something like this:

std::vector<Drawable*> ents;

or if you have C++11 or Boost:

std::vector<std::shared_ptr<Drawable>> ents;

And your append method would take a reference.

void Controller::append(Drawable &item) {
     ents[ent_count++] = &item;
}

or

void Controller::append(Drawable &item) {
  ents.push_back(&item);
}

or

void Controller::append(std::shared_ptr<Drawable> item) {
  ents.push_back(item);
}

And your render loop would be like this:

for (int i = 0; i < ents.size(); ++i) {
  ents[i]->draw();
}

This loop could also be cleaned up to use iterator.

Community
  • 1
  • 1
loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
  • If you have _Boost_ then `vector_ptr` may be a better tool for the job – K-ballo Jan 02 '13 at 00:40
  • @metredigm I added some examples, are those helpful? – loganfsmyth Jan 02 '13 at 00:48
  • This answer is good considering the question. However, as others mentioned it is better to look into packages like [OPGLplus](http://http://oglplus.org/) before trying to wrap OpenGL into an OOP interface. – meyumer Jan 02 '13 at 00:49
  • Thank you! The drawing function in Rectangle is now being called, now I just need to fix my rectangle vertices.. [fixed, i forgot to define the height in the Rectangle constructor.] – jtst Jan 02 '13 at 00:51
0

You might want to have a look at Coin3D. It is a large rich set of C++ class wrappers around OpenGL. You clould either look at how they do things of just use it. There are libraries for integration with various windowing systems (Qt, Windows, Cocoa, ...).

emsr
  • 15,539
  • 6
  • 49
  • 62