2013年7月26日金曜日

LEAP MOTION + openFrameworksでアプリケーション開発


先日発売になったLEAP MOTIONが、ようやく手元に届きました。
予約注文をしてから実に丸一年、予約した事を忘れてしまう程長かったです。

届いて以来、openFrameworksを使ってLEAP MOTIONで遊んでいるので、簡単な開発の仕方などご紹介します。



LEAP MOTIONとは
多くのメディアでも取り上げられていますが、LEAP MOTIONは手の動きを検知するモーションコントローラーです。発表当時のデモ動画のクオリティが素晴らしく、一躍脚光を浴びました。(それから届くまで一年)




実際使ってみて、デモ動画と較べても遜色の無いクオリティでした。
検知範囲の狭さや、検知されにくい指の角度など、物理的なセンサーのサイズによる制約はあるにせよ、次世代の入力インターフェースとしての可能性を強く感じます。


製品としてのパッケージングも素晴らしく、専用アプリのインストール、チュートリアル、LEAP MOTION対応アプリのストアまでの導線が完璧に出来ていて、ほぼ迷う事なく導入する事が出来ます。


openFrameworksでLEAP MOTION
アプリを幾つか購入して遊んでからは、LEAP MOTIONのSDKをダウンロードして、コードを書きながらLEAP MOTIONの仕組みを勉強しています。


実はこれまでろくに3Dの実装をした事が無かったので、LEAP MOTIONが届くのを待つ傍ら、ここ2週間ほどopenFrameworksをいじって勉強していました。なので、基本的にはopenFrameworksの作法に則って、LEAP MOTIONのAPIを組み込む様な形で実装しています。


今のところ手と指の検知、それによる3Dのオブジェクトの回転、拡大縮小といった動きが確認出来ていて、こんな↓事が出来ています。動画作りました。





手のボーンが表現出来ていないので少々判りにくいですが、5個ある小さな赤い球体が指先で、その手前にある大きめな球体が掌の位置を表しています。




openFrameworksでLEAP MOTIONを動かすまでの最短手順をメモしておきます。
1.openFrameworksインストール
openFrameworksのWebサイトから、openFrameworksをダウンロードします。ダウンロードしたら、適当なディレクトリに展開します。


2.LEAP MOTIONのSDKインストール
続けてLEAP MOTIONのデベロッパーWebサイトからSDKをダウンロードします。こちらも展開して適当なディレクトリに置きます。


3.openFrameworksのプロジェクト作成
まずはopenFrameworksでXCodeプロジェクトの雛形を作ります。
openFrameworsにはprojectGeneratorというツールがあり、簡単にプロジェクトのスケルトンを作る事が出来ます。



「Name」にプロジェクト名を適当に指定して「GENERATE PROJECT」ボタンを押します。



プロジェクトはopenFrameworksのディレクトリの/apps/myAppsの下に生成されます。


4.LEAP MOTIONのSDK組み込み
openFrameworksのプロジェクトにLEAP MOTIONのSDKを組み込みます。先ずはヘッダーファイルのパスをBuild Settingsの「Header Search Paths」に追加。



次にLEAP MOTION SDKのlib/libLeap.dylibをプロジェクトにドラッグして追加します。



ここでビルドすると、以下の様なエラーが発生するはずです。


これはopenFrameworksの定義と競合してしまっている為に起きています。以下の様に、#ifndefで囲う事で回避できます。




コンパイルが出来たら、プロジェクトを実行します。しかし以下の様なエラーが出てプログラムが終了するはずです。



これを回避するには、Edit Schemeで実行時の環境変数にDYLD_LIBRARY_PATHにLEAP MOTIONのlibディレクトリへのパスを指定して追加します。



これでエラーが出なくなるはずです。

↑上記の方法だとXCodeからの実行時しかエラーが回避できない事が判りました。bin/ディレクトリに出来上がった実行バイナリを直接実装すると、DYLD_LIBRARY_PATH変数が適用されないので、結局同じエラーが発生して落ちます。

以下のように、Build PhasesのAdd Build PhaseからAdd Copy Filesを選択し、libLeap.dylibを追加して下さい。DestinationはExecutableを指定します。



5.簡単なサンプルコード
testApp.h
#pragma once

#include "ofMain.h"
#include "Leap.h"

// Leap Motionのネームスペース
using namespace Leap;

class testApp : public ofBaseApp{

public:
    void setup();
    void update();
    void draw();

    void keyPressed  (int key);
    void keyReleased(int key);
    void mouseMoved(int x, int y );
    void mouseDragged(int x, int y, int button);
    void mousePressed(int x, int y, int button);
    void mouseReleased(int x, int y, int button);
    void windowResized(int w, int h);
    void dragEvent(ofDragInfo dragInfo);
    void gotMessage(ofMessage msg);

    // Leap Motionのコントローラー
    Controller controller;
    // カメラ
    ofCamera camera;
    // 球体の描画処理
    void drawSphere(Leap::Vector vector, float radius);
};

testApp.cpp
#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
    // カメラの初期位置と方向を指定
    camera.setFov(60);
    camera.setPosition(0, 200, ofGetWidth()/3);
    camera.lookAt(ofVec3f(0, 200, 0));
}

//--------------------------------------------------------------
void testApp::update(){
}

//--------------------------------------------------------------
void testApp::draw(){
    
    camera.begin();
    // 背景を黒に塗りつぶし
    ofBackground(0, 0, 0);
    // フレームを取得
    Frame frame = controller.frame();
    // Handをあるだけ列挙
    for(int i=0; i<frame.hands().count(); i++) {
        Hand hand = frame.hands()[i];
        // てのひらの位置に球体を描画
        drawSphere(hand.palmPosition(), 20);
        // Hand内のFingerをあるだけ描画
        for(int j=0; j<hand.fingers().count(); j++) {
            Finger finger = frame.fingers()[j];
            drawSphere(finger.tipPosition(), 8);
        }
    }
    camera.end();
}

void testApp::drawSphere(Vector vector, float radius) {
    // 球体の描画処理    
    ofNoFill();
    ofPushMatrix();
    ofPoint point = ofPoint(vector.x, vector.y, vector.z);
    ofTranslate(point);
    ofRotateX(point.x);
    ofRotateY(point.y);
    ofRotateZ(point.z);
    ofSetColor(0xCC,0,0,255);
    ofSphere(radius);
    ofPopMatrix();
}

//--------------------------------------------------------------
void testApp::keyPressed(int key){
}

//--------------------------------------------------------------
void testApp::keyReleased(int key){
}

//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){
}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){
}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){
}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){
}

//--------------------------------------------------------------
void testApp::windowResized(int w, int h){
}

//--------------------------------------------------------------
void testApp::gotMessage(ofMessage msg){
}

//--------------------------------------------------------------
void testApp::dragEvent(ofDragInfo dragInfo){
}

上記のソースをそれぞれのファイルにコピペして、実行して下さい。
LEAP MOTIONに手をかざすと、以下の様にてのひらと指を検知する事が出来るはずです。




以上、openFrameworksでLEAP MOTIONを動かす簡単なサンプルでした。

今後も引き続き、LEAP MOTIONを使って何が出来るのか試していくので、またご紹介していきたいと思います。



LEAP MOTION関連記事:
・LEAP MOTION + openFrameworksでアプリケーション開発
LEAP MOTION + openFrameworks 手のモデル解説
LEAP MOTION + openFrameworks 手のモデル改良