Irrlicht Engineを使うために右往左往する

しむどん 2026-01-10

しばらく前から仲間内でプレイしているゲームがある。活動する曜日を決めて、毎週欠かさず集まってプレイしており、はじめてからかれこれ3年以上になる。僕にとってはその中の方が居心地が良いし、そこに居る事で安心感を覚える。だからそこで使われている技術要素についても興味がある。今回はその中でも中心的に使われている要素である Irrlicht Engine について、少しだけ理解を深める事にした。

Irrlicht Engine は、3Dゲームエンジンというよりはレンダリングライブラリだ。C++で実装されており、軽量で、商用にも利用できるし、非常に寛容なオープンソースライセンスとして配布されている。ただ音声に関する実装はないし、衝突判定も簡易な実装となっているらしい。

以前はフリーのゲームエンジン用途として人気があったのかもしれないけれど、今は他にも優れたゲームエンジンがあるため、あまり使われていないかもしれない。ソースコードはSourceForgeにあり、もう更新もされていないのかと思ったのだが、確認してみるとそんな事は全くなく、 cutealien という人が精力的にコミットしていた。この人はドイツの3D関連のエンジニアらしい。本当に凄い。

IrrlichtのDownloadページには、 Irrlicht SDK なるものがZIP形式で配布されており、最新版は1.8.5だった。このファイルをダウンロードし、ローカルでビルドしようとした所、Irrlichtに同梱されているlibpngとzlibのバージョンの食い違いによってリンカーがエラーを出力する状態だった。この問題を解決するためにいろいろと試行錯誤したが、終ぞ問題を解決できなかった。しかし諦めるにはまだ早い。開発者が少ないようなプロダクトでは、最新版が一番安定しているという事はよくある。だから Irrlicht SDK 1.8.5 は諦め、最新のコードをSVNから取得する事にした。

svn checkout https://svn.code.sf.net/p/irrlicht/code/trunk irrlicht-code

SVNでコードを取得すると、 https://sourceforge.net/p/irrlicht/code/6731/ がHEADの状態のソースコードを取得できた。以降はこのコードを前提とする。

実はこの後の作業を進める上で、ビルドは通るのだが、実行中にNULLポインタに対する不正参照によってクラッシュするという、macOS上でのIrrlichtのバグに気が付いた。そのため次のパッチを当てる必要がある。

--- source/Irrlicht/CIrrDeviceOSX.mm    (リビジョン 6731)
+++ source/Irrlicht/CIrrDeviceOSX.mm    (作業コピー)
@@ -584,6 +584,11 @@
             // Create menu
             
             NSString* bundleName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+            // Fix: Ensure bundleName is never nil to prevent NSMenuItem crash
+            if (bundleName == nil || [bundleName length] == 0)
+            {
+                bundleName = @"Irrlicht";
+            }
             
             NSMenu* mainMenu = [[[NSMenu alloc] initWithTitle:@"MainMenu"] autorelease];
             NSMenu* menu = [[[NSMenu alloc] initWithTitle:bundleName] autorelease];

ビルドする環境はmacOS、XCodeなどは既にインストール済みであるものとして、このあたりの環境構築については省略する。

ソースツリー内に Irrlicht.xcodeproj というファイルがある。このファイルはXCode用のプロジェクトファイルだ。

まずはどのようなターゲットが用意されているかを確認する。

xcodebuild -list -project ./irrlicht-code/source/Irrlicht/Irrlicht.xcodeproj
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -list -project ./irrlicht-code/source/Irrlicht/Irrlicht.xcodeproj

User defaults from command line:
    IDEPackageSupportUseBuiltinSCM = YES

Information about project "Irrlicht":
    Targets:
        Irrlicht_OSX

    Build Configurations:
        Debug
        Release

    If no build configuration is specified and -scheme is not passed then "Release" is used.

    Schemes:
        Irrlicht_OSX

Irrlicht_OSX というターゲットが用意されている事が分かった。このターゲットを指定し xcodebuild でビルドする。

xcodebuild -project ./irrlicht-code/source/Irrlicht/Irrlicht.xcodeproj -configuration Release -target Irrlicht_OSX

ビルドに成功すると、 ./irrlicht-code/lib/OSX/libIrrlicht.a に静的ライブラリが出力される。

この静的ライブラリを使用するコードを実装する。

#include <stdio.h>
#include <irrlicht.h>
#include <exampleHelper.h>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

int main()
{
  IrrlichtDevice *device = createDevice(video::EDT_BURNINGSVIDEO,
					dimension2d<u32>(640, 480),
					16,
					false, false, false, 0);

  if (!device)
    {
      return 1;
    }
	

  device->setWindowCaption(L"Hello World! - Irrlicht Engine Demo");
	
  IVideoDriver* driver = device->getVideoDriver();
  ISceneManager* smgr = device->getSceneManager();
  IGUIEnvironment* guienv = device->getGUIEnvironment();

  guienv->addStaticText(L"Hello World!",
			rect<s32>(10,10,260,22),
			true);
	
  const io::path mediaPath = "./irrlicht-code/media/";
  IAnimatedMesh* mesh = smgr->getMesh(mediaPath + "sydney.md2");
  if (!mesh)
    {
      device->drop();
      return 1;
    }
  
  IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
  if (node)
    {
      node->setMaterialFlag(EMF_LIGHTING, false);
      node->setMD2Animation(scene::EMAT_STAND);
      node->setMaterialTexture( 0, driver->getTexture(mediaPath + "sydney.bmp") );
    }
  
  smgr->addCameraSceneNode(0,
			   vector3df(0,30,-40),
			   vector3df(0,5,0));
  
  while(device->run())
    {
      driver->beginScene(ECBF_COLOR | ECBF_DEPTH, SColor(255,100,101,140));
      smgr->drawAll();
      guienv->drawAll();
      driver->endScene();
    }
  
  device->drop();
  return 0;

}

このコードを clang++ でビルドする。

clang++ main.cpp -I./irrlicht-code/include -L./irrlicht-code/lib/OSX/ -lIrrlicht -framework Cocoa -framework OpenGL -framework IOKit

ビルドに成功すると a.out が出力される。

./a.out

このコマンドを実行すると、Irrlichtのウィンドウが開き読み込んだモデルが表示される。