ARWorldMap
AR1.0までAR体験ができてもそれを永続化することができなかった。
こういった弱点を克服するためにARWorldMapは導入され、データの永続化と共有ができるようになった。
具体的にはARSessionでトラッキングした実世界の情報とARアンカーの情報が入っている。
実際なプロパティ
// ワールドマップで検知されたARアンカーたち
var anchors: [ARAnchor] { get set }
// ワールドマップの特徴点
var rawFeaturePoints: ARPointCloud { get }
// ワールドマップの空間のサイズ
var extent: simd_float3 { get }
var center: simd_float3
// ワールドマップの中心点
これらのデータを使用してARSessionを復元する。
ARWorldMapを取得する
ARSessionの下記メソッドを使用して非同期に取得する。
func getCurrentWorldMap(completionHandler: @escaping (ARWorldMap?, Error?)
-> Void)
実際に取得してみる
let session = ARSession()
session.getCurrentWorldMap { worldMap, error in
guard let worldMap = worldMap else { fatalError() }
}
実際に保存するにはNSKeyedArchiverクラスからarchivedDataメソッドでData型に変換し、
data.write(to: url)を呼び出す。
func writeWorldMap(_ worldMap: ARWorldMap, to url: URL) throws {
let data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true)
try data.write(to: url)
}
実際に ARWorldMapを取り出すためにはNSKeyedArchiverからunarchviedObjectを呼び出す。
func loadWorldMap(from url: URL) throws -> ARWorldMap {
let mapData = try Data(contentsOf: url)
guard let worldMap = try NSKeyedUnarchiver.unarchivedObject(ofClass: ARWorldMap.self, from: mapData)
else { throw ARError(.invalidWorldMap) }
return worldMap
}
ワールドマップからセッションを復元してみる
ARWorldTrackingConfigurationには下記のプロパティが存在する
var initialWorldMap: ARWorldMap? { get set }
ここに Data型から復元したARWorldMapオブジェクトをセットしてrunすることでできる。
let configuration = ARWorldTrackingConfiguration()
configuration.initialWorldMap = worldMap
sceneView.session.run(configuration)
これを応用すれば保存したDataを何処かに保存しといて、他の誰かと共有することができる。
ワールドマップをゲットできるタイミング
ARFrameに worldMappingStatusという ARFrame.WorldMap
pingStatus型のプロパティが存在し、ワールドマップを取得するのに良いタイミングかどうかを判断する
var worldMappingStatus: ARFrame.WorldMappingStatus { get }
ARFrame.WorldMappingStatusは enum 型で、次の caseが定義されています。
enum WorldMappingStatus {
case notAvailable
//ワールドマッピングができない
case limited
// ワールドマッピング可能だが、機能制限が出てきてしまう
case extending
// ユーザーの操作によってマッピングが拡大中
case mapped
//見えているエリアのマッピング完了した状態
}
他のステータスではユーザーに行動の指針を示す、といったことが可能になります。
オブジェクトが停止しなければフレームごとに呼ばれる。
そこで保存ボタンなどを
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
// オブジェクトが更新されるたびにARFrameオブジェクトを取り出す。
guard let frame = sceneView.session.currentFrame else { return }
DispatchQueue.main.async {
// ワールドマッピングステータスが mapped のときだけ保存ボタンを有効にする
if frame.worldMappingStatus == .mapped {
ここで保存を実行するボタンをアクティブとかにしてみる。
}
}
}
参考