やっぱりLuaでやる

とりあえず確実にできる方法でさくっとやってみることにした。
使ったバインダはtolua++。

本当にさくっとできた。tolua++はテンプレートによるコード生成では無くて、実際にcppとhをはきだすのでこちらの方が分かりやすい。コードジェネレート時のメッセージや出てくるコードはあまりわかりやすくないのだが、テンプレートの長大なエラーメッセージと格闘する必要が無いので楽だ。あと不完全な状態からエラーメッセージを出しながら作っていっても一度もクラッシュしなかった。やはりシンタックス以外はいけてるよlua


luaでシステムを作るとスクリプト側でクラス的な継承システムを自前で用意したり、グローバル変数への書き込み禁止スクリプトを用意したりというluaによる環境作りが発生したりするので便利になるまではまだ手間がかかったりする。その辺りに関してはこの本がだいぶ参考になる。


irrlichtでluaと言えば既に全部をラップした
http://irrlua.sourceforge.net/using.html
があったりするのだがコンパイルが通らなかった。対象のirrlichtのバージョンが古いようだ。自前ノードをluaでコントロールするのが目的なのでコンパイルが通らないのは使えぬ。直すくらいなら作り直したほうがましな感触だったので、とっかかりとしてirrlichtチュートリアルその1HelloWorldを動くようにした。
後で、見るかもしれないので全部乗せとく。

luaコード。

local MEDIA_PATH="C:/irrlicht-1.7.1/media/"

local device=irr.createDevice(irr.video.EDT_SOFTWARE,
irr.dimension2du(640, 480), 16, false, false, false, nil);
if not device then
  return 1
end
device:setWindowCaption(irr.L("Hello World! - Irrlicht Engine with tolua++ Demo"):c_str());

local driver = device:getVideoDriver();
local smgr = device:getSceneManager();
local guienv = device:getGUIEnvironment();

guienv:addStaticText(irr.L("Hello World! This is the Irrlicht Software renderer!"):c_str(),
irr.recti(10,10,260,22), true);

local mesh = smgr:getMesh(irr.P(MEDIA_PATH.."sydney.md2"));
if not mesh then
  print("fail to getMesh")
  device:drop();
  return 2;
end

local node = smgr:addAnimatedMeshSceneNode(mesh);
if not node then
  print("fail to addAnimatedMeshSceneNode")
  device:drop();
  return 3;
end

node:setMaterialFlag(irr.video.EMF_LIGHTING, false);
node:setMD2Animation(irr.scene.EMAT_STAND);
node:setMaterialTexture(0, driver:getTexture(irr.P(MEDIA_PATH.."sydney.bmp")));

smgr:addCameraSceneNode(nil, irr.vector3df(0,30,-40), irr.vector3df(0,5,0));

while device:run() do
  driver:beginScene(true, true, irr.video.SColor(255,100,101,140));
  smgr:drawAll();
  guienv:drawAll();
  driver:endScene();
end

device:drop();

return 0;

tolua向けの入力。ネストしたnamespaceの扱いがよくわからなかったのでusing namespaceで全展開しているのと、文字列周りのirr::io::path, wchar*あたりで小細工をしている以外は元のヘッダからコピペしただけ。

// irr.pkg
$#include <irrlicht.h>

// UTF-8 to WideChar
$#include <vector>
$#include <windows.h>
$#include <string>
$ std::wstring L(const std::string &text)
$ {
$   int size=MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, NULL, 0);
$   std::vector<wchar_t> buf(size);
$   size=MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, &buf[0], buf.size());
$   return std::wstring(buf.begin(), buf.begin()+size);
$ }

$ irr::io::path P(const char *text)
$ {
$   return irr::io::path(text);
$ }

namespace std {
  class wstring
  {
    const wchar_t* c_str()const;
  };
}

module irr {
  $using namespace irr;
  $using namespace core;
  $using namespace video;
  $using namespace gui;
  $using namespace scene;
  $using namespace io;
 
  std::wstring L(const std::string &text);
  path P(const char *text);

  typedef unsigned int u32;
  typedef signed int s32;
  typedef float f32;

  class dimension2du
  {
    dimension2du(u32, u32);
  };

  class recti
  {
    recti(s32, s32, s32, s32);
  };

  class vector3df
  {
    vector3df(f32, f32, f32);
  };

  class IrrlichtDevice : public IRefrenceCounted
  {
    virtual void setWindowCaption(const wchar_t* text)=0;
    virtual IVideoDriver* getVideoDriver() = 0;
    virtual IGUIEnvironment* getGUIEnvironment() = 0;
    virtual ISceneManager* getSceneManager() = 0;
    virtual bool run() = 0;
    virtual void yield() = 0;
  };

  class IReferenceCounted
  {
    bool drop() const;
  };

  module video {
    enum E_DRIVER_TYPE {
      EDT_SOFTWARE,
    };
    enum E_MATERIAL_FLAG {
      EMF_LIGHTING,
    };

    class SColor
    {
      SColor(u32, u32, u32, u32);
    };

    class IVideoDriver
    {
      virtual ITexture* getTexture(const path& filename) = 0;
      virtual bool beginScene(bool backBuffer=true, bool zBuffer=true,
          SColor color=SColor(255,0,0,0),
          const SExposedVideoData& videoData=SExposedVideoData(),
          recti* sourceRect=0) =0;
      virtual bool endScene() =0;
    };
  }

  module gui {
    class IGUIEnvironment
    {
      virtual IGUIStaticText* addStaticText(
          const wchar_t* text, const recti& rectangle,
          bool border=false, bool wordWrap=true, IGUIElement* parent=0, 
          s32 id=-1,
          bool fillBackground = false) = 0;
      virtual void drawAll() = 0;
    };
  }

  module scene {
    enum EMD2_ANIMATION_TYPE
    {
      EMAT_STAND,
    };

    class ISceneManager
    {
      virtual IAnimatedMesh* getMesh(const path& filename) = 0;
      virtual IAnimatedMeshSceneNode* addAnimatedMeshSceneNode(IAnimatedMesh* mesh,
          ISceneNode* parent=0, s32 id=-1,
          const vector3df& position = vector3df(0,0,0),
          const vector3df& rotation = vector3df(0,0,0),
          const vector3df& scale = vector3df(1.0f, 1.0f, 1.0f),
          bool alsoAddIfMeshPointerZero=false) = 0;

      virtual ICameraSceneNode* addCameraSceneNode(ISceneNode* parent = 0,
          const vector3df& position = vector3df(0,0,0),
          const vector3df& lookat = vector3df(0,0,100),
          s32 id=-1, bool makeActive=true) = 0;

      virtual void drawAll() = 0;
    };

    class IAnimatedMeshSceneNode : public ISceneNode
    {
      virtual bool setMD2Animation(EMD2_ANIMATION_TYPE anim) = 0;
    };

    class ISceneNode
    {
      void setMaterialFlag(E_MATERIAL_FLAG flag, bool newvalue);
      void setMaterialTexture(u32 textureLayer, ITexture* texture);
    };
  }

  IrrlichtDevice* createDevice(
      E_DRIVER_TYPE deviceType,
      const dimension2du& windowSize,
      u32 bits,
      bool fullscreen,
      bool stencilbuffer,
      bool vsync,
      IEventReceiver* receiver);
}
#include <iostream>
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// c++で使うときはlua.hだとリンク時にエラーに
// なるのでlua.hppを使う
#include <lua.hpp>

#include <lauxlib.h>
#include <lualib.h>

#include <tolua++.h>
#include "irr_glue.h"

class LuaState
{
  lua_State *L_;
public:
  LuaState()
    : L_(0)
    {
      L_=luaL_newstate();
      luaL_openlibs(L_);
    }

  ~LuaState()
  {
    lua_close(L_);
  }

  lua_State *get(){ return L_; }

  void error()
  {
    fprintf(stderr, "%s\n", lua_tostring(L_, -1));
    exit(EXIT_FAILURE);
  }

  void error(const char *fmt, ...)
  {
    va_list argp;
    va_start(argp, fmt);
    vfprintf(stderr, fmt, argp);
    va_end(argp);
    exit(EXIT_FAILURE);
  }

  void dofile(const char *path)
  {
    // Luaスクリプト(チャンク)
    // をコンパイルしてスタックのトップに関数として積む
    if(luaL_loadfile(L_, path)){
      error();
    }
    // スタックトップの関数を保護モード(エラーが発生したら
    // エラーコードを返す)で実行する。
    if(lua_pcall(L_, 0, 0, 0)){
      error();
    }
  }
};

int main(int argc, char **argv)
{
  if(argc<2){
    fprintf(stderr, "usage: %s {lua script}\n", argv[0]);
    return 1;
  }

  LuaState lua;

  tolua_irr_open(lua.get());

  // スクリプト実行
  lua.dofile(argv[1]);

  return 0;
}

build

> tolua++ -n irr -o irr_glue.cpp -H irr_glue.h irr.pkg