第2章 ARKit 堤 修一/@shu223
コード内コメント(1)で示した行で、平面ジオメトリを作成する際に、そのサイズをARPlaneAnchorのextentプロパティのxとzだけから決定しています。この理由については 「2.3.2 平面検出に関するイベントをフックする - ARSessionDelegate」で解説しました。
(2)で示した行では、「平面ジオメトリを持つノード」を、x軸まわりに90度回転させています。これはSCNPlaneジオメトリはデフォルトではx-y平面に対して作成されるので、それをx-z平面(=ARKit/SceneKitの座標系における水平面)に重なるようにするためです。
前節で現実世界の水平面を検出できるようになりました。次は、その検出した水平面に3Dモデルを仮想オブジェクトとして設置してみましょう。
ここまでの節で、ARSCNViewがSCNSceneオブジェクトを持っており、ARSCNViewDelegateのデリゲートメソッドから、検出した平面アンカーに対応するSCNNodeオブジェクトが取得できることは解説しました。あとは仮想オブジェクトとしてのSCNNodeを子ノードとして載せるだけです。
仮想オブジェクトとしてどのような3Dモデルを利用するか、そのノードをどのように扱うかはすべてSceneKitの役割であり、ARKitには依存しません。言い換えると、従来からある実装方法やアセットを利用できる、ということになります。
では、具体的な実装を見ていきましょう。
サンプルコード:03_VirtualObject
2.4.1 3Dモデルを読み込む
まず、仮想オブジェクトとして設置する3Dモデルを読み込みます。.scnファイルから読み込む場合は、SCNSceneのイニシャライザを利用できます。
// シーンファイルからシーンを生成 let scene = SCNScene(named: "filename.scn", inDirectory: "directoryname")! // 生成したシーンのrootNode配下の子ノードを別のノードに載せ替える let virtualObjectNode = SCNNode() for child in scene.rootNode.childNodes { virtualObjectNode.addChildNode(child) }
シーンファイルから生成したSCNSceneオブジェクトのrootNode配下の子ノードを別のノードに載せ替えています。これは、ARSCNViewが保持しているシーンのノード階層下に置けるようにするための、従来どおりの(ARKit登場以前からある)手法です。
SCNSceneSourceクラスを利用すれば、.daeなど他のファイルフォーマットから読み込むこともできます。
let url = Bundle.main.url(forResource: "filename", withExtension: "dae")! let sceneSource = SCNSceneSource(url: url, options: nil)! let virtualObjectNode = sceneSource.entryWithIdentifier("modelId", withClass: SCNNode.self)!