FLARToolKitを使ったAR系Flashの作り方

第2回3Dソフトで作ったオブジェクトを使ってみる

複雑なオブジェクトを表示する

前回は単純なCubeを表示するだけで少しもおもしろくありませんでしたので、今回は、より複雑なオブジェクトを表示して遊んでみましょう。

複雑なオブジェクトとは、例えばFlash以外の3Dソフトで作ったキャラクターとか、そういうものです。しかし、オブジェクトをマーカー上に表示するといっても全く難しくなく、Papervision3Dの機能だけであっさりできます。

そもそも、FLARToolKitがやっていることは前回もお話したようにカメラ座標の計算のみであり、そこから先はPapervision3Dをいかに組み合わせて使うか、という部分がほとんどで、つまり、ここから先はPapervision3D講座みたいになります。

今回使うデータは、以下になります。

Flashをお持ちの方はflaディレクトリに.flaファイルがあるのでそれをパブリッシュ、Flex Builderを使ってる人はこのzipをそのままFlex ProjectのArchiveファイルとしてimportしてください。

なお、今回も前回使用したものと同じマーカーを使用します。そのため、まだマーカーを作ってない人は、マーカーのPDFファイルを80mm x 80mm(等倍)で印刷しておいてください。

外部データを読み込む

Papervision3Dは基本機能の1つとして、Colladaというデータ形式の3Dオブジェクトを読み込む機能がついています。ColladaとはSony Computer EntertainmentがPlayStation 3の標準データフォーマットとして開発したもので、現在ではKhronos Groupという組織が管理している3Dソフトウェア業界標準の3Dデータフォーマットです。

例えば、フリーの3Dソフト、BlenderでもCollada形式に対応しているので、とりあえずお金をかけずに3Dモデルを作ってみたいという方はBlenderを試してみてください。ちょっとクセがあるけど慣れれば問題ないと思います。

ただし、Colldaのサイトに、Collada対応の3Dソフト一覧という項目がありますが、必ずしもPapervision3Dで使えるColladaを書き出せるとは限らないので、注意してください。

みっくみくにしてやんよ

モデリングの説明から始めると話がそれすぎてしまうため、今回はすでにCollada形式で書き出されたデータを読み込むところから説明します。

まずは、外部データとしてnote.xさんがフリーで配布しているねぎミクColladaファイルを使ってみます。いわゆる「はちゅね」です。

外部データを読み込むコードは以下のようになります。詳細はGihyo2_1a.asを確認してください。

リスト1 外部ファイルの読み込み(Gihyo2_1a.as)
// Gihyo2_1a.as
package {
  
  import flash.display.Sprite;
  
  import org.papervision3d.objects.parsers.DAE;

  [SWF(width=640, height=480, backgroundColor=0x0, frameRate=30)]

  public class Gihyo2_1a extends PV3DARApp {
    
    private var _miku:DAE;
    
    public function Gihyo2_1a() {
      this.init('Data/camera_para.dat', 'Data/flarlogo.pat');
    }
    
    protected override function onInit():void {
      super.onInit();
      
      this._miku = new DAE();
      this._miku.load('negimiku.dae');
      this._baseNode.addChild(this._miku);
    }
  }
}

とても簡単ですね。コードの流れは、前回のSimpleCube.asと同じように、まずはPV3DARAppクラスを継承したGihyo2_1aクラスを作ります。

そして、onInitの中でオブジェクトを作るのですが、今回は外部ファイルを読み込むだけなので、たったの3行です。DAEクラスがColladaファイルを読み込んで表示してくれるクラスです。このクラスのloadメソッドに読み込みたいColladaファイル(.daeという拡張子です)を指定すると、あとは勝手にモデルデータやテクスチャデータを読み込んでくれます。

読み込んだら前回と同じように、_baseNodeにaddChildします。そして、Flash使ってる方はGihyo2_1a.flaをパブリッシュ、Flex Builderを使っている方はGihyo2_1a.asをRunしてください。以下の図のように表示されるはずです。

図1 はちゅねを表示できたけど、サイズが小さいし、下を向いてしまっている
図1 はちゅねを表示できたけど、サイズが小さいし、下を向いてしまっている

そうでした、FLARToolKitの座標系が違うんでしたね。そこで、そのあたりを調整しましょう。それがGihyo2_1b.asです。以下の部分を確認してください。

リスト2 サイズと座標系の調整(Gihyo2_1b.as)
// Gihyo2_1b.as
this._miku = new DAE();
this._miku.load('negimiku.dae');
this._miku.scale = 5;        // 1
this._miku.rotationX = 90;   // 2
this._miku.rotationZ = 90;   // 3
this._miku.z = 9.68721 * 5;  // 4
this._baseNode.addChild(this._miku);

1では、サイズがもう少し大きくなるように調整しています。2と3では、正しい方向に回転させています。4では、Z方向に移動させています。

今回使ったデータは原点がはちゅねのおなかあたりにあるため、そのまま表示してしまうと、マーカーの上に正しく立ってくれません。そこで、ちょうど足がマーカー原点になるように移動させる必要があります。この数字はモデリングデータから調べたものです。実際に自分でモデルを作るときには、原点がマーカー中心だということを考えて作れば、わざわざActionScirptで移動させなくてもよくなります。

さて、Flashを使ってる方はGihyo2_1b.flaをパブリッシュ、Flex Builderを使っている方はGihyo2_1b.asをRunしてください。

図2 はちゅねがきちんと表示された
図2 はちゅねがきちんと表示された

どーん。はちゅね召喚に成功しました!

ね?簡単でしょう?

ARラジコン的な何か

外部オブジェクトの読み込みができるようになったところで、次はラジコンっぽいものを動かしてみましょう。

意外とPapervision3Dは歴史が長く、いろんな方がサンプルを作っています。そのため、そういうサンプルをFLARToolkitと組み合わせてみるだけでも、新しいおもしろさを表現できたりします。

例えば、以下の図の車は、Papervisio3D 1.7時代に作られたFocusというサンプルです。

図3 Focus
図3 Focus
URL:http://saqoosha.net/lab/Focus/

このサンプルは、当時Papervision3Dに付属していました。Ford社のFocusという車(のラリー仕様?)のCollada形式のモデリングデータを読み込んで、十字キーで操作([↑]キーがアクセル、[←][→]キーでハンドル。[↓]はバック)&マウスで視点が変えられるというものです。実際に車を動かしてみてください

今回の目的に必要十分な要素が使われてるので、これをFLAR対応させてみましょう。元のFocusサンプルはPapervision3DのSubversionリポジトリからダウンロードできます。Papervision3D 1.7用なのでPapervision3D 2.0でそのまま動かすことはできませんが、FLAR対応には特に問題ありません。

さきほどと同様に、PV3DARAppクラスを継承したGihyo2_2クラス(Gihyo2_2.as)を作って、main.as(Focusサンプルのメインとなるドキュメントクラス)から必要な部分をどんどんコピーして持ってきます。

リスト3 Gihyo2_2.as、15~31行目
// ____________________ 3D vars

private var rootNode  :DisplayObject3D;

// ____________________ Car vars

private var topSpeed  :Number = 0;
private var topSteer  :Number = 0;
private var speed     :Number = 0;
private var steer     :Number = 0;

// ____________________ Keyboard vars

private var keyRight   :Boolean = false;
private var keyLeft    :Boolean = false;
private var keyForward :Boolean = false;
private var keyReverse :Boolean = false;

15~31行目の変数宣言部分は、ほとんどそのままです。

リスト4 Gihyo2_2.as、37~52行目
protected override function onInit():void {
  super.onInit();
 
  var car:DAE = new DAE();   // 1
  car.load('Focus.dae');
  car.scale = 3;
  car.rotationX = 90;
  car.rotationZ = -90;
  this._baseNode.addChild(car);
  this.rootNode = car;

  stage.addEventListener( KeyboardEvent.KEY_DOWN, keyDownHandler );   // 2
  stage.addEventListener( KeyboardEvent.KEY_UP, keyUpHandler );       // 3

  this.addEventListener( Event.ENTER_FRAME, loop3D );   // 4
}

37~52行目は、onInit内での初期化部分です。1のFocus.daeの読み込みはPapervision3D 1.7と2.0でずいぶん違うため、ここは自分で書き直す必要があります(はちゅねのサンプルとほぼ同じです⁠⁠。2、3、4はmain.asのコンストラクタの58~61行目をコピーして持ってきます。そして、キーボード用のイベントハンドラとフレームごとに車を動かすloop3Dを登録します。

リスト5 Gihyo2_2.as、57~114行目
private function keyDownHandler( event :KeyboardEvent ):void
{
  // 省略
}

private function keyUpHandler( event :KeyboardEvent ):void
{
  // 省略
}

57~114行目のキーボード処理関連はそのままなので、コピペします。

リスト6 Gihyo2_2.as、119~158行目
private function driveCar():void
{
  // Speed
  if( keyForward )
  {
    topSpeed = 2;
  }
  else if( keyReverse )
  {
    topSpeed = -1;
  }
  else
  {
    topSpeed = 0;
  }

  speed -= ( speed - topSpeed ) / 10;

  // Steer
  if( keyRight )
  {
    if( topSteer < 45 )
    {
      topSteer += 5;
    }
  }
  else if( keyLeft )
  {
    if( topSteer > -45 )
    {
      topSteer -= 5;
    }
  }
  else
  {
    topSteer -= topSteer / 5;
  }

  steer -= ( steer - topSteer ) / 2;
}

119~158行目は、スピードとステアリングの処理部分です。元のままだと、なぜかものすごいスピードで操作できないため、数値を調整します。

リスト7 Gihyo2_2.as、194~222行目
private function loop3D( event :Event ):void
    {
// 1
//      camera.z = -300 + scene.container.mouseX * 5;
//      camera.y = Math.max( 0, this.mouseY ) * 5;
 
      // Get plane from rootNode
      var car :DisplayObject3D = this.rootNode.getChildByName("Focus", true);  // 2
      
      // Check if car has been loaded
      if( car )
      {
// 3
//        // Get plane from rootNode, we obviously don't need to check if it has been loaded.
//        var plane :DisplayObject3D = this.rootNode.getChildByName( "Plane" );
//  
//        // Check if car hits plane and change color
//        if( car.hitTestObject( plane ) )
//          plane.material.fillColor = 0xFFFFFF;
//        else
//          plane.material.fillColor = 0x333333;
 
        // Calculate current steer and speed
        driveCar();
 
        // Update car model
        updateCar( car );
      }
 
      // Render the scene
// 4
//      this.scene.renderCamera( camera );
    }

194~222行目では、フレームごとに実行されるloop3Dメソッドを記述し、ここで車を動かしています。

ここは結構いらない部分があるのでいろいろコメントアウトします。1はカメラ制御のコードですが、カメラはFLARが処理しちゃうのでいじる必要ありません。

3は床の色を車の位置に応じて変えるという処理ですが、これも必要ないのでコメントアウトします。4はシーンをレンダリングしているのですが、レンダリングはPV3DARAppクラスが行ってくれるのでコメントアウトします。

2はgetChildByNameの第2引数にtrueを渡すように変更します。これはPapervison 2.0での仕様変更に伴うものです。

それでは、動かしてみましょう(Flash使ってる人はGihyo2_2.flaをパブリッシュ、Flex Builderの人はGihyo2_2.asをRun⁠⁠。

図4 AR的ラジコンが完成!
図4 AR的ラジコンが完成!

キー操作は、先に述べたものと同じですムービーで見てみる⁠。

ほとんどコピペと、ちょっとした調整だけでFLAR対応できちゃいました。簡単ですね。

まとめ

最初に言及したようにFLARToolKitはカメラの計算をするだけなので、今回説明したように、ほとんどがPapervision3Dのお話になります。つまり、Papervision3Dが使える人であれば簡単にFLARなコンテンツが作れてしまうわけです。

次回は、僕が作った株式会社カタマリの年賀サイトで使ったテクニックをいくつか紹介してみようと思います。

おすすめ記事

記事・ニュース一覧