04 июля 2010

OpenGL: Окно SDL

Привет. Решил я рассказать про OpenGL. Почему бы и нет. Тем более в нескольких статьях, OpenGL мной использовался. Вот и решил что надо бы рассказать. За одно и сам вспомню, что забыл, и узнаю, что не знал.

Начнем мы с простого. Так как для того, что бы что-то нарисовать, будь то прямоугольник, либо сложная трехмерная модель, OpenGL нужно предоставить окно (своего рода холст для рисования), мы начнем именно с создания окна.

К нашему с вами счастью, SDL может предоставить окно для рисования в нем средствами OpenGL. Делается это очень просто. Подготовим для наших экспериментов файл Game.hpp:

#ifndef GAME_HPP__
#define GAME_HPP__

#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <SDL/SDL.h>

class Game
{
public:
  Game(int width = 640, int height = 480, int bpp = 32)
  {
    using std::cerr;
    using std::endl;
    using std::stringstream;
    using std::runtime_error;

    this->m_width  = width;
    this->m_height = height;
    this->m_bpp    = bpp;

    if (SDL_Init(SDL_INIT_VIDEO))
      {
        stringstream err;
        err << "Unable to initialize SDL: " << SDL_GetError() << endl;
        cerr << err.str();
        throw runtime_error(err.str().c_str());
      }
    if (!SDL_SetVideoMode(width, height, bpp, SDL_OPENGL))
      {
        stringstream err;
        err << "Unable to set video mode: " << SDL_GetError() << endl;
        cerr << err.str();
        throw runtime_error(err.str().c_str());
      }

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, true);
  }

  virtual void setup_opengl(int width, int height) {}
  virtual void draw(int dt) {}
  virtual void on_key_down(SDL_KeyboardEvent key) {}
  virtual void on_key_up(SDL_KeyboardEvent key) {}

  void set_caption(std::string caption)
  {
    SDL_WM_SetCaption(caption.c_str(), NULL);
  }

  void run()
  {
    SDL_Event    event;
    bool         done  = false;
    unsigned int dt    = 0;
    unsigned int old   = 0;

    this->setup_opengl(this->m_width, this->m_height);

    while (!done)
      {
        dt = SDL_GetTicks() - old;
        old += dt;
        while (SDL_PollEvent(&event))
          {
            switch (event.type)
              {
              case SDL_KEYDOWN:
                this->on_key_down(event.key);
                break;
              case SDL_KEYUP:
                this->on_key_up(event.key);
                break;
              case SDL_QUIT:
                done = true;
                break;
              }
          }

        this->draw(dt);
        SDL_GL_SwapBuffers();
      }
  }

  void quit()
  {
    SDL_Event push_event;

    push_event.type=SDL_QUIT;
    SDL_PushEvent(&push_event);
  }

  virtual ~Game()
  {
    SDL_Quit();
  }

private:
  int m_width;
  int m_height;
  int m_bpp;
};

#endif /* GAME_HPP__ */

В SDL_SetVideoMode мы используем флаг SDL_OPENGL, для того что бы создать окно, в котором может рисовать OpenGL. Я же говорил, что это легко.

Для настройки других параметров окна, которые влияют на OpenGL, используется SDL_GL_SetAttribute, о функции можно узнать в документации к SDL. В данном случае, мы используем данную функцию для включения двойной буферизации. В общем не поленитесь и посмотрите в документацию, там можно узнать много интересного.

Для того, что бы поэкспериментировать с OpenGL. Достаточно переопределить несколько виртуальных методов в наследнике класса Game.

#if 0
echo "compile $0 to sdlgl..."

g++ -o sdlgl $0 `pkg-config --libs gl glu sdl` \
`pkg-config --cflags sdl` -Wall -Os -s

exit
#endif

#include <iostream>
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL.h>
#include "Game.hpp"

class MyGame : public Game
{
public:
  MyGame(int width=640, int height=480, int bpp=32)
  : Game(width, height, bpp)
  {
    this->m_rotate = 0;
  }

  virtual void setup_opengl(int width, int height)
  {
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(0, width, 0, height);
    glMatrixMode(GL_MODELVIEW);
  }

  virtual void draw(int dt)
  {
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    glTranslatef(150, 150, 0);
    m_rotate = m_rotate > 360?dt/20.0:m_rotate+dt/20.0;
    glRotatef(this->m_rotate, 0, 0, 1);
    glTranslatef(-25, -25, 0);
    glBegin(GL_QUADS);
    {
      glVertex2d( 0.0,  50.0);
      glVertex2d( 0.0,  0.0);
      glVertex2d( 50.0, 0.0);
      glVertex2d( 50.0,  50.0);
    }
    glEnd();
    glFlush();
  }

  virtual void on_key_down(SDL_KeyboardEvent key)
  {
    if (key.keysym.sym == SDLK_ESCAPE)
      {
        this->quit();
      }
  }

  virtual ~MyGame(){}

private:
  float m_rotate;
};

int main(int argc, char *argv[])
{
  MyGame game;
  game.set_caption("(Урок 1)OpenGL: Окно SDL");
  game.run();
  return 0;
}

На этом пока все. В следующий раз мы будем рассматривать исключительно команды OpenGL.