LEAP MOTION + openFrameworks 手のモデル改良
前回のエントリで掲載した、openFrameworkでの手モデルの描画方法を改良しました。
上図の通り、LEAP MOTIONは掌の以下の情報を取得する事が出来ます。
palmPosition
掌の中心部のXYZ座標を表しています。
palmNormal
掌のLEAP MOTIONの水平面に対する角度(ベクトル)を表しています。
direction
掌が指す先の角度を表しています。
しかし、前回のエントリで掲載したコードでは、掌の状態のうちdirectionの角度が正しく反映出来ていませんでした。
具体的に間違っている例を御覧ください。
↓
この例では、ピッチ(X軸)とロール(Z軸)の回転は正しく反映出来ているのですが、ヨー(Y軸)の回転が出来ていませんでした。そのため手の指先を左方向に向けると、掌の立方体が回転せずに指だけが左方向に移動しています。
これに対して、改良した例がこちらです。
↓
こちらではヨー(Y軸)の回転も正しく表現出来ています。
コードの変更点は116〜118行目のハイライトされている箇所です。directionからY軸の角度だけを取り出して、更に回転させる処理を追加しています。
#include "testApp.h" #include "Poco/Mutex.h" //-------------------------------------------------------------- void testApp::setup(){ // カメラの初期位置と方向を指定 camdistance = ofGetWidth()/4; camdegree = 90; camera.setFov(60); camera.setPosition(0, 200, camdistance); 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]; // Hand内のFingerをあるだけ描画 for(int j=0; j<hand.fingers().count(); j++) { Finger finger = frame.fingers()[j]; drawFinger(finger); } // Handを描画 drawPalm(hand); } camera.end(); } void testApp::drawFinger(Finger finger) { // 指先の点を描画 ofPoint tip(finger.tipPosition().x, finger.tipPosition().y, finger.tipPosition().z); drawPoint(tip); // 指の付け根の座標を計算 ofPoint base = ofPoint(tip.x + finger.direction().x * finger.length() * -1, tip.y + finger.direction().y * finger.length() * -1, tip.z + finger.direction().z * finger.length() * -1); // 指の付け根を描画 drawPoint(base); // 指先から付け根に線を描く ofLine(tip.x, tip.y, tip.z, base.x, base.y, base.z); // 付け根から掌に線を描く ofLine(base.x, base.y, base.z, finger.hand().palmPosition().x, finger.hand().palmPosition().y, finger.hand().palmPosition().z); // 指の箱を描画 drawFingerBox(finger, tip, base); } // 点を描画 void testApp::drawPoint(ofPoint point) { ofPushMatrix(); ofTranslate(point); ofNoFill(); ofSetColor(0xFF, 0xFF, 0xFF, 255); ofSphere(3); ofPopMatrix(); } // 指の箱を描画 void testApp::drawFingerBox(Finger finger, ofPoint tip, ofPoint base) { // 指の中間の座標 ofPoint middle = base.middle(tip); ofPushMatrix(); ofTranslate(middle); // 指の方向に従い回転 ofQuaternion quat; quat.makeRotate(ofPoint(0, -1, 0), ofPoint(finger.direction().x, finger.direction().y, finger.direction().z)); ofMatrix4x4 matrix; quat.get(matrix); glMultMatrixf(matrix.getPtr()); ofNoFill(); ofSetColor(0xCC,0,0,255); ofScale(1, finger.length()/10, 1); ofBox(10); ofPopMatrix(); } void testApp::drawPalm(Hand hand) { // 掌の描画処理 ofPoint point = ofPoint(hand.palmPosition().x, hand.palmPosition().y, hand.palmPosition().z); drawPoint(point); ofPushMatrix(); ofTranslate(point); // 掌を回転 ofQuaternion quat; quat.makeRotate(ofPoint(0, -1, 0), ofPoint(hand.palmNormal().x, hand.palmNormal().y, hand.palmNormal().z)); ofMatrix4x4 matrix; quat.get(matrix); glMultMatrixf(matrix.getPtr()); // directionを反映する為に、Y軸の角度を取り出す float rotationY = ofRadToDeg(atan2(hand.direction().x, hand.direction().z)) + 180; ofRotateY(rotationY); ofNoFill(); ofSetColor(0xCC,0x0,0x0,255); ofScale(1, 0.25, 1.0); ofBox(0, 0, 0, 60); ofPopMatrix(); } //-------------------------------------------------------------- void testApp::keyPressed(int key){ switch (key) { case 358: // 右 camdegree += 5; if(camdegree > 360) camdegree = 0; break; case 356: // 左 camdegree -= 5; if(camdegree < 0) camdegree = 360; break; case 357: // 上 camdistance -= 10; break; case 359: // 下 camdistance += 10; break; default: break; } float radian = ofDegToRad(camdegree); float x = camdistance * cos(radian); float z = camdistance * sin(radian); camera.setPosition(x, camera.getY(), z); camera.lookAt(ofVec3f(0, 200, 0)); } //-------------------------------------------------------------- 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){ }
そもそも、positionNormal()とdirection()の2つのベクトルからクォータニオンを求める事が出来れば1度の処理で済みそうなのですが、残念ながらその計算方法が判りませんでした。
どなたか、もっとスマートな方法がありましたら教えて下さい。m(_ _)m
LEAP MOTION関連記事:
・LEAP MOTION + openFrameworksでアプリケーション開発
・LEAP MOTION + openFrameworks 手のモデル解説
・LEAP MOTION + openFrameworks 手のモデル改良