下記のセッションは、Wikitude Java Script SDK の機能の詳細を、簡単な実行例とそのうえで開発した2つの成功した実行例を紹介します。最初は、Wikitude Java Script SDK が提供する容易さを示しながら、最小限必要な準備をします。その後に続く事例は、3D モデルARと、補助的な自在な回転機能を追加します。それは、実用的で本格的に使えうことのできる事例です。
個々のセクションが重複しないようにするために、事例別に作業することをお勧めします。
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 |
平面の方向 | x | ☑ |
カメラの制御 | x | ☑ |
Instant Targetの保存と読み込み | x |
☑ |
平面検出 | x |
☑ |
インスタントトラッキングは、以前にWikitude SDKで導入されたものとは異なり、あらかじめ定義されたターゲットを認識し、トラッキングを開始することをせず、ただちに任意の環境でトラッキングを開始するアルゴリズムです。 これにより、非常に具体的な使用方法を実装することができます。 そのような使用方法の1つとして、家具製品の視覚化アプリケーションを実装しています。
アルゴリズムは2つの異なる状態で動作します。 最初のものは初期化状態です。 この状態では、ユーザは、単にデバイスを指示し、それによってインジケータを整列させることによって、トラッキングの起点を定義します。 一旦、(ユーザが積極的に確認する必要がある)ユーザが満足できるものであると判明すると、トラッキング状態が開始されます。 この状態では、環境は継続的にトラッキングされているため、実風景内に拡張現実のイメージを配置することができます。
初期状態
トラッキング状態
インスタントトラッキングのアルゴリズムは、初期化状態で別の入力値を提供することを要求します。 具体的には、トラッキングしているデバイスの高さは、シーンの範囲内で正確に増加したスケールを調整するために必要となります。 この目的のために、3つの例は、メートル単位で高さを設定できる範囲入力要素を備えています。
初期化中にインスタントトラッキングの平面を整列するための別のパラメータを設定できます。この平面を初期化インジケータにして表示させ、床の代わりに壁をターゲットとしてトラッキングするために回転させることができます。詳細については、APIリファレンスAPI
reference
を参照してください。
基本的なインスタントトラッキングの例として、インスタントトラッキングのアルゴリズムの最小限の実装で示します。
これは2つの必須クラスAR.InstantTracker
とAR.InstantTrackable
を使用しています。
既に画像のトラッキングについて精通しているならば、AR.ImageTracker
とAR.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.InstantTracker
のdeviceHeight
プロパティを設定し、範囲入力エレメントに接続します。
この変更は厳密に言えば必須ではありませんが、この方法を用いて、正確にデバイスの高さを指定することを強く推奨します。
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モデルを追加してアプリケーションを拡張します。 内部的には、このタッチで定義された線は、インスタントトラッキングの平面で交差し、モデルのtransformプロパティに適用される交差位置を生成します。 この発生時に、AR.InstantTrackable
のonTrackingPlaneClick
コールバックが呼び出され、交差位置座標が別々のパラメータとして提供されます。
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に関係がないものは排除しています。
風景は空です。
風景にいくつかのクリックでインプットがなされています。
最後に、サンプルアプリケーションをさらに拡張します。前の風景にいくつかの異なる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にある、
onTrackingPlaneDragBegan
と
onTrackingPlaneDragChanged
および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)
Instant Trackingを使用することで、3Dポイントをポイントクラウドからクエリすることができます。このセクションは、対応するサンプルアプリケーションに基づいてこの機能を紹介します。
この機能を使用するために、シーン内に2Dモデルを配置する位置が必要となります。この位置を取得するためにAR.context.onScreenClick
イベントを使用して、入力としてマウスがクリックされたときの座標を受け取る関数をアタッチします。受け取った座標は、変更されていないAR.InstantTrackable
のconvertScreenCoordinateToPointCloudCoordinate
関数に供給することができます。この関数では、入力座標に加えて、クエリ完了時に呼び出される他の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 の保存および読み込みの機能により、ARの実行は複数のユーザーがデバイスやオペレーティングシステムに関係なく、横断的に、持続的にアクセスできるようになります。 さらにInstant Targets を拡大することができます。このセクションでは、サンプルアプリケーションに基づいてこの機能を紹介します。 この機能は、プラットフォームによるトラッキングが有効な場合は使用できません。
Instant Targets を保存するには、アクティブ状態のInstantTrackerがトラッキング状態でなければならず、指定されたパスのディレクトリが存在する必要があります。
有効なパスを取得するにはAR.platform.sendJSONObject
とarchitectView.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 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のための最初のパスをセットします。
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()));
}