LoginSignup
0
0

More than 1 year has passed since last update.

Pythonista3 でARKit 拡張現実体験をするよーーー!!!

Last updated at Posted at 2022-12-18

この記事は、Pythonista3 Advent Calendar 2022 の19日目の記事です。

一方的な偏った目線で、Pythonista3 を紹介していきます。

ほぼ毎日iPhone(Pythonista3)で、コーディングをしている者です。よろしくお願いします。

以下、私の2022年12月時点の環境です。

sysInfo.log
--- SYSTEM INFORMATION ---
* Pythonista 3.3 (330025), Default interpreter 3.6.1
* iOS 16.1.1, model iPhone12,1, resolution (portrait) 828.0 x 1792.0 @ 2.0

他の環境(iPad や端末の種類、iOS のバージョン違い)では、意図としない挙動(エラーになる)なる場合もあります。ご了承ください。

ちなみに、model iPhone12,1 は、iPhone11 です。

この記事でわかること

  • ARKit をPythonista3 で起動する
  • 前面カメラで世界を映す
  • AR 世界にオブジェクトを出す

Pythonista3 の拡張現実の世界へようこそ

拡張現実(AR)の開発ができるARKit も、objc_util から呼び出しが可能です。

AR とは、ARKit とは。の部分は別の解説に譲るとして、この記事ではPythonista3 でAR が使える状態になるまでを紹介します。

ARKit - 日本語ドキュメント - Apple Developer

RealityKit は呼び出せないかも

iOS 関係でAR を調べると、ARKit 以外にRealityKit もありますが、Pythonista3 でRealityKit は(私の観測上だと)呼び出せません。

(私が察する理由ですが)Swift でしか開発ができないので、objc_util で呼び出せないのだと思われます。

自分が実装してみたい内容の背景が、何で構成されているのかの確認は必要ですね。

RealityKitの概要 - 拡張現実 - Apple Developer

ARKit とSceneKit(または、SpriteKit) の連携

前回までのSceneKit の技術をたくさん使います。

ARKit で取得した現実情報とSceneKit のSCNScene を結びつけ画面上に登場させるイメージです。

「これはARKit での処理、こっちはSceneKit の処理」と課題の分離ができると、確認用のミニマムな実装も行いやすいと思います。

ARSCNView → SCNView からAR のView に変更
  |
SCNScene
  |
rootNode
↑ ↑ ↑ ↑ addChildNode(Node)
さまざまなNode たち

AR の世界へダイブ

ARKit のミニマムな内容と、SceneKit 生成したBox を呼び出します。

リアカメラから現実世界を取得し、SceneKit のBox はその場に止まっている状態です。

ちなみにload_framework('SceneKit') の呼び出しは不要です。

load_framework('ARKit') とARKit をロードすることで、SceneKit も呼び出されます。

from objc_util import load_framework, ObjCClass
from objc_util import UIColor
import ui

import pdbg

load_framework('ARKit')

ARSCNView = ObjCClass('ARSCNView')
ARWorldTrackingConfiguration = ObjCClass('ARWorldTrackingConfiguration')

SCNScene = ObjCClass('SCNScene')
SCNNode = ObjCClass('SCNNode')

SCNAction = ObjCClass('SCNAction')

SCNBox = ObjCClass('SCNBox')


class GameScene:
  def __init__(self):
    self.scene: SCNScene
    self.setUpScene()

  def setUpScene(self):
    scene = SCNScene.scene()
    scene_rootNode_addChildNode_ = scene.rootNode().addChildNode_

    box = SCNBox.boxWithWidth_height_length_chamferRadius_(0.5, 0.5, 0.5, 0.08)
    box.firstMaterial().diffuse().contents = UIColor.blueColor()

    geometryNode = SCNNode.nodeWithGeometry_(box)
    geometryNode.position = (0, -1.0, -1.0)

    geometryNode.runAction_(
      SCNAction.repeatActionForever_(
        SCNAction.rotateByX_y_z_duration_(0.0, 0.2, 0.1, 0.3)))

    scene_rootNode_addChildNode_(geometryNode)

    self.scene = scene


class ViewController:
  def __init__(self):
    self.sceneView: ARSCNView
    self.scene: GameScene
    self.viewDidLoad()
    self.viewWillAppear()

  def viewDidLoad(self):
    scene = GameScene()

    _frame = ((0, 0), (100, 100))
    sceneView = ARSCNView.alloc().initWithFrame_(_frame)
    sceneView.autoresizingMask = (1 << 1) | (1 << 4)
    sceneView.autoenablesDefaultLighting = True
    sceneView.showsStatistics = True
    ''' debugOptions
    OptionNone = 0
    ShowPhysicsShapes = (1 << 0)
    ShowBoundingBoxes = (1 << 1)
    ShowLightInfluences = (1 << 2)
    ShowLightExtents = (1 << 3)
    ShowPhysicsFields = (1 << 4)
    ShowWireframe = (1 << 5)
    RenderAsWireframe = (1 << 6)
    ShowSkeletons = (1 << 7)
    ShowCreases = (1 << 8)
    ShowConstraints = (1 << 9)
    ShowCameras = (1 << 10)
    ARSCNDebugOptionShowFeaturePoints = (1 << 30)
    ARSCNDebugOptionShowWorldOrigin = (1 << 32)
    '''

    _debugOptions = (1 << 1) | (1 << 30) | (1 << 32)
    sceneView.debugOptions = _debugOptions

    sceneView.scene = scene.scene
    sceneView.autorelease()

    self.scene = scene
    self.sceneView = sceneView

  def viewWillAppear(self):
    self.resetTracking()

  def viewWillDisappear(self):
    self.sceneView.session().pause()

  def resetTracking(self):
    configuration = ARWorldTrackingConfiguration.new()
    self.sceneView.session().runWithConfiguration_(configuration)


class View(ui.View):
  def __init__(self, *args, **kwargs):
    ui.View.__init__(self, *args, **kwargs)
    self.bg_color = 'maroon'
    self.vc = ViewController()
    self.objc_instance.addSubview_(self.vc.sceneView)

  def will_close(self):
    self.vc.viewWillDisappear()


if __name__ == '__main__':
  view = View()
  view.present(style='fullscreen', orientations=['portrait'])

SceneKit からARKit へ書き換え

前回のSceneKit では、objc_util とPythonista3 のui.View の連携としてView class 内で完結をさせていました。

今回はARSCNView のView 実装処理記述が多くなるためui.View とは別にViewController class を用意しました。

objc_util としての、View 設定に関してはSCNView と大きな変化はありません。

サンプルコードに乗っ取り、 UIView のライフサイクルを採用していますが

ViewController のあとに、View 呼んでいるので、なんじゃこりゃって感じなのですが。。。

debugOptions のEnumeration Case

ARSCNViewdebugOptionsSCNView のEnumeration Case を流用して呼び出せます。

''' debugOptions
OptionNone = 0
ShowPhysicsShapes = (1 << 0)
ShowBoundingBoxes = (1 << 1)
ShowLightInfluences = (1 << 2)
ShowLightExtents = (1 << 3)
ShowPhysicsFields = (1 << 4)
ShowWireframe = (1 << 5)
RenderAsWireframe = (1 << 6)
ShowSkeletons = (1 << 7)
ShowCreases = (1 << 8)
ShowConstraints = (1 << 9)
ShowCameras = (1 << 10)
ARSCNDebugOptionShowFeaturePoints = (1 << 30)
ARSCNDebugOptionShowWorldOrigin = (1 << 32)
'''

_debugOptions = (1 << 1) | (1 << 30) | (1 << 32)
sceneView.debugOptions = _debugOptions
  • ARSCNDebugOptionShowFeaturePoints
    • 1 << 30
    • トラッキングされるポイントを可視化
  • ARSCNDebugOptionShowWorldOrigin
    • (1 << 32)
    • AR の座標軸を可視化

の2点がARSCNView にて追加されています。

(1 << 30), (1 << 32) の数値は:

def setDebugOptions(arscn):
    # Work In Progress Here, I'm trying to decipher the arkit constants...
    #val = ARSCNDebugOption.ARSCNDebugOptionShowWorldOrigin | ARSCNDebugOption.ARSCNDebugOptionShowFeaturePoints
    val = int("fffffffffc000000", 16) # this value is a combination of ShowWorldOrigin and ShowFeaturePoints flags, but I can't isolate each flags....
    print('Before calling setDebugOptions_(%s) : debugOptions=%s' %(hex(val), hex(arscn.debugOptions())))
    arscn.setDebugOptions_(val)
    print('After calling setDebugOptions_(%s) : debugOptions=%s' % (hex(val),hex(arscn.debugOptions())))

ここから無理やり割り出したように記憶しています。。。

main.py#L61 | Pythonista/main.py at master · Brun0oO/Pythonista

AR 機能を実行させる

SCNView は、View へaddSubview_ すれば描画されました:

ui.View.objc_instance.addSubview_(SCNView)

ARSCNView では「AR やるでー」と宣言しないと、カメラが起動しAR が始まりません。

「AR やるでー」の機能を司っているのはARSession class です。

ARSession | Apple Developer Documentation

ObjCClass('ARSession') と呼び出さなくとも、ARSCNViewARSCNView.session として、ARSession を呼び出せます。

def resetTracking(self):
  configuration = ARWorldTrackingConfiguration.new()
  self.sceneView.session().runWithConfiguration_(configuration)

ARWorldTrackingConfiguratWorld(リアカメラ)となり、フロントカメラを使った選択はここでおこないます。

img221211_153618

起動しているかどうかは、ステータスバーの右上に緑の が付きます。

「AR おわったでー」と、宣言させないと、終了してくれないので:

def viewWillDisappear(self):
  self.sceneView.session().pause()


class View(ui.View):
  def __init__(self, *args, **kwargs):
    ui.View.__init__(self, *args, **kwargs)
    self.bg_color = 'maroon'
    self.vc = ViewController()
    self.objc_instance.addSubview_(self.vc.sceneView)

  def will_close(self):
    self.vc.viewWillDisappear()

Pythonista3 のui.View 、終了処理時のui.View.will_close より、終了を呼んでいます。

世界にSceneKit のジオメトリを呼ぶ

SCNScene 内に、SceneKit 実装時と同様の処理で呼び出すことができます。

カメラ(SCNCamera)はリアカメラとなるので、呼び出し不要です。

def setUpScene(self):
  scene = SCNScene.scene()
  scene_rootNode_addChildNode_ = scene.rootNode().addChildNode_

  box = SCNBox.boxWithWidth_height_length_chamferRadius_(0.5, 0.5, 0.5, 0.02)
  box.firstMaterial().diffuse().contents = UIColor.blueColor()

  geometryNode = SCNNode.nodeWithGeometry_(box)
  geometryNode.position = (0, -1.0, -1.0)

  geometryNode.runAction_(
    SCNAction.repeatActionForever_(
      SCNAction.rotateByX_y_z_duration_(0.0, 0.2, 0.1, 0.3)))

  scene_rootNode_addChildNode_(geometryNode)

  self.scene = scene

box のサイズがSCNBox.boxWithWidth_height_length_chamferRadius_(0.5, 0.5, 0.5, 0.08) かなり小さいです。

大きすぎると、Material の設定によっては何も描画されないのでお気をつけください。

次回は

SceneKit からARKit への流れですと想定より簡単にAR が実装できたのではないかと思います。

SCNScene 内のbox は、呼び出し時の位置から移動せず、カメラが動くと見切れてしまいました。

次回は、現実世界の情報をトラッキングして、良き位置に配置できるようにしていきます。

ここまで、読んでいただきありがとうございました。

せんでん

Discord

Pythonista3 の日本語コミュニティーがあります。みなさん優しくて、わからないところも親身に教えてくれるのでこの機会に覗いてみてください。

書籍

iPhone/iPad でプログラミングする最強の本。

その他

  • サンプルコード

Pythonista3 Advent Calendar 2022 でのコードをまとめているリポジトリがあります。

コードのエラーや変なところや改善点など。ご指摘やPR お待ちしておりますー

  • Twitter

なんしかガチャガチャしていますが、お気兼ねなくお声がけくださいませー

  • GitHub

基本的にGitHub にコードをあげているので、何にハマって何を実装しているのか観測できると思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0