バックエンドをSDL+OpenGLとしてguichanを使用する


Visual C++ 2008 Express Editionでビルドしてみました。 公式サイトにはSDLとOpenGLを同時使用しているサンプルが見当たらなかったのでできるかどうか確認しました。 SDLの場合と違うところはSDLのコードをコメントで残すことで示しました。

あと、公式サイトのサンプルプログラムはビジーループなのでウェイト関数 elapse_time を追加しました。 また、画像を簡単に表示できるのか確認するためにMyPaneクラスを追加しています。gcn::Widgetを継承してdraw関数をオーバーライドすれば良いようで。

アルファベットを表示するために、公式サイトのThe basics of Guichan - Guichan(リンク切れになっていたらご容赦)にあるfixedfont.pngをfixedfont.bmpという名前で保存して実行ファイルと同じ場所に置きます。

OpenGL使うので OpenGL32.lib と glu32.lib を忘れずにライブラリに指定。

#include <iostream>
#include <guichan.hpp>
#include <guichan/sdl.hpp>
#include <guichan/opengl.hpp>
#include <guichan/opengl/openglsdlimageloader.hpp>
#include <GL/gl.h>
#include <GL/glu.h>
#include "SDL.h"
#include <cassert>

#define FRAME_PER_SEC 60

SDL_Surface* screen;
SDL_Event event;
gcn::SDLInput* input;
//gcn::SDLGraphics* graphics;
gcn::OpenGLGraphics* graphics;
//gcn::SDLImageLoader* imageLoader;
gcn::OpenGLSDLImageLoader* imageLoader;

class TMyPane : public gcn::Widget
{
public:
    gcn::Image* m_Image;
    void draw(gcn::Graphics* g)
    {
        g->drawImage(m_Image, 0, 0);
    };
};

gcn::Gui* gui;
gcn::Container* top;
gcn::ImageFont* font;
gcn::Label* label;
gcn::Button* button;
gcn::Window* window;
TMyPane* myPane;

void init()
{
    SDL_Init(SDL_INIT_VIDEO);
    //screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE);
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 );
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
    screen = SDL_SetVideoMode(640, 480, 32, SDL_OPENGL);
    SDL_EnableUNICODE(1);
    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);

    //imageLoader = new gcn::SDLImageLoader();
    imageLoader = new gcn::OpenGLSDLImageLoader();
    gcn::Image::setImageLoader(imageLoader);
    //graphics = new gcn::SDLGraphics();
    graphics = new gcn::OpenGLGraphics();
    //graphics->setTarget(screen);
    graphics->setTargetPlane(640, 480);
    input = new gcn::SDLInput();

    font = new gcn::ImageFont("fixedfont.bmp", " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
    gcn::Widget::setGlobalFont(font); 

    gui = new gcn::Gui();
    gui->setGraphics(graphics);
    gui->setInput(input);

    top = new gcn::Container();    
    top->setDimension(gcn::Rectangle(0, 0, 640, 480));
    gui->setTop(top);

    window = new gcn::Window("Hello Window");
    window->setPosition(120, 120);
    top->add(window); 

    label = new gcn::Label("Hello World");
    label->setPosition(0, 0);
    window->add(label);

    button = new gcn::Button("Button1");
    button->setPosition(160, 4);
    button->setBaseColor(gcn::Color(255,255,255));
    window->add(button);

    myPane = new TMyPane;
    myPane->m_Image = imageLoader->load("tmp.png");
    myPane->setWidth(myPane->m_Image->getWidth());
    myPane->setHeight(myPane->m_Image->getHeight());
    assert(myPane->m_Image);
    window->add(myPane, 0, 32);

    window->resizeToContent();
}

/*<---------------------------------------------------------------------->*/
static void elapse_time(void) /* (C)yaneurao 1998-1999 */
{
/* 厳粛かつ正確かつ効率良く時間待ちをする */
    static DWORD lastdraw = 0; /* 前回の描画時刻 */
    /* (不運にも第一発目のtimeGetTime() == 0とかゆーこともあるが、それは構わない) */
    const DWORD dwFPSWait = (1000 * 0x10000)/FRAME_PER_SEC;
    DWORD t, dwFPSWaitTT, dwWait, dwElp;

/*    if (iFPS == 0)
        return ; *//* Non-wait mode */

    t = SDL_GetTicks(); // 現在時刻

    dwFPSWaitTT = (dwFPSWaitTT & 0xffff) + dwFPSWait; // 今回の待ち時間を計算

    /*
     * dwFPSWaitは、待ち時間の小数以下を16ビットの精度で持っていると考えよ
     * これにより、double型を持ち出す必要がなくなる。
     * dwFPSWaitTT = 1000 * 0x10000 / FPS;である
     */

    dwWait = dwFPSWaitTT >> 16; /* 結局のところ、今回は何ms待つねん? */

    /* 1フレーム時間を経過しちょる。ただちに描画しなちゃい! */
    dwElp = (DWORD)(t - lastdraw); /* 前回描画からいくら経過しとんねん? */
    if (dwElp>=dwWait) {
        lastdraw = t;
        return ;
    }

    /* ほな、時間を潰すとすっか! */

    /* まだ時間はたっぷりあるのか? */
    /* 4ms以上消費する必要があるのならば、Sleepする */
    if (dwWait-dwElp >= 4)
        SDL_Delay(dwWait-dwElp-3);
    /* いまdwWait>dwElpなのでdwWait-dwElp>=0と考えて良い */

    /*
     * 95/98/NTで測定したところSleep(1);で1ms単位でスリープするのは可能
     * ただし、実装系依存の可能性もあるのでSleepの精度は3ms以内と仮定
     */
    while ((SDL_GetTicks()-lastdraw)<dwWait) ;
    /* ループで時間を潰す(あまり好きじゃないけど) */

    /* これで、時間つぶし完了! */

    lastdraw += dwWait; /* ぴったりで描画が完了した仮定する。(端数を持ち込まないため) */
}

void run()
{
    bool running = true;

    while (running)
    {
        while(SDL_PollEvent(&event))
        {
            if (event.type == SDL_KEYDOWN)
            {
                if (event.key.keysym.sym == SDLK_ESCAPE)
                {  
                    running = false;
                }
       
            }
            else if(event.type == SDL_QUIT)
            {
                running = false;
            }
      
           input->pushInput(event);
        }

       gui->logic();
       gui->draw();
    
       //SDL_Flip(screen);
       SDL_GL_SwapBuffers();
       elapse_time();
    }
}

void halt()
{
    delete button;
    delete label;
    delete window;
    delete font;
    delete top;
    delete gui;
  
    delete input;
    delete graphics;
    delete imageLoader;
    delete myPane->m_Image;
    delete myPane;
    
    SDL_Quit();
}

int main(int argc, char **argv)
{
    FILE *fp = fopen("log.txt", "w");
    try 
    {
        init();
        run();  
        halt();
    }
    catch (gcn::Exception e)
    {
        std::cout << e.getMessage() << std::endl;
        if(fp)
            fprintf(fp, "%s\n", e.getMessage().c_str());
    }
    catch (std::exception e)
    {
        std::cout << "Std exception: " << e.what() << std::endl;  
    }
    catch (...)
    {
        std::cout << "Unknown exception" << std::endl;
    }
    if(fp)
        fclose(fp);

    return 0;
}

実行するとこうなります。OpenGL使うと何か起動が遅い。


戻る