Home > プログラミング > SDL + OpenGL で

SDL + OpenGL で

Javaではいささか遅すぎるので, SDL + OpenGL で書き直し.

とりあえず,SDL日本語ドキュメントを参照しつつてきとうに組んでみた.画像表示部分には OpenGL を使うので,そっちもてきとうに調べるべし.ちなみに,SDL を入れて C++ でコンパイルしようとしたら SDL_audio.h の 97 行目で文句を言われたので,

 <- void (SDLCALL *filters[10])(struct SDL_AudioCVT *cvt, Uint16 format);
 -> void (SDLCALL *filters)(struct SDL_AudioCVT *cvt, Uint16 format);

こんな書き換えをしてやった.audio 使わない限り大丈夫でしょう.

んで,プログラムの感じは以下のとおりで.まず main 関数は SDL の初期化(VIDEOのみ)をして GL の属性を指定して,Window を作ったら他の初期化をしてからループしてメッセージ処理と描画処理をする.

int main(int argc,char *argv[])
{
     /* SDL初期化 */ 
    if(SDL_Init(SDL_INIT_VIDEO)<0){
        cerr << "SDL Initialization failed." << endl; 
        exit(-1);
    }
    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, 1 );
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
    /* 画面作成 */
    int flags = SDL_OPENGL;// | SDL_FULLSCREEN;
    screen = SDL_SetVideoMode(WindowSizeX,WindowSizeY,ColorDepth,flags);
    if(screen == NULL){    
        cerr << "Window Creation failed." << endl; 
        SDL_Quit();
        exit(-2);
    }
    init();
    /* メインループ */
    do{
        processEvent();
        updateScreen();
    } while(!done); 
    SDL_Quit();
    return 0;  
}

メッセージ処理はいつもの Windows のと変わるはずも無く.

int processEvent()
{
    SDL_Event e;
    if (SDL_PollEvent(&e) != 0){  // event がある?
        switch(e.type){
        case SDL_QUIT:
            done = true;
            break;
        case SDL_KEYDOWN:
            switch(e.key.keysym.sym){
            case SDLK_1:                 // アルゴリズム選択
                rotType = 1;
                break;
            case SDLK_2:
                rotType = 2;
                break;
            case SDLK_3:
                rotType = 3;
                break;
            case SDLK_4:
                rotType = 4;
                break;
            case SDLK_p:                 // 描画・非描画フリップ
                painting = !painting;
                break;
            case SDLK_u:                // 描画スキップ
                stepsPerDraw++;
                break;
            case SDLK_d:
                if(stepsPerDraw>1) stepsPerDraw--;
                break;
            case SDLK_f:                 // fps を描画
                fpsDrawFlag = true;
                break;
            case SDLK_q:
            case SDLK_ESCAPE:
                done = true;
                break;
            }
            break;
        }
    }
}

描画部分は fps を計算しつつ適当にと.ゲームなら fps の固定のための wait があるでしょう.

int updateScreen()
{
    static int steps = 0;
    static int time = SDL_GetTicks();
    if(steps%stepsPerDraw==0)
        drawImage();
    rotate();
    steps++;
    int t = SDL_GetTicks();
    if(t - time >= 1000 / freqFPSUpdate){
        totalSteps-=stepsHist[sp];
        totalMillis-=millisHist[sp];
        stepsHist[sp] = steps;
        millisHist[sp] = (int)(t - time);
        totalSteps+=stepsHist[sp];
        totalMillis+=millisHist[sp];
        sp = (sp + 1) % millisHistLength;
        steps = 0;
        fps = totalSteps * 100000 / totalMillis; // fps*100です
        time = t;
    }
}

OpenGL での描画はこんな感じで.glDrawPixels でフレームバッファをダイレクトに転送.その際,GL_LUMINANCE で輝度情報のみにしているが,GL_RGB とかにすると3byte / 1 ドット でカラーの転送とかできる.

画像転送後には文字列を描画している.

void drawImage()
{
    static char str[64]={0};
    static char *s1 = str;
    sprintf(s1, "%d.%d fps (skip %d) algo-%d", fps/100, fps%100, stepsPerDraw-1, rotType);
    if(painting){
        glClear(GL_COLOR_BUFFER_BIT);
        //glDrawPixels(ImageWidth , ImageHeight, GL_BLUE, GL_UNSIGNED_BYTE , rotatedPixels);
        glDrawPixels(ImageWidth , ImageHeight, GL_LUMINANCE, GL_UNSIGNED_BYTE , rotatedPixels);
        //glFlush();
        drawString(s1);
        SDL_GL_SwapBuffers();
    } else {
        if(fpsDrawFlag){
            glClear(GL_COLOR_BUFFER_BIT);
            fpsDrawFlag = false;
            drawString(s1);
            SDL_GL_SwapBuffers();
        }
    }
}

んで,文字列の描画には ASCII だけでいいなら glut のビットマップフォントが使えるのでそれを使う.font_init で各キャラクタの書き方を指定しておいて,あとでglCallListsで使う.font_init のループでは文字のASCIIコードに対してその文字の描画コードを対応付けているので,glCallLists でASCIIコード列であるところの文字列を渡すとうまい具合に文字が表示できる(リスト生成時にスクランブルしとくと楽しいかも).ちなみに,日本語も使えるようにするにはここら辺が参考になるかも.

void font_init()
{
    base = glGenLists(128);
    for(int i=0;i<128;i++){
        glNewList(base+i, GL_COMPILE);
        glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, i);
        glEndList();
    }
    glListBase(base);
}
void drawString(const char *out, float r=1.0f, float g=1.0f, float b=1.0f)
{
     glPushAttrib(GL_ALL_ATTRIB_BITS);
     glColor3f(r, g, b);
     glRasterPos2i(whiteSpace/2, WindowSizeY-statusFieldY+5);
     glCallLists( strlen(out) , GL_BYTE, out);
     glPopAttrib();
}

あとはまあ,初期化部分ののこりをば.glOrtho のことろは 2D の描画の座標とスクリーン上の座標のスケールをあわせるためのもの.これをやっとかないと glRasterPos2i で予想以上に座標が動きすぎる.

void init()
{
     glViewport(0,0, WindowSizeX, WindowSizeY);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity(); 
    glOrtho(0.0, (GLdouble) WindowSizeX , 0.0, (GLdouble) WindowSizeY , -1.0, 1.0);
    glRasterPos2i(whiteSpace, whiteSpace);
    fps = 0;
    steps = 0;
    totalSteps = 0;
    totalMillis = 0;
    for(int i = 0; i < millisHistLength; i++){
        stepsHist[i] = 0;
        millisHist[i] = 0;
    }
    sp = 0;
    font_init();
    glDisable( GL_DEPTH_TEST );
    glDisable( GL_LIGHTING );
}

んで,最後にコンパイルはこんな感じで.オプションが長い...

g++ ImageRotator.cpp -o ImageRotator `sdl-config --cflags --libs` -lglut32 -lopengl32 -lglu32 -mno-cygwin -O3

とりあえず動くものをここにおいておこう.インクルードが <SDL/SDL.h> でなく <SDL.h> でないとだめかも.

★下記に2つの英単語をスペースで区切って入力してください

Home > プログラミング > SDL + OpenGL で

Search
Feeds

Page Top