エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

LuaとGLUTをつなげてみる

以前C++で書いたコードLuaを使って実現させてみた。

学習中ということもありかなり力わざでどうにかしています。


まず、GLUTLuaから扱えるようするコードを書きます。
今回はこころみということで、イベントは表示とキー入力とタイマだけ、表示できるのはラインだけにしてます。

#include "lua.h"
#include "lauxlib.h"

#include <GLUT/glut.h>
#include <stdlib.h>

static lua_State *G_L;

static int interval = 200;

static int width  = 320;
static int height = 240;

int vertex2i(lua_State *L)
{
    int x = luaL_checkinteger(L, 1);
    int y = luaL_checkinteger(L, 2);
    glVertex2i(x, y);
    return 0;
}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT);

    glColor3d(0.0, 0.0, 0.0);
    glBegin(GL_LINES);

    lua_getfield(G_L, 1, "display");
    if(lua_isfunction(G_L, -1))
    {
        lua_call(G_L, 0, 0);
    }
    lua_settop(G_L, 1);

    glEnd();

    glFlush();
}

void resize(int w, int h)
{
    glViewport(0, 0, w, h);
    width  = w;
    height = h;
    glLoadIdentity();
    glOrtho(-0.5, w - 0.5, h - 0.5, -0.5, -1.0, 1.0);
}

void keyboard(unsigned char key, int x, int y)
{
    lua_getfield(G_L, 1, "keyboard");
    if(lua_isfunction(G_L, -1))
    {
        lua_pushinteger(G_L, key);
        lua_pushinteger(G_L, x);
        lua_pushinteger(G_L, y);
        lua_call(G_L, 3, 0);
    }
    lua_settop(G_L, 1);
}

void timer(int n)
{
    lua_getfield(G_L, 1, "timer");
    if(lua_isfunction(G_L, -1))
    {
        lua_call(G_L, 0, 0);
    }
    lua_settop(G_L, 1);

    glColor3d(0.0, 0.0, 0.0);
    glBegin(GL_LINES);

    lua_getfield(G_L, 1, "display");
    if(lua_isfunction(G_L, -1))
    {
        lua_call(G_L, 0, 0);
    }
    lua_settop(G_L, 1);

    glEnd();

    glFlush();

    glutTimerFunc(interval, timer, 0);
}

static int main_loop(lua_State *L)
{
    int   argc = 0;
    char* argv[] = { 0 };

    G_L = L;

    lua_getfield(G_L, 1, "width");
    if(lua_isnumber(G_L, -1))
    {
        width = lua_tointeger(G_L, -1);
    }
    lua_getfield(G_L, 1, "height");
    if(lua_isnumber(G_L, -1))
    {
        height = lua_tointeger(G_L, -1);
    }
    lua_getfield(G_L, 1, "interval");
    if(lua_isnumber(G_L, -1))
    {
        interval = lua_tointeger(G_L, -1);
    }
    lua_settop(G_L, 1);

    glutInitWindowPosition(100, 100);
    glutInitWindowSize(width, height);
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA);

    glutCreateWindow(argv[0]);

    glutDisplayFunc(display);
    glutReshapeFunc(resize);
    glutKeyboardFunc(keyboard);
    glutTimerFunc(interval, timer, 0);

    glClearColor(1.0, 1.0, 1.0, 1.0);

    glutMainLoop();

    return 0;
}

static const luaL_Reg glutlib[] =
{
    { "vertex2i",  vertex2i   },
    { "main_loop", main_loop  },
    { NULL,        NULL       }
};

LUALIB_API int luaopen_glut(lua_State *L)
{
    luaL_register(L, "glut", glutlib);
    return 1;
}


次にlinit.cを書き換えて上記のコードをライブラリとして組み込みます。

LUALIB_API int luaopen_glut(lua_State *L); /* これを追加 */

static const luaL_Reg lualibs[] = {
  {"", luaopen_base},
  {LUA_LOADLIBNAME, luaopen_package},
  {LUA_TABLIBNAME, luaopen_table},
  {LUA_IOLIBNAME, luaopen_io},
  {LUA_OSLIBNAME, luaopen_os},
  {LUA_STRLIBNAME, luaopen_string},
  {LUA_MATHLIBNAME, luaopen_math},
  {LUA_DBLIBNAME, luaopen_debug},
  {"glut", luaopen_glut}, /* これを追加 */
  {NULL, NULL}
};


ビルドをする際にはOpenGLGLUTのライブラリもリンクする必要があります。今回はMac OS X (Lepard)上のXcodeで試しているので、プロジェクトにGLUT.frameworkとOpenGL.frameworkを追加してビルドしました。


今回は機能の追加だけなので、ビルドして実行すると、標準の機能の範囲ではかわりありません。
で、次のようなスクリプトを書いて実行させてみます。

CTRL_C = 3

p1 = { x = 10, y = 10, vx =  8, vy = -2 }
p2 = { x = 75, y = 55, vx = -6, vy =  4 }

interval = 100
width    = 160
height   = 120

function on_key(key, x, y)
  print(key, x, y)
  if key == CTRL_C then os.exit() end
end

function on_display()
  glut.vertex2i(p1.x, p1.y)
  glut.vertex2i(p2.x, p2.y)
end

function on_timer()
  p1.x = p1.x + p1.vx
  p1.y = p1.y + p1.vy
  p2.x = p2.x + p2.vx
  p2.y = p2.y + p2.vy
  if (p1.x < 0) or (width  < p1.x) then p1.vx = -p1.vx end
  if (p1.y < 0) or (height < p1.y) then p1.vy = -p1.vy end
  if (p2.x < 0) or (width  < p2.x) then p2.vx = -p2.vx end
  if (p2.y < 0) or (height < p2.y) then p2.vy = -p2.vy end
end

glut.main_loop
{
  width    = width,
  height   = height,
  interval = interval,
  keyboard = on_key,
  display  = on_display,
  timer    = on_timer
}


実行結果。内容としては元にしたC++のプログラムと一緒です。


Ctrl+Cで終了するようにしていますが、終了手続きとか一切無視してプログラムを終了しているのでこれで大丈夫なのか、まだ確認できてません。