Android マーカーからマーカーレスへ

このチュートリアルは、画像トラッカブルに追加されたノードを ArbiTrack に転送する方法を紹介します。


上級者向けチュートリアル

このチュートリアルは、マーカーの基礎ArbiTrack の基礎を終了済みであることを前提にしています。まだ終了していない場合は、先にこれらのチュートリアルを実施することを強く推奨します。

このチュートリアルは、マーカーの基礎チュートリアルで使用した Lego Marker と Kudan Cow アセットを使用します。これらのアセットをまだ入手していない場合は、マーカーの基礎にあるダウンロード リンクから入手してください。

画像トラッカブルの作成

最初に、Lego Marker 画像を使用して画像トラッカブルを作成します。次に、Kudan Cow 画像を使用して画像ノードを作成し、画像トラッカブルに追加します。

また、画像ノードが一意な名前になるようにコード行を追加する必要があります。この名前は、後でトラッカーで検索する際に使用します。

MainActivity.java
imageTrackable.setName("Lego Marker");
imageNode.setName("Cow");

アクティビティを ArbiTrack デリゲートとして登録

ArbiTrack がいつ開始されたのかを知る必要があります。幸いにも、そのためのデリゲートがあります。次のコードを setup メソッドの最後に追加します。

MainActivity.java
// ArbiTrack を初期化
ARArbiTrack arbiTrack = ARArbiTrack.getInstance();
arbiTrack.initialise();
 
// アクティビティを ArbiTrack デリゲートとして追加
arbiTrack.addListener(this);

このコードは、ArbiTrack が開始してワールド空間に配置されると、デリゲート メソッド arbiTrackStarted を呼び出します。

ターゲット ノードの設定

ここでは、ターゲット ノードをやや異なる方法で使用します。ArbiTrack の基礎チュートリアルでは、ノードを作成してターゲットとして使用しました。このチュートリアルでは、ArbiTrack が画像トラッカブルと同じ位置でトラッキングを開始するようにします。そのため、トラッカブルのワールド空間を ArbiTrack のターゲット ノードとして使用する必要があります。次のコードを setup メソッドに追加します。

MainActivity.java
// 画像トラッカブルのワールド空間をターゲット ノードとして使用
// これにより、ArbiTrack がトラッカブルの位置でトラッキングを開始
arbiTrack.setTargetNode(imageTrackable.getWorld());

ArbiTrack のデリゲート メソッドの実装

次に、ArbiTrack が開始時に何をすべきかを指示します。そのためには、アクティビティで arbiTrackStarted デリゲート メソッドを実装します。次のようなコードを使用します。

MainActivity.java
// ArbiTrack が開始したらデリゲート メソッドが呼び出される
@Override
public void arbiTrackStarted()
{
}

マーカーとマーカーレス トラッキングを切り替えるには、マーカーの検出時に表示される画像ノードを取得して、ArbiTrack のワールド空間に転送する必要があります。また、画像が正しい位置と向きであることを確実にする必要があります。そうでないと、トラッキング タイプを切り替えたときに画像がジャンプします。これは望ましくありません。

次のコードを arbiTrackStarted メソッドに追加します。

MainActivity.java
boolean firstRun = false;
     @Override
    public void arbiTrackStarted()
    {
        Log.d("K","=== ArbiTrack started ===");

       if(firstRun) {

       ARArbiTrack arArbiTrack = ARArbiTrack.getInstance();

       // トラッカーに登録されているトラッカブルを名前で検索
       ARImageTrackable legoTrackable = ARImageTracker.getInstance().findTrackable("Lego Marker");

       // ARImageNode の初期化、マーカー ノードの検索、および ARArbitrack へアタッチ
       cowNode = new ARImageNode();
       cowNode = (ARImageNode) trackable.getWorld().findChildByName("Cow");    
       arArbiTrack.getWorld().addChild(cowNode);

       cowNode.remove();

       // 画像ノードをトラッカブルのワールド空間の子として追加
       trackable.getWorld().addChild(imageNode);
  
       firstRun = false;

       }

     if(updateImage && trackable.getDetected()) {

        // ArbiTrack インスタンスを取得
        ARArbiTrack arArbiTrack = ARArbiTrack.getInstance();

        // マーカーレス空間をトラッカブル空間へ移動
        Matrix4f transform = arArbiTrack.getWorld().getFullTransform().invert().mult(trackable.getWorld().getFullTransform());

        // 完全な変換から向きを取得
        Quaternion orInArbiTrack = new Quaternion().fromRotationMatrix(transform.toRotationMatrix());

        // 完全な変換から位置を取得
        Vector3f posInArbiTrack = transform.mult(new Vector3f());

        // 画像ノードを ArbiTrack の子として追加
        arArbiTrack.getWorld().addChild(cowNode);

        // 画像ノードの位置をマーカー ノードのワールド空間に対応するように変更
        cowNode.setPosition(posInArbiTrack);

        // 画像ノードの向きをマーカー ノードのワールド空間に対応するように変更
        cowNode.setOrientation(orInArbiTrack);

     }
     updateImage = false;

  }

ブール型変数 ‘firstRun’ は、フレームワーク V1.5 においてメソッドが本来呼び出されるタイミングよりも早く呼び出されてしまう不具合に対応するために必要です。現在、この不具合を修正するため取り組んでいますが、修正が提供されるまでは、メソッドが初期化中に呼び出されないようにこの回避策を使用してください。

これで、ArbiTrack が開始すると、画像ノードの親が変更され、マーカーレスにトラッキングされます。ただし、ArbiTrack にいつ開始すべきかを知らせる必要があります。

ArbiTrack の開始

ArbiTrack にいつ開始すべきかを知らせるには、ArbiTrack の基礎チュートリアルのボタン入力への応答とは異なるメソッドを使用します。onSingleTapUp で ArbiTrack を開始する代わりに、次のメソッドを呼び出します。

MainActivity.java
public void toggleArbiTrack(View view)
    {
        Log.d("D","FLAG Button Pressed");

        // ArbiTrack インスタンスを取得
        ARArbiTrack arArbiTrack = ARArbiTrack.getInstance();

        updateImage = true;

        if(!arArbiTrack.getIsTracking()) {

        // マーカーが検出されていない場合はメソッドを終了して ArbiTrack への切り替えは行わない
             if(!trackable.getDetected())
             {
                 return;
             }

             imageNode.remove();

             arArbiTrack.start();
        }
        else {

             arArbiTrack.stop();

             imageNode.setPosition(new Vector3f());

             imageNode.setOrientation(new Quaternion());

             trackable.getWorld().addChild(imageNode);
         }
    }

toggleArbiTrack を開始するボタンを作成します。

activity_main.xml
<Button
        android:id="@+id/button"
        ...
        android:onClick="toggleArbiTrack"
        ...

このメソッドでは、ArbiTrack が開始していない場合にのみ開始するように、ボタンをタップしたときに ArbiTrack が実行中かどうかを知る必要があります。さらに、マーカーをトラッキングしていなければ、マーカー トラッキングからマーカーレス トラッキングに切り替えることはできないため、トラッカブルが検出されたかどうかも知る必要があります。

つまり、ボタンをタップしたときに、ArbiTrack がまだ実行されておらず、マーカーが検出されている場合は、マーカーレス トラッキングを開始します。これにより、arbiTrackStarted メソッドがトリガーされて、画像ノードを介して転送されます。

なぜ arbiTrackStartedメソッドを実装するのか、皆さんは不思議に思っているかもしれません。たしかに、メソッドを実装しなくても、ボタンをタップしたときに同じコードを実行すれば済みます。これは、ArbiTrack が最初に開始したときに、ジャイロスコープからデータをすぐに取得できないためです。このため、ワールド空間の変換が数フレーム分更新されません。この時点で画像ノードを転送しようとすると、向きが変わり、画面上でジャンプします。

メソッドは、arbiTrackStarted が開始され、かつ値がある場合にのみ呼び出されます。そのため、このメソッドが呼び出される場合、ワールド空間は更新済みであり、コンテンツを安全に転送できることを確認できます。

アプリをビルドして実行すると、マーカーを検出することができ、ボタンをタップするとマーカーレス トラッキングに切り替わります。再度タップすると、マーカー トラッキングに戻ります。トラッキング方法の変更時に視覚的フィードバックを得るため、何らかの形式のテキストを追加することもできます。あるいは、画像ノードの 1 つを変更すると面白いでしょう。