LoginSignup
5

More than 5 years have passed since last update.

pythonista3を使ってmotionセンサのデータをSceneKitで描画

Last updated at Posted at 2017-05-03

前回、sceneを使ってmotionセンサの値から回転直方体を描画させましたが、今回は、scenekitを使って描画します。
光があたって3次元で表示されるので前回より少しかっこいいです。
IMG_0342.JPG

ソースは以下のとおり。

motion_with_scenkit.py
# coding: utf-8
from objc_util import *
import ui
import math
import motion
from scene import *
load_framework('SceneKit')
SCNView, SCNScene, SCNBox, SCNText, SCNNode, SCNLight, SCNCamera, SCNAction, SCNTransaction,UIFont= map(ObjCClass, ['SCNView', 'SCNScene', 'SCNBox', 'SCNText', 'SCNNode', 'SCNLight',  'SCNCamera', 'SCNAction','SCNTransaction','UIFont'])
class SCNVector3 (Structure):
    _fields_ = [('x', c_float), ('y', c_float), ('z', c_float)]
W=30
L=15
H=2.5

@on_main_thread
class MyScene (Scene):
    def setup(self):
        #motion start
        motion.start_updates()
    def draw(self):
        #motion update
        gravity_vectors=motion.get_attitude()
        pitch, roll, yaw = [x for x in gravity_vectors]
        self.box_node.runAction_(SCNAction.rotateToX_y_z_duration_(pitch,-roll,yaw,0))
    def make_view(self,mc):
        pitch,roll,yaw=0.0,0.0,0.0
        main_view_objc = mc
        scene_view = SCNView.alloc().initWithFrame_options_(((0, 0),(100, 100)), None).autorelease()
        scene_view.setAutoresizingMask_(18)
        scene_view.setAllowsCameraControl_(True)
        scene = SCNScene.scene()
        root_node = scene.rootNode()  

        box= SCNBox.boxWithWidth_height_length_chamferRadius_(W, L,H,0)
        self.box_node = SCNNode.nodeWithGeometry_(box)
        self.box_node.setPosition_((0, 0, 0))

        base= SCNBox.boxWithWidth_height_length_chamferRadius_(100, 100, 4, 0)
        base_node = SCNNode.nodeWithGeometry_(base) 
        base_node.setPosition_((0, 0, -20))

        light_node = SCNNode.node()
        light_node.setPosition_((0, 0, 70))
        light_node.setRotation_((0, 0, 1, -math.pi/2))
        light = SCNLight.light()
        light.setType_('spot')
        light.setCastsShadow_(True)
        light.setColor_(UIColor.cyanColor().CGColor())
        light_node.setLight_(light)

        camera = SCNCamera.camera()
        camera_node = SCNNode.node()
        camera_node.setCamera(camera)
        camera_node.setPosition((0, 0,80))
        camera_node.setRotation_((0, 0, 1, -math.pi*1/2))

        root_node.addChildNode_(camera_node)
        root_node.addChildNode_(self.box_node)
        root_node.addChildNode_(base_node)
        root_node.addChildNode_(light_node)

        scene_view.setScene_(scene)
        main_view_objc.addSubview_(scene_view) 
if __name__ == "__main__":
    #set view
    main_view = ui.View()
    main_view_objc = ObjCInstance(main_view)
    main_view.name = 'SceneKit Demo'
    #run MyScene
    my_scene = MyScene()
    scene_view = SceneView()
    scene_view.scene = my_scene
    #make scenekit
    main_view_objc.addSubview_(scene_view)
    my_scene.make_view(main_view_objc)
    #present view
    main_view.present()  

少し解説。
Scenkitを使うためにobjc_utilをimportして、scenekitのメソッドをmapします。

from objc_util import *

load_framework('SceneKit')
SCNView, SCNScene, SCNBox, SCNText, SCNNode, SCNLight, SCNCamera, SCNAction, SCNTransaction,UIFont= map(ObjCClass, ['SCNView', 'SCNScene', 'SCNBox', 'SCNText', 'SCNNode', 'SCNLight',  'SCNCamera', 'SCNAction','SCNTransaction','UIFont'])

def make_view(self,mc):以下でBox、Base、Light、Cameraをセットしていきます。
SCNView,SCNSene,rootNodeをまずセットします。

def make_view(self,mc):
        pitch,roll,yaw=0.0,0.0,0.0
        main_view_objc = mc
        scene_view = SCNView.alloc().initWithFrame_options_(((0, 0),(100, 100)), None).autorelease()
        scene_view.setAutoresizingMask_(18)
        scene_view.setAllowsCameraControl_(True)
        scene = SCNScene.scene()
        root_node = scene.rootNode()  

Boxは、幅、高さ、長さ、コーナーのカーブの設定をします。

        box= SCNBox.boxWithWidth_height_length_chamferRadius_(W, L,H,0)
        self.box_node = SCNNode.nodeWithGeometry_(box)
        self.box_node.setPosition_((0, 0, 0))

Baseはboxの下に配置します。

        base= SCNBox.boxWithWidth_height_length_chamferRadius_(100, 100, 4, 0)
        base_node = SCNNode.nodeWithGeometry_(base) 
        base_node.setPosition_((0, 0, -20))

lightノードとcameraノードをセットします。
lightは、今回はspotにセットにします。
cameraは視野に入るようにセットします。

        light_node = SCNNode.node()
        light_node.setPosition_((0, 0, 70))
        light_node.setRotation_((0, 0, 1, -math.pi/2))
        light = SCNLight.light()
        light.setType_('spot')
        light.setCastsShadow_(True)
        light.setColor_(UIColor.cyanColor().CGColor())
        light_node.setLight_(light)

        camera = SCNCamera.camera()
        camera_node = SCNNode.node()
        camera_node.setCamera(camera)
        camera_node.setPosition((0, 0,80))
        camera_node.setRotation_((0, 0, 1, -math.pi*1/2))

各ノードをrootノードに付け加えて,viewにセットすればscenekitの世界が完成します。

        root_node.addChildNode_(camera_node)
        root_node.addChildNode_(self.box_node)
        root_node.addChildNode_(base_node)
        root_node.addChildNode_(light_node)

        scene_view.setScene_(scene)
        main_view_objc.addSubview_(scene_view) 

本来は、CADisplayLinkをセットして再描画させるのでしょうが、良くわからなかったので、今回は、sceneのdrawメソッドでレンダリングループさせます。box_nodeをrunActionを使って回転させます。

   def draw(self):
        #motion update
        gravity_vectors=motion.get_attitude()
        pitch, roll, yaw = [x for x in gravity_vectors]
        self.box_node.runAction_(SCNAction.rotateToX_y_z_duration_(pitch,-roll,yaw,0))

run(MyScene())のかわりに以下のコードでSceneを走らせます。

    #run MyScene
    my_scene = MyScene()
    scene_view = SceneView()
    scene_view.scene = my_scene
    main_view_objc.addSubview_(scene_view)

以上で、scenekitを使って、motionセンサの値からipadの動きで直方体をグルグルさせる方法の説明を終わります。
scenkitは物理シミュレーションなどいろいろおもしろそうです。

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
5