Luaを組み込むメモ

を読みながら練習中。
サンプルゲームはいろいろと参考になる。


glutのアプリにluaを組み込んだ手順。
(glut, lua, tolua++が必要)

置き換え前

main.cpp

#ifdef _WIN32
#include <windows.h>
#else
#include <stdlib.h>
#endif
#include <GL/glut.h>
#include <stdio.h>
#include "glapp.h"
#include "main.h"
#include <iostream>

#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480

// opengl操作クラス
GLApp g_gl;

void resize(int w, int h)
{
  g_gl.setViewPort(0, 0, w, h);
  g_gl.setProjection(w, h);
}

void display()
{
  g_gl.draw();
  glutSwapBuffers();
}

void mouse(int button, int state, int x, int y)
{
  if(button==GLUT_RIGHT_BUTTON){
    switch (state) {
      case GLUT_UP:
        if(g_gl.onMouseRightUp(x, y)){
          glutPostRedisplay();
        }
        break;
      case GLUT_DOWN:
        if(g_gl.onMouseRightDown(x, y)){
          glutPostRedisplay();
        }
        break;
      default:
        break;
    }
  }
}

void motion(int x, int y)
{ 
  if(g_gl.onMouseMove(x, y)){
    glutPostRedisplay();
  }
}

void keyboard(unsigned char key, int x, int y)
{
  switch (key) {
    case 'q':
    case 'Q':
    case '\033':  /* '\033' は ESC の ASCII コード */
      exit(0);
    default:
      break;
  }
}

void initGlut(int *argc, char **argv)
{
  // init glut
  glutInitWindowSize(320, 240);
  glutInit(argc, argv);
  glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE);
  glutCreateWindow(argv[0]);
  // init glut callbacks
  glutDisplayFunc(display);
  glutReshapeFunc(resize);
  glutMouseFunc(mouse);
  glutMotionFunc(motion);
  glutKeyboardFunc(keyboard);
}

int main(int argc, char *argv[])
{
  initGlut(&argc, argv);
  g_gl.initialize();

  // ↓lua置き換え予定
  VertexArray &object=g_gl.appendDrawable();
  object.appendVertex(0.0, 1.0, 0.0);
  object.appendVertex(-1.0, -1.0, 0.0);
  object.appendVertex(1.0, -1.0, 0.0);
  object.appendColor(1.0, 0.0, 0.0);
  object.appendColor(0.0, 1.0, 0.0);
  object.appendColor(0.0, 0.0, 1.0);
  // ↑lua置き換え予定

  // main loop
  glutMainLoop();
  // ここには来ない
  return 1;
}

glapp.h

#ifndef GLAPP_H
#define GLAPP_H
  
#include <vector>
#include <GL/gl.h>

class HeadPitchCamera
{
  bool isMouseRightDown_;
  int mouseX_;
  int mouseY_;
  float head_;
  float pitch_;
  float distance_;

  public:
  HeadPitchCamera();
  bool onMouseRightDown(int x, int y);
  bool onMouseRightUp(int x, int y);
  bool onMouseMove(int x, int y);
  void draw();
};
  
class VertexArray
{
  std::vector<GLfloat> vertices_;
  std::vector<GLfloat> colors_;
 
  public:
  void draw();
  void appendVertex(float x, float y, float z);
  void appendColor(float r, float g, float b);
};        
        
class GLApp
{     
  HeadPitchCamera camera_;
  std::vector<VertexArray> drawables_;
        
  public:
  void initialize();
  void setViewPort(int left, int top, int width, int height);
  void setProjection(float w, float h);
  VertexArray& appendDrawable();
  void draw();
  bool onMouseRightDown(int x, int y);
  bool onMouseRightUp(int x, int y);
  bool onMouseMove(int x, int y);
};  
    
#endif // GLAPP_H

glapp.cpp

#include "glapp.h"
#include <GL/glu.h>
#include <algorithm>
#include <boost/bind.hpp>

  HeadPitchCamera::HeadPitchCamera()
  : isMouseRightDown_(false), mouseX_(-1), mouseY_(-1)
    , head_(0), pitch_(0), distance_(10)
{}

  bool
HeadPitchCamera::onMouseRightDown(int x, int y)
{
  isMouseRightDown_=true;
  mouseX_=x;
  mouseY_=y;
  return false;
}

  bool
HeadPitchCamera::onMouseRightUp(int x, int y)
{
  isMouseRightDown_=false;
  return false;
}

  bool
HeadPitchCamera::onMouseMove(int x, int y)
{
  if(!isMouseRightDown_){
    return false;
  }

  head_+=(x-mouseX_);
  pitch_+=(y-mouseY_);
  mouseX_=x;
  mouseY_=y;
  return true;
}

  void
HeadPitchCamera::draw()
{
  glTranslatef(0, 0, -distance_);
  glRotatef(head_, 0, 1, 0);
  glRotatef(pitch_, 1, 0, 0);
}

  void
VertexArray::draw()
{
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_COLOR_ARRAY);

  glColorPointer(3, GL_FLOAT, 0, &colors_[0]);
  glVertexPointer(3, GL_FLOAT, 0, &vertices_[0]);
  glDrawArrays(GL_TRIANGLES, 0, 3);

  glDisableClientState(GL_VERTEX_ARRAY);
  glDisableClientState(GL_COLOR_ARRAY);
}

  void
VertexArray::appendVertex(float x, float y, float z)
{
  vertices_.push_back(x);
  vertices_.push_back(y);
  vertices_.push_back(z);
}

  void
VertexArray::appendColor(float r, float g, float b)
{
  colors_.push_back(r);
  colors_.push_back(g);
  colors_.push_back(b);
}

  void
GLApp::initialize()
{
  glEnable(GL_TEXTURE_2D);
  glDisable(GL_CULL_FACE);
  glClearColor(0, 0, 0.5, 0);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}

  void
GLApp::setViewPort(int left, int top, int width, int height)
{
  glViewport(left, top, width, height);
  setProjection(width, height);
}

  void
GLApp::setProjection(float w, float h)
{
  float aspect=w/h;
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0, aspect, 1, 1000);
}

  VertexArray&
GLApp::appendDrawable()
{
  drawables_.push_back(VertexArray());
  return drawables_.back();
}

  void
GLApp::draw()
{
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  camera_.draw();
  std::for_each(drawables_.begin(), drawables_.end()
      , boost::bind(&VertexArray::draw, _1));

  glFlush();
}

bool
GLApp::onMouseRightDown(int x, int y)
{
  return camera_.onMouseRightDown(x, y);
}

bool
GLApp::onMouseRightUp(int x, int y)
{
  return camera_.onMouseRightUp(x, y);
}

bool
GLApp::onMouseMove(int x, int y)
{
  return camera_.onMouseMove(x, y);
}
TARGET=lua_sample
LDFLAGS=-lglut -lGLU -lGL

$(TARGET): main.o glapp.o
 gcc -o $@ $^ $(LDFLAGS)

.cpp.o:
 gcc -c -o $@ $< $(CFLAGS)

lua vmの組み込み

main.cppへの追加

// ヘッダ
#include <lua.hpp>

// VM
lua_State *g_L;

void finalize()
{
  luaL_dostring(g_L, "print('end lua')");
  lua_close(g_L);
}

int main(int argc, char **argv)
{
  // VM
  g_L=lua_open();
  atexit(finalize);

  // 標準ライブラリの読み込み
  luaL_openlibs(g_L);
  luaL_dostring(g_L, "print('start lua')");

  // 省略

  // main loop
  glutMainLoop();

  return 1;
}

Makefileの修正

LDFLAGS=-lglut -lGLU -lGL -llua

tolua++の仕込み

luafunc.pkgを作成。

// まだ空
$ tolua++ -h

usage: tolua++ [options] input_file

# help抜粋
Command line options are:
  -o  file : set output file; default is stdout.
  -H  file : create include file.
  -n  name : set package name; default is input file root name.

toluaでソース生成。

$ tolua++ -n luafunc -o luafunc_glue.cpp -H luafunc_glue.h luafunc.pkg

"-n"で指定したluafuncに対してtolua_luafunc_openという関数ができるので呼ぶようにする。
main.cpp

// 追加
#include "tolua++.h"
#include "luafunc_glue.h"

int main()
{
  // luaL_openlibs(L);の次くらいに追加
  tolua_luafunc_open(g_L);
}

Makefile修正と追加。

LDFLAGS=-lglut -lGLU -lGL -ltolua++ -llua
$(TARGET): main.o glapp.o luafunc_glue.o

luafunc_glue.cpp: luafunc.pkg
 tolua++ -n $@ -o $@_glue.cpp -H $@_glue.h $<

tolua++でc++の要素をluaにバインドする

GLApp g_gl
luaから操作できるようにする。
main.hにextern宣言を追加。

extern GLApp g_gl;

luafunc.pkgの中身を書く。
glapp.hから使う所だけ抜き出す。
(privateな部分やpublicでluaスクリプトから呼ばない部分は書かない)

$#include "glapp.h"
$#include "main.h"

module cfunc {

class VertexArray
{ 
  void appendVertex(float x, float y, float z);
  void appendColor(float r, float g, float b);
};

class GLApp
{
  VertexArray& appendDrawable();
};

extern GLApp g_gl;

}

luaに処理を移す

main.cpp

  // init scene
  VertexArray &object=g_gl.appendDrawable();
  object.appendVertex(0.0, 1.0, 0.0);
  object.appendVertex(-1.0, -1.0, 0.0);
  object.appendVertex(1.0, -1.0, 0.0);
  object.appendColor(1.0, 0.0, 0.0);
  object.appendColor(0.0, 1.0, 0.0);
  object.appendColor(0.0, 0.0, 1.0);

  if(argc>1){
    std::cout << "luaL_dofile: " << argv[1] << "...";
    // スクリプト実行
    int ret=luaL_dofile(g_L, argv[1]);
    // error処理
    if(ret){
      std::cout << "error: " << lua_tostring(g_L, -1);
    }
    std::cout << std::endl; 
  }

scene.lua

object=cfunc.g_gl:appendDrawable()
object:appendVertex(0.0, 1.0, 0.0);
object:appendVertex(-1.0, -1.0, 0.0);
object:appendVertex(1.0, -1.0, 0.0);
object:appendColor(1.0, 0.0, 0.0);
object:appendColor(0.0, 1.0, 0.0);
object:appendColor(0.0, 0.0, 1.0);

実行

$ ./lua_sample scene.lua

ここまででシーンへの形状配置をスクリプト化した。