SwiftでOculus Rift SDKを使う

Macアプリ記述言語のSwiftからOclus SDKを呼び出す手順が少々面倒でしたので、手順を公開します。

私は、京都電創庵( http://www57.atwiki.jp/kyoto-densouan/ )という電子工作サークルの仲間と「ケットシー・デンジャー」というロボットを作っていて、頭部にステレオカメラOvrvisionを設置し、Oculus Rift DK2で立体的にモニタする機構を組み込んでいます。
https://www.youtube.com/watch?v=ypdVjHlC7Bg
ステレオカメラOvrvisionについてはこちら → http://ovrvision.com/

ロボットをモニタするプログラムをMacで作成したのですが、Oculus RiftとOvrvisionの動きを連動させるため、Oculus SDKのAPIを用いてOculus Riftの向きを読みだしています。
そのためにSwiftからOculus SDKを呼び出す必要がありました。

Oculus Riftをこのような用途で使いたいかたの参考になればと思います。


記事執筆時の環境
・Xcode Version 6.1 (6A1052d)
・Oculus SDK 0.4.3 Beta

#Cocoa Applicationプロジェクトを作成
XcodeでCocoa Applicationのプロジェクトを作成します。**LanguageにはSwiftを選択します。**

#プロジェクトにOculusライブラリを追加
プロジェクトにOculus SDK内のLibOVRフォルダをドロップします。
**※この時、「Add to targets:」のチェックを外しておきます。**
![スクリーンショット 2014-11-18 2.19.29.png](https://qiita-image-store.s3.amazonaws.com/0/26936/605232d8-42e3-7a00-d3cc-bff2ad2629ea.png)

「Linked Frameworks and Libraries」に「Libovr.a」をドロップします。
![スクリーンショット 2014-11-18 2.23.44.png](https://qiita-image-store.s3.amazonaws.com/0/26936/40453d2b-8432-b37b-1eb8-eb1bd65e176e.png)

「Header Search Paths」に「LibOVR/include」を追加します。
![スクリーンショット 2014-11-18 2.31.34.png](https://qiita-image-store.s3.amazonaws.com/0/26936/1f36035c-4407-aedd-9962-549a0012af4e.png)

#Objective-C ソースを追加する
OculusのライブラリはC++用のため、Swiftから呼び出すには、Objective-Cコードを追加します。
メニューの「New File...」から「Cocoa Class」を選んでObjective-Cのソースを生成します。
ブリッジングヘッダ作成確認のダイアログが表示されますので、YESを押して作成します。
※もしブリッジングヘッダ作成確認ダイアログが表示されない場合はこちら http://qiita.com/titoi2/items/abe7b58a9e644b29e8c3 を参考に追加して下さい。
![スクリーンショット 2014-11-18 2.36.56.png](https://qiita-image-store.s3.amazonaws.com/0/26936/a9f38c0a-e523-bd09-e692-1aeec4845190.png)

ここではクラス名をOvrManagerとしておきます。
**C++のライブラリを呼び出すために、OvrManager.m の拡張子を.mm に変更しておきます。**

#Oculus SDKを呼び出すコードを追加する

OvrManagerクラスにOculus SDKを呼び出すコードを記述します。
例としてOculus Riftの向きを読み出すコードを示します。

```obj-c:OvrManager.h
#import <Foundation/Foundation.h>

@interface OvrManager : NSObject

- (void) ovrInitialize;
- (void) ovrDestroy;

- (BOOL) ovrGetOrientationYaw:(CGFloat*)yaw Pitch:(CGFloat*)pitch Roll:(CGFloat*)roll;

@end
```

```obj-c:OvrManager.mm

#import "OvrManager.h"
#import "OVR.H"

@interface  OvrManager()

@property  ovrHmd hmd;

@end

@implementation OvrManager


- (void) ovrInitialize {
    ovr_Initialize();
    self.hmd = ovrHmd_Create(0);
    
    if (self.hmd) {
        // Get more details about the HMD.
        ovrSizei resolution = self.hmd->Resolution;
        NSLog(@"resolution w:%d,h:%d", resolution.w,resolution.h );
        
        ovrHmd_ConfigureTracking(self.hmd, ovrTrackingCap_Orientation |
                                 ovrTrackingCap_MagYawCorrection |
                                 ovrTrackingCap_Position, 0);
    }
}

- (BOOL) ovrGetOrientationYaw:(CGFloat*)yaw Pitch:(CGFloat*)pitch Roll:(CGFloat*)roll {
    ovrTrackingState ts  = ovrHmd_GetTrackingState(self.hmd, ovr_GetTimeInSeconds());
    if (ts.StatusFlags & (ovrStatus_OrientationTracked | ovrStatus_PositionTracked)) {
        OVR::Posef pose = ts.HeadPose.ThePose;
        float hmdYaw, hmdPitch, hmdRoll;
        pose.Rotation.GetEulerAngles<OVR::Axis_Y, OVR::Axis_X, OVR::Axis_Z>(&hmdYaw, &hmdPitch, &hmdRoll);
        
        *yaw  = hmdYaw;
        *pitch = hmdPitch;
        *roll = hmdRoll;
        return YES;
    }
    return NO;
}

- (void) ovrDestroy {
    ovrHmd_Destroy(self.hmd);
    ovr_Shutdown();
}

@end

```

ブリッジングヘッダにOvrManager.hのimportを記述します。

```obj-c:OvrTest-Bridging-Header.h
//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "OvrManager.h"
```

#コーディング例

例としてタイマーで定期的にOculus Riftの向きをログに出力するコードを示します。
実行して、それっぽいログが流れれば成功です。
停止はXcodeのStopで行って下さい。

```swift:AppDelegate.swift
import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    
    @IBOutlet weak var window: NSWindow!
    
    // ロックオブジェクト
    private let lockQueue = dispatch_queue_create("qiita.advent2014.swift.oculus.LockQueue", nil)
    
    private let ovrManager = OvrManager()
    private var timer : NSTimer? = nil
    
    // 向きの現在値
    private var currentYaw : CGFloat = 0.0
    private var currentPitch : CGFloat = 0.0
    private var currentRoll : CGFloat = 0.0
    
    
    func applicationDidFinishLaunching(aNotification: NSNotification) {
        ovrManager.ovrInitialize()
        sensingStart()
    }
    
    func applicationWillTerminate(aNotification: NSNotification) {
        // Insert code here to tear down your application
    }
    
    func sensingStart() {
        dispatch_sync(lockQueue, {
            if self.timer != nil {
                return
            }
            self.timer = NSTimer.scheduledTimerWithTimeInterval(0.05,
                target:self,
                selector:Selector("sensingUpdate"),userInfo:nil,repeats:true);
        })
    }
    
    func sensingStop() {
        dispatch_sync(lockQueue, {
            if self.timer == nil {
                return
            }
            
            self.timer?.invalidate()
            self.timer = nil
        })
    }
    
    @objc private  func sensingUpdate() {
        ovrManager.ovrGetOrientationYaw (&currentYaw, pitch: &currentPitch, roll: &currentRoll)
        NSLog("sensingUpdate Yaw:\(currentYaw), Pitch:\(currentPitch), Roll:\(currentRoll)")
    }
}

```

#終わりに向かって
明日12月06日の記事は、
それでは、Oculusを使って楽しいSwiftライフを！