Wikitude Instant Tracking


Instant Tracking

下記のセッションは、Wikitude Java Script SDK の機能の詳細を、簡単な実行例とそのうえで開発した2つの成功した実行例を紹介します。最初は、Wikitude Java Script SDK が提供する容易さを示しながら、最小限必要な準備をします。その後に続く事例は、3D モデルARと、補助的な自在な回転機能を追加します。それは、実用的で本格的に使えうことのできる事例です。

個々のセクションが重複しないようにするために、事例別に作業することをお勧めします。

SMART - Seamless AR Tracking

SMARTはシームレスなAPIです。それは、ARKit、ARCore、Wikitude SLAM の3つの機能を、どんなデバイスにも対応して、1つのAR SDKに連結統合する機能です。それは、市場に出回っている、iOSデバイスの92.6%、Android デバイスのおよそ35%、を広範囲にカバーして、ARに最も優れた実行可能性を確実に提供しています。

WikitudeでARCoreを使えるようにするためには、Enable ARCore Enable ARCore のセクションの中のARCoreのドキュメンテーションの中にある命令に従ってください。これは、また、参考アプリケーションの中でも見ることができます。

 

SMARTはデフォルトで使用できますが、smart Enabled optionでAR.Instant Tracker AR.InstantTracker が起動した時にパラメーターがセットがされた場合は使用できません。これは実行時間中変えることができません。

 

new AR.InstantTracker({
    smartEnabled: false
});

SMART(ARCore Tracking)でトラッキングできるデバイスがどうかをチェックするためには、AR.hardware.smart.isPlatformAssistedTrackingSupportedを必ず参照してください。ARCoreは、要求されたARCoreに親和性のあるアプリケーションを自動的にダウンロードと、インストールする機能を持っています。Wikitude Java Script SDKで、この手順を起動するためには、2つのステップがあります。

AR.hardware.smart.isPlatformAssistedTrackingSupportedを呼び出し、該当するなら、SMART flag をTrue にセットしてAR.InstantTrackerを作成してください。

最初の呼び出しでは、現在のデバイスがARCoreをサポートしているかどうかを決定するために連続したクエリーがスタートします。AR.hardware.smart.onPlatformAssistedTrackingAvailabilityChanged コールバック機能を通して、現在のステータスを繰り返し報告してきます。このコールバックは下記の一覧表のうちの一つをインプットパラメーターとして提供します。

Value Action
 AR.hardware.smart.SmartAvailability.INDETERMINATE_QUERY_FAILED

 クエリーが何らかの理由でフェイルした時

再トライするか、ARCore無しで稼働するトラッカーを作成する。コールバックは再度呼び出せない。

AR.hardware.smart.SmartAvailability.CHECKING_QUERY_ONGOING

クエリは現在進行中。 何もする必要はない。 コールバックが再度呼び出される。

AR.hardware.smart.SmartAvailability.UNSUPPORTED

デバイスはARCoreをサポートしていない。 ARCoreなしで実行するトラッカーを作成する。 コールバックは再び呼び出さない。

AR.hardware.smart.SmartAvailability.SUPPORTED_UPDATE_REQUIRED

デバイスはARCoreをサポートしているが、コンパニオンアプリをインストールまたは更新する必要がある。 インストールプロセスを開始するトラッカーを作成する。 コールバックが再度呼び出される。

AR.hardware.smart.SmartAvailability.SUPPORTED

デバイスはARCoreをサポートしており、コンパニオンの現在のバージョンはすでにインストールされている。 ARCoreで実行するトラッカーを作成する。 コールバックは再び呼び出せない。

JavaScriptコードに移された場合、表には次のスニペットが表示されます。

AR.hardware.smart.onPlatformAssistedTrackingAvailabilityChanged = function(availability) {
    switch(availability) {
        case AR.hardware.smart.SmartAvailability.INDETERMINATE_QUERY_FAILED:
            /* query failed for some reason; try again or accept the fact. */
            World.showUserInstructions("Could not determine if platform assisted tracking is supported.<br>Running without platform assisted tracking (ARKit or ARCore).");
            World.createOverlays();
            break;
        case AR.hardware.smart.SmartAvailability.CHECKING_QUERY_ONGOING:
            /* query currently ongoing; be patient and do nothing or inform the user about the ongoing process */
            break;
        case AR.hardware.smart.SmartAvailability.UNSUPPORTED:
            /* not supported, create the scene now without platform assisted tracking enabled */
            World.showUserInstructions("Running without platform assisted tracking (ARKit or ARCore).");
            World.createOverlays();
            break;
        case AR.hardware.smart.SmartAvailability.SUPPORTED_UPDATE_REQUIRED:
        case AR.hardware.smart.SmartAvailability.SUPPORTED:
            /* supported, create the scene now with platform assisted tracking enabled * * SUPPORTED_UPDATE_REQUIRED may be followed by SUPPORTED, make sure not to * create the scene twice */
            World.platformAssisstedTrackingSupported = true;
            if (!World.createOverlaysCalled) {
                World.showUserInstructions("Running with platform assisted tracking(ARKit or ARCore). <br> Move your phone around until the crosshair turns green, which is when you can start tracking.");
                World.createOverlays();
                World.createOverlaysCalled = true;
            }
            break;
    }
};

ARCoreアプリケーションのインストールが必要な場合は、トラッカーを2回作成しないように注意してください。 AR.hardware.smart.SmartAvailability.SUPPORTED_UPDATE_REQUIRED定数の後にAR.hardware.smart.SmartAvailability.SUPPORTED定数が続く場合がありますが、一度だけトラッカーを作成したい場合があります。 提示されたスニペットは、その参照をチェックすることによってトラッカーを二重に作成することを回避します。

SMARTは、制御を犠牲にして改善されたトラッキング機能を提供します。 そのため、SMARTを有効にしてプラットフォームアシスタントトラッキング機能を使用すると一部のWikitude SDK機能を使用できません。

機能 SMART on  SMART off
トラッキングの改善  ☑  x
平面の方向
カメラの制御
Instant Targetの保存と読み込み

平面検出

Introduction(導入)

インスタントトラッキングは、以前にWikitude SDKで導入されたものとは異なり、あらかじめ定義されたターゲットを認識し、トラッキングを開始することをせず、ただちに任意の環境でトラッキングを開始するアルゴリズムです。 これにより、非常に具体的な使用方法を実装することができます。 そのような使用方法の1つとして、家具製品の視覚化アプリケーションを実装しています。

アルゴリズムは2つの異なる状態で動作します。 最初のものは初期化状態です。 この状態では、ユーザは、単にデバイスを指示し、それによってインジケータを整列させることによって、トラッキングの起点を定義します。 一旦、(ユーザが積極的に確認する必要がある)ユーザが満足できるものであると判明すると、トラッキング状態が開始されます。 この状態では、環境は継続的にトラッキングされているため、実風景内に拡張現実のイメージを配置することができます。

 

プラットフォームアシスタントトラッキングが有効な場合、前提条件として平面が決定されている必要があるため、初期状態からトラッキング状態への切り替えが常に可能であるとは限りません。平面が決定されるまでには数秒かかることがあります。基礎になるアルゴリズムが準備されていない状態で切り替えようとすると、エラーコールバックによってエラーが発生します。

初期状態

トラッキング状態


インスタントトラッキングのアルゴリズムは、初期化状態で別の入力値を提供することを要求します。 具体的には、トラッキングしているデバイスの高さは、シーンの範囲内で正確に増加したスケールを調整するために必要となります。 この目的のために、3つの例は、メートル単位で高さを設定できる範囲入力要素を備えています。

プラットフォームアシスタントトラッキングが有効な場合、平面の入力パラメータより上のデバイスの高さと、対応範囲の入力要素は、初期状態のスケールにのみ影響します。トラッキング状態のスケールは、プラットフォーム追跡アルゴリズムによって自動的に決定されます。スケール値が正しくない場合、ステータスの切替時にスケールの不一致を引き起こすので、正しい値を設定することを推奨します。

初期化中にインスタントトラッキングの平面を整列するための別のパラメータを設定できます。この平面を初期化インジケータにして表示させ、床の代わりに壁をターゲットとしてトラッキングするために回転させることができます。詳細については、APIリファレンスAPI referenceを参照してください。

プラットフォームアシスタントトラッキングが有効な場合、トラッキングする平面の方向を変更することができません。ただし、エラーコールバックでエラーを発生させる以外の影響はありません。

Basic Instant Tracking

基本的なインスタントトラッキングの例として、インスタントトラッキングのアルゴリズムの最小限の実装で示します。 これは2つの必須クラスAR.InstantTrackerAR.InstantTrackableを使用しています。 既に画像のトラッキングについて精通しているならば、AR.ImageTrackerAR.ImageTrackableは画像のトラッキングと同じパターンを使用するため、使い慣れているはずです。

AR.InstantTrackerは、パラメータなしでインスタンス化できます。

this.tracker = new AR.InstantTracker();

しかしながら、ステイタスの間での移動を発生させるために(onChangedState)Call Back機能を必要とした場合は、初期値で設定した高さ(deviceHeightががよびだされます。

this.tracker = new AR.InstantTracker({
    onChangedState:  function onChangedStateFn(state) {
    },
    deviceHeight: 1.0
});

AR.InstantTrackableは、初期に生成されたトラッキングで簡易化することはできますが、実用的な使用の場合は、初期化状態とトラッキング状態の両方で認識されるdrawablesを使用することを推奨します。そうすることで、2つのAR.ImageDrawableでの簡易化とそれに対応する2つのAR.ImageResourceの簡易化が生成され提供されます。

var crossHairsRedImage = new AR.ImageResource("assets/crosshairs_red.png");
this.crossHairsRedDrawable = new AR.ImageDrawable(crossHairsRedImage, 1.0);

var crossHairsBlueImage = new AR.ImageResource("assets/crosshairs_blue.png");
this.crossHairsBlueDrawable = new AR.ImageDrawable(crossHairsBlueImage, 1.0);

this.instantTrackable = new AR.InstantTrackable(this.tracker, {
    drawables: {
        cam: World.crossHairsBlueDrawable,
        initialization: World.crossHairsRedDrawable
    },
});

SMARTによってプラットフォームアシスタントトラッキングがサポートされている場合、ARKitまたはARCoreが平面を検出した後にトラッキングを開始することができます。このため、AR.InstantTracker.canStartTrackingで確認して、初期状態からトラッキングに切り換えが可能なことを示すために、3つ目のAR.ImageDrawableを追加することを推奨します。

var crossHairsGreenImage = new AR.ImageResource("assets/crosshairs_green.png");
this.crossHairsGreenDrawable = new AR.ImageDrawable(crossHairsGreenImage, 1.0);

setInterval(
    function() {
        if (World.tracker.canStartTracking) {
            World.instantTrackable.drawables.initialization = [World.crossHairsGreenDrawable];
        } else {
            World.instantTrackable.drawables.initialization = [World.crossHairsRedDrawable];
        }
    },
    1000
);

追加で必要となる変更は、ある状態から別の状態に変わる際にどのように移行するかについてです。 ここでは、ボタンクリックで便利に呼び出changeTrackerState機能を実装します。 AR.InstantTrackerStateは、各状態を識別するために使用される2つの値を定義します。

changeTrackerState: function changeTrackerStateFn() {
    if (this.tracker.state === AR.InstantTrackerState.INITIALIZING) {
        this.tracker.state = AR.InstantTrackerState.TRACKING;
    } else {
        this.tracker.state = AR.InstantTrackerState.INITIALIZING;
    }
}
<input id="tracking-start-stop-button" type="image" src="assets/buttons/start.png" onclick="World.changeTrackerState()"/>

クリックだけで呼び出しをするためのボタンのコーディング

最後に、changeTrackingHeight機能を用いて、AR.InstantTrackerdeviceHeightプロパティを設定し、範囲入力エレメントに接続します。 この変更は厳密に言えば必須ではありませんが、この方法を用いて、正確にデバイスの高さを指定することを強く推奨します。

changeTrackingHeight: function changeTrackingHeightFn(height) {
    this.tracker.deviceHeight = parseFloat(height);
}
<input id="tracking-height-slider" type="range" min="0.1" value="1.0" max="2.0" step="0.1" onchange="World.changeTrackingHeight(value)">

正確な高さを指定するバーのコーディング

このセクションで概説した例では、初期状態をインジケータにして赤い十字線画像の表示を行い、その増補としてトラッキング状態のときに対応する青の十字画像を表示します。 読者にすばやくトラッキングの基本概念を理解してもらうため、この例は非常に詳細です。さらに、サンプルアプリケーションの簡単さを強調しておきます。 JavaScriptコードの行数が20行に満たない場合、このサンプルは完璧に機能します。 以下のセクションで紹介する変更は、同じように簡単です。

 Basic Instant Tracking の初期状態

Basic Instant Trackingのトラッキング状態


平面上の3Dモデル

このセクションでは、以前に実装されたサンプルアプリケーションを修正して、ユーザーとのやりとりや、より洗練された機能拡張の使用方法を示します。

まず、画面上をクリックするだけで、シーン内に配置する3Dモデルを追加してアプリケーションを拡張します。 内部的には、このタッチで定義された線は、インスタントトラッキングの平面で交差し、モデルのtransformプロパティに適用される交差位置を生成します。 この発生時に、AR.InstantTrackableonTrackingPlaneClickコールバックが呼び出され、交差位置座標が別々のパラメータとして提供されます。

this.instantTrackable = new AR.InstantTrackable(this.tracker, {
    drawables: {
        cam: crossHairsBlueDrawable,
        initialization: crossHairsRedDrawable
    },
    onTrackingPlaneClick: function onTrackingPlaneClickFn(xpos, ypos) {
        World.addModel(xpos, ypos);
    }
});

addModel機能は、AR.Modelを簡易化し、初期スケール、変換、回転(rotate)のプロパティを設定します。 translateプロパティは、onTrackingPlaneClickコールバックに渡される交差座標に直接設定されます。 視覚的多様性を追加するために、Z軸回りの回転がランダム化されます。

addModel: function addModelFn(xpos, ypos) {
    if (World.isTracking()) {
        var model = new AR.Model("assets/models/couch.wt3", {
            scale: {
                x: 0.045,
                y: 0.045,
                z: 0.045
            },
            translate: {
                x: xpos,
                y: ypos
            },
            rotate: {
                z: Math.random() * 360.0
            },
        })

        allCurrentModels.push(model);
        this.instantTrackable.drawables.addCamDrawable(model);
    }
}

isTracking機能は、トラッカーがトラッキング状態になっているかどうかをチェックして、ユーザーの相互干渉を制限します。

isTracking: function isTrackingFn() {
    return (this.tracker.state === AR.InstantTrackerState.TRACKING);
}

この例は、以前に生成されたモデルを再構築する機能をすでに持っています。そのモデルが直接にInstant Trackingに関係がないものは排除しています。

風景は空です。

風景にいくつかのクリックでインプットがなされています。


自由自在に動かす機能(Interactivity)

最後に、サンプルアプリケーションをさらに拡張します。前の風景にいくつかの異なるAR.Modelを配置し、以前に配置されたAR画像にさらに動きの選択肢を加えます。

まずこのページの最初のペアの画像に表示しているように、使用可能なモデルごとに1つずつボタンをいくつか追加します。 そのHTML定義は簡単なためここでは省略しますが、将来参照するため、その存在を認識しておく必要があります。 さらに重要なのは、最初に、それぞれのボタンでtouchstart eventがトリガーされるようにevent listenersが設定されている点です。これを使ってrequestedModelプロパティを設定します。 このプロパティは、どのモデルがInstant化するモデルかを示します。

setupEventListeners: function setupEventListenersFn() {
    document.getElementById("tracking-model-button-clock").addEventListener('touchstart', function(ev){
        World.requestedModel = 0;
    }, false);
    document.getElementById("tracking-model-button-couch").addEventListener('touchstart', function(ev){
        World.requestedModel = 1;
    }, false);
    document.getElementById("tracking-model-button-chair").addEventListener('touchstart', function(ev){
        World.requestedModel = 2;
    }, false);
    document.getElementById("tracking-model-button-table").addEventListener('touchstart', function(ev){
        World.requestedModel = 3;
    }, false);
    document.getElementById("tracking-model-button-trainer").addEventListener('touchstart', function(ev){
        World.requestedModel = 4;
    }, false);
},

モデルをInstant化するために、AR.InstantTrackableにある、onTrackingPlaneDragBeganonTrackingPlaneDragChangedおよびonTrackingPlaneDragEndedの各CallBackを実装します。 開始および終了CallBackは、片方向のドラッグが開始または解除されたときに呼び出されます。 動作が継続されている限り、更新コールバックが定期的に呼び出されます。 onTrackingPlaneClickのCallBackと同様に、それらはtouch ray線instant tracking 平面の交差位置を取得します。

this.instantTrackable = new AR.InstantTrackable(this.tracker, {
    drawables: {
        cam: crossHairsBlueDrawable,
        initialization: crossHairsRedDrawable
    },
    onTrackingPlaneDragBegan: function onTrackingPlaneDragBeganFn(xPos, yPos) {
        World.updatePlaneDrag(xPos, yPos);
    },
    onTrackingPlaneDragChanged: function onTrackingPlaneDragChangedFn(xPos, yPos) {
        World.updatePlaneDrag(xPos, yPos);
    },
    onTrackingPlaneDragEnded: function onTrackingPlaneDragEndedFn(xPos, yPos) {
        World.updatePlaneDrag(xPos, yPos);
        World.initialDrag = false;
    }
});

交差位置をupdatePlaneDrag機能に転送するだけで、requestModelプロパティをチェックしています。これは、以前、ボタンのうちの一つのtouchmoveハンドラ機能によっておそらく設定されています。もし、それがこのケースに当てはまったら、addModel機能を使って指定された交差位置にそのモデルのIDが生成されます。

 その後のonTrackingPlaneDragChanged呼び出しによって位置が更新されます。そして、以前作成したボタンをドラッグすることによってAR.Model事例が作成されます。

addModel: function addModelFn(pathIndex, xpos, ypos) {
    if (World.isTracking()) {
        var modelIndex = rotationValues.length;
        World.addModelValues();

        var model = new AR.Model(World.modelPaths[pathIndex], {
            scale: {
                x: defaultScaleValue,
                y: defaultScaleValue,
                z: defaultScaleValue
            },
            translate: {
                x: xpos,
                y: ypos
            },
            onDragChanged: function(relativeX, relativeY, intersectionX, intersectionY) {
                this.translate = {x:intersectionX, y:intersectionY};
            },
            onRotationChanged: function(angleInDegrees) {
                this.rotate.z = rotationValues[modelIndex] - angleInDegrees;
            },
            onRotationEnded: function(angleInDegrees) {
               rotationValues[modelIndex] = this.rotate.z
            },
            onScaleChanged: function(scale) {
                var scaleValue = scaleValues[modelIndex] * scale;
                this.scale = {x: scaleValue, y: scaleValue, z: scaleValue};
            },
            onScaleEnded: function(scale) {
                scaleValues[modelIndex] = this.scale.x;
            }
        })

        allCurrentModels.push(model);
        lastAddedModel = model;
        this.instantTrackable.drawables.addCamDrawable(model);
    }
}

addModel関数は前の例で示したものと非常によく似ていますが、いくつかの追加が行われています。特に、ジェスチャーコールバックがAR.Modelに追加されました。これらのコールバックは、onClickコールバックの呼び出しパターンに従います。 AR.Drawableから派生したクラスがタッチでヒットすると、対応するジェスチャーコールバックが実装されている場合にはそのジェスチャーコールバックが呼び出されます。トラッキングできない場合は、次にAR.Contextオブジェクトとみなされます。 AR.InstantTrackableに属するAR.Drawableの場合、ドラッグジェスチャコールバックは相対座標に加えてTrackingの平面上の交点座標を受け取ります。これにより、受信した交差座標にAR.Drawableのtranslateプロパティを設定するだけで、Instant Tracking Scene のオブジェクトを変換することが可能になります。個々のコンポーネントを設定するのではなく、JavaScriptからネイティブOS環境への必要なコールバックを最小限に抑えるために、変換プロパティ(回転、スケール、変換)全体を設定することをお勧めします。回転と大きさのジェスチャコールバックは、興味を持った読者がここで参照するジェスチャー専用の事例でデモされているとおりに動作します。

考慮すべきもう1つの複雑な点は、2つの指での動作がアクティブである間にドラッグジェスチャを引きだすことを無効にして、相互に直感的に自在に作動することを防止することです。 これは、AR.context.on2FingerGestureStartedコールバックを実装し、その中にフラグを設定することで実現できます。

AR.context.on2FingerGestureStarted = function() {
    oneFingerGestureAllowed = false;
}

onDragChangedコールバックは、このフラグを考慮し、許可されている場合にのみtranslateプロパティを更新するようになっています。

onDragChanged: function(relativeX, relativeY, intersectionX, intersectionY) {
    if (oneFingerGestureAllowed) {
        this.translate = {x:intersectionX, y:intersectionY};
    }
}

このフラグは、次回のonDragBeganコールバック呼び出しでリセットされ、再び有効になります。

onDragBegan: function(x, y) {
    oneFingerGestureAllowed = true;
}

変更された要点が、最終的に最初の家具の可視化アプリケーションの使用例を可能にしました。事例アプリには、まだ、カバーされていない側面の可視化がありますが、それらは直接的には、Instant Tracking 機能には関係ありません。そして、例示したアプリケーションのソースコードから容易に理解することができます。

Wikitude社のオフィスの床にあるミニチュアの

リビングルームの風景(Scene)


風景とのやり取り(Scene Interaction)

Instant Trackingを使用することで、3Dポイントをポイントクラウドからクエリすることができます。このセクションは、対応するサンプルアプリケーションに基づいてこの機能を紹介します。

この機能を使用するために、シーン内に2Dモデルを配置する位置が必要となります。この位置を取得するためにAR.context.onScreenClickイベントを使用して、入力としてマウスがクリックされたときの座標を受け取る関数をアタッチします。受け取った座標は、変更されていないAR.InstantTrackableconvertScreenCoordinateToPointCloudCoordinate関数に供給することができます。この関数では、入力座標に加えて、クエリ完了時に呼び出される他の2つの関数も必要です。これらの関数は、クエリが成功した場合と失敗した場合の両方で呼び出されます。成功は入力座標に対して3D位置が見つかることを意味し、失敗は3D位置が見つからないことを意味します。成功した場合は、3D位置は3つの異なるパラメータで提供されます。これらのパラメータは、以下のAR.Circleのような任意のAR.Drawableの平行移動を直接設定するために使用できます。シーンとのやり取りは、トラッキング状態でのみ使用できます。

AR.context.onScreenClick = function(touchLocation) {
    if ( World.tracker.state === AR.InstantTrackerState.TRACKING ) {
        World.instantTrackable.convertScreenCoordinateToPointCloudCoordinate(touchLocation.x, touchLocation.y, function(x, y, z) {
            var circle = new AR.Circle(0.1, {
                translate: {
                    x: x,
                    y: y,
                    z: z
                },
                style: {
                    fillColor: '#FF8C0A'
                }
            });
            World.instantTrackable.drawables.addCamDrawable(circle);
        }, function () {
            alert('nothing hit. try selecting another scene location');
        });
    } else {
        alert('Scene information are only available during tracking. Please click the start button to start tracking');
    }
}

サンプルを実行すると、スクリーンのタッチ操作でトラッキング状態の環境で円型のARの印が配置されます。

オレンジの円形がWikitude社のオフィスの床に生成、現れます。これはScene Picking 機能を使っています。

保持されるInstant Targets

Instant Targets の保存および読み込みの機能により、ARの実行は複数のユーザーがデバイスやオペレーティングシステムに関係なく、横断的に、持続的にアクセスできるようになります。 さらにInstant Targets を拡大することができます。このセクションでは、サンプルアプリケーションに基づいてこの機能を紹介します。 この機能は、プラットフォームによるトラッキングが有効な場合は使用できません。

Instant Targetsの保存

Instant Targets を保存するには、アクティブ状態のInstantTrackerがトラッキング状態でなければならず、指定されたパスのディレクトリが存在する必要があります。

有効なパスを取得するにはAR.platform.sendJSONObjectarchitectView.callJavaScriptのプラットフォームを使用します。 この例では、Instant TargetsとともにAR機能を保存および読み込む方法を示しています。 これは、すべてのアプリ用に独自に最適に作成されたSDKの機能の一部ではありません。

saveCurrentInstantTarget: function () {
    var augmentations = [];

    allCurrentModels.forEach(function (model) {
        augmentations.push({
            uri: model.uri,
            translate: model.translate,
            rotate: model.rotate,
            scale: model.scale
        });
    });

    if (this.tracker.state === AR.InstantTrackerState.TRACKING) {
        AR.platform.sendJSONObject({
            action: "save_current_instant_target",
            augmentations: JSON.stringify(augmentations)
        });
    } else {
        alert("Save instant target is only available while tracking.")
    }
}

saveCurrentInstantTargetToUrlは、URLとともにプラットフォームコードからよびだされます。そしてそれは、saveCurrentInstantTargetをセーブするためのパスとして使用されます。

saveCurrentInstantTargetToUrl: function (url) {
    this.tracker.saveCurrentInstantTarget(url, function () {
        alert("Saving was successful");
    }, function (error) {
        alert("Saving failed: " + error);
    })
}

Instant Targetの読込み

Instant Targetsを読み込むには、アクティブトラッカーと以前に保存したInstant Targets がなければなりません。

AR.platform.sendJSONObjectプラットフォームから有効なパスを取得するために、architectView.callJavaScript使用されます。

loadExistingInstantTarget: function () {
    AR.platform.sendJSONObject({
        action: "load_existing_instant_target"
    });
},

loadExistingInstantTargetFromUrlは、TargetCollectionResourceを作成するために使用され、それからloadExistingInstantTargetによって使用されるURLとともに、プラットフォームコードから呼び出されます。こ の例では、Instant Targetsの読み込みが成功したときに、以前に保存されたARが再作成されます。

loadExistingInstantTargetFromUrl: function (url) {
    var mapResource = new AR.TargetCollectionResource(url);
    this.tracker.loadExistingInstantTarget(mapResource, function () {
        var augmentations = JSON.parse(augmentationsJSON);

        World.instantTrackable.drawables.removeCamDrawable(World.drawables);
        World.drawables.forEach(function (drawable) {
            drawable.destroy();
        });
        World.drawables = [];
        augmentations.forEach(function (model) {
            var modelIndex = rotationValues.length;

            rotationValues[modelIndex] = model.rotate.z;
            scaleValues[modelIndex] = model.scale.x;

            World.drawables.push(new AR.Model(model.uri, {
                translate: model.translate,
                rotate: model.rotate,
                scale: model.scale,
                onDragBegan: function() {
                    oneFingerGestureAllowed = true;
                },
                onDragChanged: function(relativeX, relativeY, intersectionX, intersectionY) {
                    if (oneFingerGestureAllowed) {
                        // We recommend setting the entire translate property rather than
                        // its individual components as the latter would cause several
                        // call to native, which can potentially lead to performance
                        // issues on older devices. The same applied to the rotate and
                        // scale property
                        this.translate = {x:intersectionX, y:intersectionY};
                    }
                },
                onRotationChanged: function(angleInDegrees) {
                    this.rotate.z = rotationValues[modelIndex] - angleInDegrees;
                },
                onRotationEnded: function() {
                    rotationValues[modelIndex] = this.rotate.z
                },
                onScaleChanged: function(scale) {
                    var scaleValue = scaleValues[modelIndex] * scale;
                    this.scale = {x: scaleValue, y: scaleValue, z: scaleValue};
                },
                onScaleEnded: function() {
                    scaleValues[modelIndex] = this.scale.x;
                }
            }))
        });
        World.instantTrackable.drawables.addCamDrawable(World.drawables);
    }, function (error) {
        alert("Loading failed: " + error);
    }, {
        expansionPolicy: AR.CONST.INSTANT_TARGET_EXPANSION_POLICY.ALLOW_EXPANSION
    })
}

Instant Targetsのためにパスを設定する

Instant Targetsのための最初のパスをセットします。

private final File instantTargetSaveLocation;

public SaveLoadInstantTargetExtension(final Activity activity, final ArchitectView architectView) {
    super(activity, architectView);
    instantTargetSaveFile = new File(activity.getExternalFilesDir(null), "SavedInstantTarget.wto");
    savedAugmentationsFile = new File(activity.getExternalFilesDir(null), "SavedAugmentations.json");
}

JavaScriptからJSONObjectsを取得するには、JavaScriptInterfaceListenerを設定します。

@Override
public void onCreate() {
    architectView.addArchitectJavaScriptInterfaceListener(this);
}

@Override
public void onDestroy() {
    architectView.removeArchitectJavaScriptInterfaceListener(this);
}

JSONオブジェクトが送信されると、それが解析され、インスタントターゲットを格納および読み込むパスがarchitectView.callJavascriptを使用してJavaScriptに送信されます。

@Override
public void onJSONObjectReceived(final JSONObject jsonObject) {
    try {
        switch (jsonObject.getString("action")) {
            case "save_current_instant_target":
                saveAugmentations(jsonObject.getString("augmentations"));
                saveCurrentInstantTarget();
                break;
            case "load_existing_instant_target":
                loadExistingInstantTarget();
                break;
        }
    } catch (JSONException e) {
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(activity, R.string.error_parsing_json, Toast.LENGTH_LONG).show();
            }
        });
    }
}

private void saveAugmentations(String data) {
    try (FileOutputStream stream = new FileOutputStream(savedAugmentationsFile)) {
        stream.write(data.getBytes());
    } catch (IOException e) {
        Log.e(TAG, "Could not save augmentations.", e);
    }
}

private String loadAugmentations() {
    int length = (int) savedAugmentationsFile.length();

    byte[] bytes = new byte[length];
    String jsonString;

    try (FileInputStream in = new FileInputStream(savedAugmentationsFile)) {
        in.read(bytes);
        jsonString = new String(bytes);
    } catch (IOException e) {
        jsonString = "";
    }

    return JSONObject.quote(jsonString);
}

private void loadExistingInstantTarget() {
    architectView.callJavascript(String.format("World.loadExistingInstantTargetFromUrl(\"%s\", %s)", instantTargetSaveFile.getAbsolutePath(), loadAugmentations()));
}

private void saveCurrentInstantTarget() {
    architectView.callJavascript(String.format("World.saveCurrentInstantTargetToUrl(\"%s\")", instantTargetSaveLocation.getAbsolutePath()));
}