2012年7月22日日曜日

【code.9leap】画面遷移の基礎!【自作チュートリアルチャレンジ!】

いつも9leapで楽しませてもらってる株式会社ユビキタスエンターテインメントがcode.9leapというサービスを開始しました。
ブラウザ上でゲーム開発を行える「Code.9leap.net」,β版サービスが始まる


これにあたり、「code.9leapカスタム・チュートリアルチャレンジ!」というコンテストが開催されました。

っということで、私もチャレンジしてみようと思います!


私がenchant.jsで最初に悩んだところは画面遷移でした。
ということで、そのあたりのチュートリアルを作ってみました 。


ゲームの基本「タイトル画面 → プレイ画面 → エンド画面」の流れを簡単に説明しています。

 




ゲームの流れを学ぼう1「タイトル画面」



 


 


 

ゲームの流れを学ぼう2「プレイ画面への遷移」

 

 

 

ゲームの流れを学ぼう3「タイトルへ戻る」

 

 

 

ゲームの流れを学ぼう4「ゲーム終了」

 

 

 

ゲームの流れを学ぼう5「1からやってみよう!」

 

 

 

 画面遷移が出来れば、ゲームの見栄えも良くなるでしょう。

これをベースにゲーム作りができるはずです!

 

ちょっとcode.9leapの仕様で変な書き方しているところはありますが・・・

2012年4月1日日曜日

Kinect for Windows SDKによる3次元ポイントクラウド

以前、OpenNIを用いたポイントクラウドの方法を紹介していました
 Kinect OpenNIによる3次元ポイントクラウド - 3次元描画 高速化

今回はマイクロソフトが公式で公開しているKinect for Windows SDKを使ってポイントクラウドを行ってみます。

描画にはいつもどおりOpenGLを使い、その過程でOpenCVを使っています。




まず初めにKinect SDKでRGB画像とDepth画像の取得方法について簡単に説明します。

細かい説明なんてどうでもいいんだよ!って人は 下のほうでサンプルプログラムがダウンロードできるので、下の方へスクロールしてってください。


では、プログラムの初期化処理は以下のようになる



初期化処理では
HRESULT NuiInitialize(
         DWORD dwFlags
)

で初期化します。

引数はRGB画像、Depth画像、骨格情報を得る場合
 NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON
とします 次に画像取得のイベントハンドルを設定します。

Depth画像の大きさによって使用するNuiImageStreamOpen関数の引数が違うので
KINECT_DEPTH_WIDTHを#defineで事前に定義して、ここで条件分岐しています。
 
KinectSDKはBetaの時はDepth画像の解像度を320x240しか設定できなかったのですが
v1.0になり、640x480に設定することが可能となりました。

上記で設定したイベントで以下のようにしてRGB画像とDepth画像を取得します

扱いやすい形にするため、OpenCVのMatに格納します
また、その処理を関数化します


ここではGetImage(cv::Mat &image,HANDLE frameEvent,HANDLE streamHandle)
という関数を作りました。

この関数は以下のように用います。

関数の戻り値が-1の時は取得失敗ということです。

Kinect SDKではOpenNIを違い、Matへの格納が少しめんどうですね。

 次にポイントクラウドを行う上で、画像のピクセルが現実座標ではどこにあるのかを知る必要があります。

OpenNIではConvertProjectiveToRealWorldという関数がありました。

Kinect SDKでは以下の関数を用います

Vector4 NuiTransformDepthImageToSkeleton(
         LONG lDepthX,
         LONG lDepthY,
         USHORT usDepthValue
)


この関数はDepth画像のピクセルの位置(x,y)とその場所の値を与えることで現実座標系で帰ってきます。

では、Depth画像から現実座標配列を格納する関数を以下のように作ります。

NuiTransformDepthImageToSkeletonはDepth画像の解像度により引数を追加します。

これはbetaの時にDepth画像が640x480に対応していなかったからかなぁっと思います。


で、最後にポイントクラウドで描画します。

drawPointCloudではRGB画像とretrievePointCloudMapで取得した三次元位置を渡し、描画します。

また、この関数内でRGBとDepthのズレを補正しています。
OpepNIでいう
DepthGenerator.GetAlternativeViewPointCap().SetViewPoint は

KinectSDKでは
NuiImageGetColorPixelCoordinatesFromDepthPixel
または
NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolution
を使います

これも、Depthの解像度により関数を変える必要があります。

まあ、NuiImageGetColorPixelCoordinatesFromDepthPixelのなかでNuiImageGetColorPixelCoordinatesFromDepthPixelAtResolutionが呼ばれてるんですけどね





これまで長く説明してきましたけど、まあ、こんな説明読むよりサンプルプログラムを見たほうが早いかな?

全体のプログラムはこんな感じです。


github
サンプルダウンロード

2012年3月31日土曜日

PhySprite.enchant.jsに多角形とジョイントを追加しました。

PhySprite.enchant.jsに機能を追加しました。

PhySprite.enchant.jsのダウンロード
ダウンロード
リファレンス

今までのPhySprite.enchant.jsには円形と四角形しか物理オブジェクトをサポートしていませんでした。

今回は多角形を追加し、さらにオブジェクトにImageを設定しなくても形がわかるDebugDraw機能をつけました。

これで多角形の物体のように画像が用意しずらいオブジェクトでもわかりやすくなりました。


多角形
ではまず、多角形の説明からします。

多角形のオブジェクトを生成するには以下の関数を用いる
   
enchant.PhyPolygonSprite(width, height, vertexs, type, density, friction, restitution, awake)
多角形の物理シミュレーション用Sprite

引数は以下の通り
Parameters:
{Number} width Optional
Spriteの横幅.
{Number} height Optional
Spriteの高さ.
{b2Vec2[]} vertexs
多角形の頂点配列
{Boolean} type Optional
静的,動的,キネマティック
{Number} density Optional
Spriteの密度.
{Number} friction Optional
Spriteの摩擦.
{Number} restitution Optional
Spriteの反発.
{Boolean} awake Optional
Spriteが初めから物理演算を行うか.

ここで新しく追加されたvertexsですが、これは多角形の頂点配列です。

どういうものかというと、例えば四角形の場合のvertexsは
   
var width = 20;
var height = 50;
var vertexs = new Array(
    new b2Vec2(-width / 2, -height / 2),
    new b2Vec2(width / 2, -height / 2),
    new b2Vec2(width / 2, height / 2),
    new b2Vec2(-width / 2, height / 2));
var phyPolygonSprite = new PhyPolygonSprite(width, height, vertexs, DYNAMIC_SPRITE, 1.0, 0.1, 0.2, true);
game.rootScene.addChild(phyPolygonSprite); // シーンに追加


このように四角形を作る
vertexsはSpriteの中心座標を(0,0)として時計回り・または反時計回りに宣言していく。



では次に五角形を生成してみます。
次は簡略化のためfor文を使います。
 
var vertexCount = 5;
var radius = 20;
var vertexs = new Array();
for (var i = 0; i < vertexCount; i++) {
vertexs[i] = new b2Vec2(radius * Math.cos(2 * Math.PI / vertexCount * i), radius * Math.sin(2 * Math.PI / vertexCount * i));
}
var phyPolygonSprite = new PhyPolygonSprite(radius * 2, radius * 2, vertexs, DYNAMIC_SPRITE, 1.0, 0.1, 0.2, true);
game.rootScene.addChild(phyPolygonSprite); // シーンに追加


これで五角形を作ることができました。
vertexCountを変更することで三角形でも七角形でも百角形でも可能です。


ここで注意することは、頂点と頂点と間に交点ができてしまうとうまく当たり判定がおかしくなるので気をつけて下さい。



ジョイント
では次はジョイントの説明

ジョイントは複数のタイプがあります。

enchant.PhyDistanceJoint(sprite1, sprite2)
距離ジョイント

enchant.PhyRevoluteJoint(axis, sprite)
物体と物体のモーター付きジョイント
enchant.PhyPrismaticJoint(sprite1, sprite2, anchor1, anchor2, ratio)
スライドジョイント
 
enchant.PhyPulleyJoint(sprite1, sprite2, anchor1, anchor2, ratio)
滑車ジョイント
1つ1つ説明していきます。

enchant.PhyDistanceJoint(sprite1, sprite2)
距離ジョイント
Parameters:
{enchant.PhySprite} sprite1 Optional
繋げるスプライト1
{enchant.PhySprite} sprite2 Optional
繋げるスプライト2
 これは最も単純なジョイントです。
 引数の2つのスプライトをジョイントで繋げます。
   
//軸
var axis = new PhyCircleSprite(8, STATIC_SPRITE);
axis.position = { x: 160, y: 160 };
game.rootScene.addChild(axis); // シーンに追加
//ボール生成
var ball = new PhyCircleSprite(8, DYNAMIC_SPRITE);
ball.position = { x: 100, y: 250 };
game.rootScene.addChild(ball); // シーンに追加
//距離ジョイント
var joint = new PhyDistanceJoint(axis, ball);

ここでは簡略化のため、物理オブジェクトのパラメータを書いていません。
この場合、 density=1.0,friction=0.5,restitution=0.3,awake=trueがデフォルトの値となります。

未定義でもいいってのはここで初めて説明したのかな?
で、この例ではaxisという静止オブジェクトにballがぶら下がっています。


では次

 
enchant.PhyRevoluteJoint(axis, sprite)
物体と物体のモーター付きジョイント
Parameters:
{enchant.PhySprite} axis Optional
軸となるスプライト
{enchant.PhySprite} sprite Optional
繋げるスプライト
これは軸(axis)にモーターを設けることができるジョイントです。


   
//軸
var axis = new PhyCircleSprite(8, STATIC_SPRITE);
axis.position = { x: 160, y: 160 };
game.rootScene.addChild(axis); // シーンに追加
//ボール生成
var ball = new PhyCircleSprite(8, DYNAMIC_SPRITE);
ball.position = { x: 100, y: 250 };
game.rootScene.addChild(ball); // シーンに追加
//距離ジョイント
var joint = new PhyRevoluteJoint(axis, ball);
joint.enableMotor = true;
joint.maxMotorTorque = 100;
joint.motorSpeed = 90;

PhyDistanceJointとの違いはenableMotorでモーターを有効化しなければいけないことと
maxMotorTorqueでモーターのトルクを設定します。
 トルクとは
そしてmotorSpeedでモーターの速度を設定することで、axisに接続されたballが動き出します。

モーター機能を有効にしていないとPhyDistanceJointと変わりません。




enchant.PhyPrismaticJoint(sprite1, axis)
スライドジョイント
Parameters:
{enchant.PhySprite} sprite1 Optional
繋げるスプライト1
{b2Vec} axis Optional

スライドオブジェクトは文字どおりスプライトをスライドさせます。


var box = new PhyBoxSprite(16, 8, DYNAMIC_SPRITE, 1.0, 0.5, 0.2, true);
box.position = { x: game.width * 2 / 3, y: game.height / 2 };
var prismaticAxis = new b2Vec2(1.0, 0);   //x軸にスライドを設定(右が正の値)
//スライドジョイント
var prismaticJoint = new PhyPrismaticJoint(box, prismaticAxis);
//スライドオブジェクトにモーター機能を持たせる場合
//prismaticJoint.enableMotor = true;      //モータの有効化
//prismaticJoint.maxMotorForce = 100.0;   //モータの最大力を設定
//prismaticJoint.motorSpeed = 50;         //モータの速度を設定


引数のaxisはスライドさせる軸です。

右方向を正としてx軸にスライドさせる場合はaxis = new b2Vec2(1.0, 0);とします。

また、このジョイントにはモーター機能が備わっており、 モータを有効化し最大力を設定することでモータ機能を使うことができます。



enchant.PhyPulleyJoint(sprite1, sprite2, anchor1, anchor2, ratio)
滑車ジョイント
Parameters:
{enchant.PhySprite} sprite1 Optional
繋げるスプライト1
{enchant.PhySprite} sprite2 Optional
繋げるスプライト2
{b2Vec2} anchor1 Optional
アンカー1の位置
{b2Vec2} anchor2 Optional
アンカー2の位置
{Number} ratio Optional
左右のバランス

滑車オブジェクトはアンカーを2つ設定し、スプライトを吊り下げることができます。


var ball1 = new PhyCircleSprite(8, DYNAMIC_SPRITE);
ball1.position = { x: 80, y: 160 };
var ball2 = new PhyCircleSprite(8, DYNAMIC_SPRITE);
ball2.position = { x: 240, y: 160 };
//滑車ジョイント
var pulleyJoint = new PhyPulleyJoint(ball1, ball2, new b2Vec2(80, 100), new b2Vec2(240, 100), 1);





以上で簡単な説明を終わります。

正直、文字で読むよりプログラムを読んだほうがわかりやすいような気がするので、サンプルプログラムをダウンロードして読んでみてください。(説明書くのがめんどくさいわけじゃないんだからね)

 多角形,ジョイントサンプル - PhySprite.enchant.js

サンプルダウンロード






2012年2月12日日曜日

OpenGLのウィンドウを動画でキャプチャするクラスを作ってみた - GLCapture

OpenGLのウィンドウを動画でキャプチャするクラスを作ってみました。

研究で、OpenGLを使っていますが
プレゼンを作るときにいつもキャプチャソフトを起動、設定して動画を撮るのが面倒でした。

そこで、プログラムの方から動画を書きだすプログラムを書きました。

動画サンプル



こんな感じで録画できます。




ダウンロード



使い方はこんな感じです。

   
GLCapture glCapture;
void init(){
    //glCaptureの設定
    glCapture.setWriteFile("output.avi");
}
void display(){
    glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
    //~描画するプログラム~
  
    glFlush();
    glutSwapBuffers();

    //動画ファイルに書き込み
    glCapture.write();
}
と、こんな感じです。

流れとしては

GLCapture glCapture;
でクラスの宣言行い。

glCapture.setWriteFile();
でインスタンスの設定を行う

その後描画関数内で
glCapture.write();
により、現在の描画された内容を動画に書き出す処理です。  


プログラム全体のサンプルはgithubに上げてます。


注意としては、このクラスは中でOpenCVを使っているので OpenCV2.0以降をインストールしてください。
github    


これでキャプチャソフトいらず\(^o^)/

キャプチャソフトよりこっちのほうがきれいだしね。

2012年2月5日日曜日

OpenNIによるポイントクラウドと骨格追跡 - 3次元描画

OpenNIは骨格追跡を行うことができ、更に各関節の3次元位置を取得することができます。

今回は、OpenGLを使ったポイントクラウドに各関節を描画してみます。


まず、ユーザ検出プログラムの説明を行います。

その前に、OpenNIで骨格追跡を有効にするため「SamplesConfig.xml」に
<Node type="User"/>
を追加します。


では、プログラムの説明を行います。


まず、初期化のところに以下のプログラムを加えます。

   
context.FindExistingNode(XN_NODE_TYPE_USER, userGenerator);
xn::SkeletonCapability skeleton = userGenerator.GetSkeletonCap();

// ユーザー認識のコールバックを登録
// キャリブレーションのコールバックを登録
XnCallbackHandle userCallbacks, calibrationCallbacks;
userGenerator.RegisterUserCallbacks(&::UserDetected, 0, 0, userCallbacks);
skeleton.RegisterCalibrationCallbacks( 0, &::CalibrationEnd, 0, calibrationCallbacks );
skeleton.SetSkeletonProfile(XN_SKEL_PROFILE_ALL);

ここで、UserGeneratorの準備とキャリブレーションのコールバックを登録しました。

UserDetectedはユーザの検出時のコールバック
CalibrationEndはユーザロスト時のコールバックです

// ユーザー検出
void XN_CALLBACK_TYPE UserDetected( xn::UserGenerator& generator, XnUserID nId, void* pCookie )
{
    printf("ユーザID%d 検出  %d人目\n",nId,generator.GetNumberOfUsers());
    generator.GetSkeletonCap().RequestCalibration(nId, TRUE);
}
// キャリブレーションの終了
void XN_CALLBACK_TYPE CalibrationEnd(xn::SkeletonCapability& capability, XnUserID nId, XnBool bSuccess, void* pCookie)
{
    if ( bSuccess ) {
        printf("ユーザID%d キャリブレーション成功\n",nId);
        capability.StartTracking(nId);
    }
    else {
        printf("ユーザID%d キャリブレーション失敗\n",nId);
    }
}


では、実際にユーザの骨格位置の取得を行います。


XnUserID aUsers[15];
XnUInt16 nUsers = 15;
userGenerator.GetUsers(aUsers, nUsers);
for (int i = 0; i < nUsers; ++i){
    if (userGenerator.GetSkeletonCap().IsTracking(aUsers[i])){
        XnSkeletonJointPosition joint;
        //aUsers[i]の頭部位置をjointに格納
        userGenerator.GetSkeletonCap().GetSkeletonJointPosition(aUsers[i], XN_SKEL_HEAD, joint);
    }
}

上記のプログラムは全ユーザの頭部位置をjoingに格納して取得するプログラム例です。

・各関数の説明
userGenerator.GetUsers(aUsers, nUsers);
    aUsersの配列に検出されたユーザのIDを格納
    nUsersに検出したユーザの数を格納

userGenerator.GetSkeletonCap().IsTracking(aUsers[i]);
    aUsers[i]がトラッキングされているかを返す
       
userGenerator.GetSkeletonCap().GetSkeletonJointPosition(aUsers[i], XN_SKEL_HEAD, joint);
    aUsers[i]の頭部位置をjointに格納

    XN_SKEL_HEADは「enum XnSkeletonJoint」で宣言されており、他にも
        XN_SKEL_HEAD    
        XN_SKEL_NECK    
        XN_SKEL_TORSO    
        XN_SKEL_WAIST    
        XN_SKEL_LEFT_COLLAR    
        XN_SKEL_LEFT_SHOULDER    
        XN_SKEL_LEFT_ELBOW    
        XN_SKEL_LEFT_WRIST    
        XN_SKEL_LEFT_HAND    
        XN_SKEL_LEFT_FINGERTIP    
        XN_SKEL_RIGHT_COLLAR    
        XN_SKEL_RIGHT_SHOULDER    
        XN_SKEL_RIGHT_ELBOW    
        XN_SKEL_RIGHT_WRIST    
        XN_SKEL_RIGHT_HAND    
        XN_SKEL_RIGHT_FINGERTIP    
        XN_SKEL_LEFT_HIP    
        XN_SKEL_LEFT_KNEE    
        XN_SKEL_LEFT_ANKLE    
        XN_SKEL_LEFT_FOOT    
        XN_SKEL_RIGHT_HIP    
        XN_SKEL_RIGHT_KNEE    
        XN_SKEL_RIGHT_ANKLE    
        XN_SKEL_RIGHT_FOOT
    と24箇所の関節が取得できる。



では、以上のことを踏まえてOpenGLで骨格を描画してみます。

プログラムは「Kinect OpenNIによる3次元ポイントクラウド - 3次元描画 高速化」のプログラムに
追加していく感じでやってきます。

#include <GL/glut.h>
#include <opencv2/opencv.hpp>
#ifdef _DEBUG
    //Debugモードの場合
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_core220d.lib")           // opencv_core
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_imgproc220d.lib")        // opencv_imgproc
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_highgui220d.lib")        // opencv_highgui
#else
    //Releaseモードの場合
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_core220.lib")            // opencv_core
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_imgproc220.lib")        // opencv_imgproc
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_highgui220.lib")        // opencv_highgui
#endif

#include <XnCppWrapper.h>
#pragma comment(lib,"C:/Program files/OpenNI/Lib/openNI.lib")

#define SAMPLE_XML_PATH "C:/Program Files/OpenNI/Data/SamplesConfig.xml"
using namespace cv;
using namespace xn;

//openNIのための宣言・定義
//マクロ定義
#define KINECT_IMAGE_WIDTH 640
#define KINECT_IMAGE_HEGIHT 480
#define KINECT_DEPTH_WIDTH 640
#define KINECT_DEPTH_HEGIHT 480

DepthGenerator depthGenerator;// depth context
ImageGenerator imageGenerator;//image context
UserGenerator userGenerator;
DepthMetaData depthMD;
ImageMetaData imageMD;
Context context;
//トラッキングの際のコールバック
void XN_CALLBACK_TYPE UserDetected( xn::UserGenerator& generator, XnUserID nId, void* pCookie );
void XN_CALLBACK_TYPE CalibrationEnd(xn::SkeletonCapability& capability, XnUserID nId, XnBool bSuccess, void* pCookie);


Mat image(480,640,CV_8UC3);
Mat depth(480,640,CV_16UC1);  
//ポイントクラウドの座標
Mat pointCloud_XYZ(480,640,CV_32FC3,cv::Scalar::all(0));

void retrievePointCloudMap(Mat &depth,Mat &pointCloud_XYZ); //3次元ポイントクラウドのための座標変換
void drawPointCloud(Mat &rgbImage,Mat &pointCloud_XYZ);  //ポイントクラウド描画
void drawUserJoint();          //ユーザの関節を描画
void DrawLimb(XnUserID player, XnSkeletonJoint eJoint1, XnSkeletonJoint eJoint2);
void DrawJoint(XnUserID player, XnSkeletonJoint eJoint1);

//openGLのための宣言・定義
//---変数宣言---
int FormWidth = 640;
int FormHeight = 480;
int mButton;
float twist, elevation, azimuth;
float cameraDistance = 0,cameraX = 0,cameraY = 0;
int xBegin, yBegin;
//---マクロ定義---
#define glFovy 45        //視角度
#define glZNear 1.0        //near面の距離
#define glZFar 150.0    //far面の距離
void polarview();        //視点変更

//描画
void display(){  
    // clear screen and depth buffer
    glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
    // Reset the coordinate system before modifying
    glLoadIdentity();   
    glEnable(GL_DEPTH_TEST); //「Zバッファ」を有効
    gluLookAt(0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0);   //視点の向き設定
    //wait and error processing
    context.WaitAnyUpdateAll();

    imageGenerator.GetMetaData(imageMD);
    depthGenerator.GetMetaData(depthMD);
    depthGenerator.GetAlternativeViewPointCap().SetViewPoint(imageGenerator);//ズレを補正
         
    memcpy(image.data,imageMD.Data(),image.step * image.rows);    //イメージデータを格納
    memcpy(depth.data,depthMD.Data(),depth.step * depth.rows);    //深度データを格納
      
    //3次元ポイントクラウドのための座標変換
    retrievePointCloudMap(depth,pointCloud_XYZ);

    //視点の変更
    polarview();  
    //ポイントクラウド
    drawPointCloud(image,pointCloud_XYZ);

 //関節の描画
 drawUserJoint();
             
    //convert color space RGB2BGR
    cvtColor(image,image,CV_RGB2BGR);     
     
    imshow("image",image);
    imshow("depth",depth);
  
    glFlush();
    glutSwapBuffers();
}
//初期化
void init(){
    context.InitFromXmlFile(SAMPLE_XML_PATH); 
    context.FindExistingNode(XN_NODE_TYPE_DEPTH, depthGenerator); 
    context.FindExistingNode(XN_NODE_TYPE_IMAGE, imageGenerator);
 context.FindExistingNode(XN_NODE_TYPE_USER, userGenerator);   
    if (!userGenerator.IsCapabilitySupported(XN_CAPABILITY_SKELETON)) {   // スケルトン・トラッキングをサポートしているか確認
  printf("ユーザー検出をサポートしてません\n");
    }

    // キャリブレーションにポーズが必要
    xn::SkeletonCapability skeleton = userGenerator.GetSkeletonCap();
    if ( skeleton.NeedPoseForCalibration() ) {
  printf("このOpenNIのバージョンはポーズ無しには対応していません\n");
    }

    // ユーザー認識のコールバックを登録
    // キャリブレーションのコールバックを登録
    XnCallbackHandle userCallbacks, calibrationCallbacks;
    userGenerator.RegisterUserCallbacks(&::UserDetected, 0, 0, userCallbacks);
    skeleton.RegisterCalibrationCallbacks( 0, &::CalibrationEnd, 0, calibrationCallbacks );

    skeleton.SetSkeletonProfile(XN_SKEL_PROFILE_ALL);
}
// アイドル時のコールバック
void idle(){
    //再描画要求
    glutPostRedisplay();
}
//ウィンドウのサイズ変更
void reshape (int width, int height){
    FormWidth = width;
    FormHeight = height;
    glViewport (0, 0, (GLsizei)width, (GLsizei)height);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    //射影変換行列の指定
    gluPerspective (glFovy, (GLfloat)width / (GLfloat)height,glZNear,glZFar); 
    glMatrixMode (GL_MODELVIEW);
}
//マウスの動き
void motion(int x, int y){
    int xDisp, yDisp;  
    xDisp = x - xBegin;
    yDisp = y - yBegin;
    switch (mButton) {
    case GLUT_LEFT_BUTTON:
        azimuth += (float) xDisp/2.0;
        elevation -= (float) yDisp/2.0;
        break;
    case GLUT_MIDDLE_BUTTON:
        cameraX -= (float) xDisp/40.0;
        cameraY += (float) yDisp/40.0;
        break;
    case GLUT_RIGHT_BUTTON:
  cameraDistance += (float) xDisp/40.0;
        break;
    }
    xBegin = x;
    yBegin = y;
}
//マウスの操作
void mouse(int button, int state, int x, int y){
    if (state == GLUT_DOWN) {
        switch(button) {
        case GLUT_RIGHT_BUTTON:
        case GLUT_MIDDLE_BUTTON:
        case GLUT_LEFT_BUTTON:
            mButton = button;
            break;
        }
        xBegin = x;
        yBegin = y;
    }
}
//視点変更
void polarview(){
    glTranslatef( cameraX, cameraY, cameraDistance);
    glRotatef( -twist, 0.0, 0.0, 1.0);
    glRotatef( -elevation, 1.0, 0.0, 0.0);
    glRotatef( -azimuth, 0.0, 1.0, 0.0);
}
//メイン
int main(int argc, char *argv[]){
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(FormWidth, FormHeight);
    glutCreateWindow(argv[0]);
    //コールバック
    glutReshapeFunc (reshape);
    glutDisplayFunc(display);
    glutIdleFunc(idle);
    glutMouseFunc(mouse);
    glutMotionFunc(motion);
    init();
    glutMainLoop();
    context.Shutdown();
    return 0;
}

//ポイントクラウド描画
void drawPointCloud(Mat &rgbImage,Mat &pointCloud_XYZ){
 static int x,y;
 glPointSize(2);
 glBegin(GL_POINTS);
 uchar *p = rgbImage.data;
 Point3f *point = (Point3f*)pointCloud_XYZ.data;
 for(y = 0;y < KINECT_DEPTH_HEGIHT;y++){
  for(x = 0;x < KINECT_DEPTH_WIDTH;x++,p += 3,point++){ 
   if(point->z == 0)
    continue;
   glColor3ubv(p);
   glVertex3f(point->x,point->y,point->z);
  }
 }
 glEnd();
}
//3次元ポイントクラウドのための座標変換
void retrievePointCloudMap(Mat &depth,Mat &pointCloud_XYZ){
 static const int size = KINECT_DEPTH_HEGIHT * KINECT_DEPTH_WIDTH;
 static XnPoint3D proj[size] = {0};
 static int x,y;
 XnPoint3D *p = proj;
 unsigned short* dp = (unsigned short*)depth.data;
 for(y = 0; y < KINECT_DEPTH_HEGIHT; y++ ){
  for(x = 0; x < KINECT_DEPTH_WIDTH; x++, p++, dp++){ 
   p->X = x;
   p->Y = y;
   p->Z = *dp * 0.001f; // from mm to meters
  }
 }
 //現実座標に変換
 depthGenerator.ConvertProjectiveToRealWorld(size, proj, (XnPoint3D*)pointCloud_XYZ.data);
}   

//人物の骨格を描画
void drawUserJoint(){
 XnUserID aUsers[15];
 XnUInt16 nUsers = 15;
 userGenerator.GetUsers(aUsers, nUsers);
 for (int i = 0; i < nUsers; ++i){
  if (userGenerator.GetSkeletonCap().IsTracking(aUsers[i])){
   //各関節に点を打つ
   glPointSize(5);
   glBegin(GL_POINTS);  
    glColor4f(0,255,0, 1); 
    for(int Joint = XN_SKEL_HEAD;Joint < XN_SKEL_RIGHT_FOOT + 1;Joint++)
     DrawJoint(aUsers[i],(XnSkeletonJoint)Joint);
   glEnd();

   //各肢に線を書く
   glBegin(GL_LINES);
    glColor4f(255,0,0, 1);
    DrawLimb(aUsers[i], XN_SKEL_HEAD, XN_SKEL_NECK);

    DrawLimb(aUsers[i], XN_SKEL_NECK, XN_SKEL_LEFT_SHOULDER);
    DrawLimb(aUsers[i], XN_SKEL_LEFT_SHOULDER, XN_SKEL_LEFT_ELBOW);
    DrawLimb(aUsers[i], XN_SKEL_LEFT_ELBOW, XN_SKEL_LEFT_HAND);

    DrawLimb(aUsers[i], XN_SKEL_NECK, XN_SKEL_RIGHT_SHOULDER);
    DrawLimb(aUsers[i], XN_SKEL_RIGHT_SHOULDER, XN_SKEL_RIGHT_ELBOW);
    DrawLimb(aUsers[i], XN_SKEL_RIGHT_ELBOW, XN_SKEL_RIGHT_HAND);

    DrawLimb(aUsers[i], XN_SKEL_LEFT_SHOULDER, XN_SKEL_TORSO);
    DrawLimb(aUsers[i], XN_SKEL_RIGHT_SHOULDER, XN_SKEL_TORSO);

    DrawLimb(aUsers[i], XN_SKEL_TORSO, XN_SKEL_LEFT_HIP);
    DrawLimb(aUsers[i], XN_SKEL_LEFT_HIP, XN_SKEL_LEFT_KNEE);
    DrawLimb(aUsers[i], XN_SKEL_LEFT_KNEE, XN_SKEL_LEFT_FOOT);

    DrawLimb(aUsers[i], XN_SKEL_TORSO, XN_SKEL_RIGHT_HIP);
    DrawLimb(aUsers[i], XN_SKEL_RIGHT_HIP, XN_SKEL_RIGHT_KNEE);
    DrawLimb(aUsers[i], XN_SKEL_RIGHT_KNEE, XN_SKEL_RIGHT_FOOT);

    DrawLimb(aUsers[i], XN_SKEL_LEFT_HIP, XN_SKEL_RIGHT_HIP);

   glEnd();
  }
 }
}
void DrawJoint(XnUserID player, XnSkeletonJoint eJoint){
 XnSkeletonJointPosition joint;
 userGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, eJoint, joint);

 //信頼性の低い場合描画しない
 if (joint.fConfidence < 0.5)
  return;

 glVertex3f(joint.position.X* 0.001f, joint.position.Y* 0.001f, joint.position.Z* 0.001f);
}
void DrawLimb(XnUserID player, XnSkeletonJoint eJoint1, XnSkeletonJoint eJoint2){
 XnSkeletonJointPosition joint1, joint2;
 userGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, eJoint1, joint1);
 userGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, eJoint2, joint2);

 //信頼性の低い場合描画しない
 if (joint1.fConfidence < 0.5 || joint2.fConfidence < 0.5)
  return;

 glVertex3f(joint1.position.X* 0.001f, joint1.position.Y* 0.001f, joint1.position.Z* 0.001f);
 glVertex3f(joint2.position.X* 0.001f, joint2.position.Y* 0.001f, joint2.position.Z* 0.001f);
} 
// ユーザー検出
void XN_CALLBACK_TYPE UserDetected( xn::UserGenerator& generator, XnUserID nId, void* pCookie )
{
 printf("ユーザID%d 検出  %d人目\n",nId,generator.GetNumberOfUsers());
    generator.GetSkeletonCap().RequestCalibration(nId, TRUE);
}
// キャリブレーションの終了
void XN_CALLBACK_TYPE CalibrationEnd(xn::SkeletonCapability& capability, XnUserID nId, XnBool bSuccess, void* pCookie)
{
    if ( bSuccess ) {
  printf("ユーザID%d キャリブレーション成功\n",nId);
        capability.StartTracking(nId);
    }
    else {
  printf("ユーザID%d キャリブレーション失敗\n",nId);
    }
}



プログラムダウンロード

github