Maverick Project
 Microsoft Visual C++ 2012 Express
 Direct3D 11.0
 Kinect for Windows SDK v1.7
 DirectXTex texture processing library

■Kinect チルトモーター Prev Top Next
関連ページ:Direct3D 自作の共通ライブラリ


今回はチルトモーターを動かします。
スケルトンの肩の位置がだいたい画面の中心位置に表示されるように、Kinectのカメラの上下の向きを自動的に調整します。

UCommon.h 共通で使用するヘッダファイル
UException.h 例外処理クラスのヘッダファイル
UDirect3D11.h Direct3Dクラスのヘッダーファイル
UDirect3D11.cpp Direct3Dクラスのソースファイル
UGraphicsPipeline.h グラフィックパイプラインクラスのヘッダーファイル
UGraphicsPipeline.cpp グラフィックパイプラインクラスのソースファイル
UMesh.h メッシュの抽象クラスのヘッダーファイル
UMesh.cpp メッシュの抽象クラスのソースファイル
UPlane05.h RGBカメラから取得したイメージをレンダリングするためのメッシュクラスのヘッダーファイル
UPlane05.cpp RGBカメラから取得したイメージをレンダリングするためのメッシュクラスのソースファイル
Sample05.hlsl hlslファイル
UKinect05.h Kinectクラスのヘッダーファイル
UKinect05.cpp Kinectクラスのソースファイル
main.cpp main関数のソースファイル

---UPlane05.h---  ↑


#ifndef UPLANE05_H
#define UPLANE05_H

#include "../../Common/Header/UCommon.h"
#include "../../Common/Header/UException.h."
#include "../../Common/Header/UMesh.h"
#include "../../Common/Header/UDirect3D11.h"
#include <NuiApi.h>

class UCPlane05: public UIMesh
{
private:
   // 頂点定義
   typedef struct _VERTEX
   {
      // 頂点座標
      float x, y, z;
      // テクセル
      float tu, tv;
   }VERTEX;

   // 頂点バッファ
   ID3D11Buffer* m_pVertexBuffer;

   // インデックスバッファ
   ID3D11Buffer* m_pIndexBuffer;

   // シェーダーリソースビュー
   ID3D11ShaderResourceView* m_pSRView;

   // サンプラーステート
   ID3D11SamplerState* m_pSamplerState;

public:
   UCPlane05();
   ~UCPlane05();
   void Invalidate();
   void CreateMesh( ID3D11Device* pD3DDevice, UINT Width, UINT Height );
   void NextFrame( ID3D11DeviceContext* pD3DDeviceContext, NUI_LOCKED_RECT* pRect );
   void Render( ID3D11DeviceContext* pD3DDeviceContext );
};

#endif

---UPlane05.cpp---  ↑

#include "UPlane05.h"

UCPlane05::UCPlane05()
{
   m_pVertexBuffer = nullptr;
   m_pIndexBuffer = nullptr;
   m_pSRView = nullptr;
   m_pSamplerState = nullptr;
}

UCPlane05::~UCPlane05()
{
   Invalidate();
}

void UCPlane05::Invalidate()
{
   SAFE_RELEASE( m_pSamplerState );
   SAFE_RELEASE( m_pSRView );
   SAFE_RELEASE( m_pIndexBuffer );
   SAFE_RELEASE( m_pVertexBuffer );
}

void UCPlane05::CreateMesh( ID3D11Device* pD3DDevice, UINT Width, UINT Height )
{
   // 頂点データを格納する
   VERTEX v[] = { 
       1,  1, 0.0f, 1.0f, 0.0f,
      -1,  1, 0.0f, 0.0f, 0.0f,
       1, -1, 0.0f, 1.0f, 1.0f,
      -1, -1, 0.0f, 0.0f, 1.0f,
   };
      
   // 頂点バッファを作成する
   m_pVertexBuffer = CreateVertexBuffer( pD3DDevice, v, sizeof( v ), 0 );

   UINT Index[] = { 0, 1, 2, 3 };

   // インデックスバッファを作成する。
   m_pIndexBuffer = CreateIndexBuffer( pD3DDevice, Index, sizeof( Index ), 0 );

   // 空のデカールマップ用シェーダーリソースビューを作成する
   // GPU (読み取りのみ) と CPU (書き込みのみ) によるアクセスが可能なリソースとして作成する設定
   m_pSRView = CreateSRViewForDecalMap( pD3DDevice, Width, Height, UD3D11_FORMAT, UEACCESS_FLAG::UPDATE_SUBRESOURCE );

   // サンプラーステートを作成する
   m_pSamplerState = CreateSamplerState( pD3DDevice, D3D11_TEXTURE_ADDRESS_CLAMP );
}

void UCPlane05::NextFrame( ID3D11DeviceContext* pD3DDeviceContext, NUI_LOCKED_RECT* pRect )
{
   ID3D11Resource* pResource = nullptr;

   __try
   {
      // シェーダーリソースビューからテクスチャーを取得する
      m_pSRView->GetResource( &pResource );

      // シェーダーリソースビューに色情報を設定する
      pD3DDeviceContext->UpdateSubresource( pResource, 0, nullptr, pRect->pBits, pRect->Pitch, 0 );
   }
   __finally
   {
      SAFE_RELEASE( pResource );
   }
}

void UCPlane05::Render( ID3D11DeviceContext* pD3DDeviceContext )
{
   // 頂点バッファ設定
   UINT stride = sizeof( UCPlane05::VERTEX );
   UINT offset = 0;
   pD3DDeviceContext->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &stride, &offset );

   // インデックスバッファ設定
   pD3DDeviceContext->IASetIndexBuffer( m_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );

   // プリミティブ タイプおよびデータの順序に関する情報を設定
   pD3DDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

   // ピクセルシェーダーのサンプラーステートを設定する
   pD3DDeviceContext->PSSetSamplers( 0, 1, &m_pSamplerState );

   // デカールマップを設定する
   pD3DDeviceContext->PSSetShaderResources( 0, 1, &m_pSRView );

   // 定数バッファを無効にする
   ID3D11Buffer* pNull[1] = { nullptr };
   pD3DDeviceContext->VSSetConstantBuffers( 0, _countof( pNull ), pNull );
   pD3DDeviceContext->PSSetConstantBuffers( 0, _countof( pNull ), pNull );

   // インデックスバッファを使用した描画
    pD3DDeviceContext->DrawIndexed( 4, 0, 0 );
}

---Sample05.hlsl---  ↑

// テクスチャー
Texture2D g_Tex : register( t0 );

// サンプラーステート
SamplerState  g_Sampler : register( s0 );

// 頂点シェーダーの入力パラメータ
struct VS_IN
{
   float3 pos    : POSITION;    // 頂点座標
   float2 texel  : TEXCOORD;    // テクセル
};

// 頂点シェーダーの出力パラメータ
struct VS_OUT_PS_IN
{
   float4 pos    : SV_POSITION;
   float2 texel  : TEXCOORD0;
};

// 頂点シェーダー
VS_OUT_PS_IN VS_Main( VS_IN In )
{
   VS_OUT_PS_IN Out;

   Out.pos = float4( In.pos, 1.0f );
   Out.texel = In.texel;
   return Out;
}

// ピクセルシェーダ
float4 PS_Main( VS_OUT_PS_IN In ) : SV_TARGET
{
   // 色取得
   float4 col =  g_Tex.Sample( g_Sampler, In.texel );
   
   return col.gbra;
}

---UKinect05.h---  ↑

#ifndef KINECT05_H
#define KINECT05_H

#include "../../Common/Header/UCommon.h"
#include <NuiApi.h>
#include "../../Common/Header/UException.h"

// Kinect用ライブラリ
#pragma comment( lib, "Kinect10.lib" )

// timeGetTime()のため
#pragma comment( lib, "winmm.lib" )

class UCKinect05
{
private:
   INuiSensor* m_pKinectSensor;
   HANDLE m_ImageStreamHandle, m_DepthStreamHandle;
   NUI_LOCKED_RECT m_pColorImageRectData;
   NUI_SKELETON_FRAME m_SkeletonFrame;

   DWORD m_NoTrackedTime;

   static const NUI_IMAGE_RESOLUTION m_ImageResolution = NUI_IMAGE_RESOLUTION_640x480;
   static const NUI_IMAGE_RESOLUTION m_DepthResolution = NUI_IMAGE_RESOLUTION_640x480;

   // スケルトン座標をカラーイメージ座標系に変換する
   void TransformSkeletonToScreenCoordinate( Vector4* pSkeletonPosition,
                                             NUI_IMAGE_RESOLUTION imageFrameResolution, NUI_IMAGE_RESOLUTION depthFrameResolution,
                                             LONG* pColorX, LONG* pColorY );

public:
   UCKinect05();
   ~UCKinect05();
   void CreateKinect( BOOL NearModeEnaled );
   void Invalidate();

   // カラーデータを取得する
   void NextFrameImageData( NUI_LOCKED_RECT** ppLockedRect );

   // スケルトンデータを取得する
   void NextFrameSkeletonData( NUI_SKELETON_FRAME** ppSkeletonData );

   // イメージフレームの解像度を取得する
   inline void GetImageResolutionToSize( DWORD& pWidth, DWORD& pHeight )
   {
      ::NuiImageResolutionToSize( m_ImageResolution, pWidth, pHeight );
   }

   // カメラを人物の肩の位置が中心になるように傾きを変える
   void UCKinect05::MoveCameraIntoTarget();
};

#endif

---UKinect05.cpp---  ↑

#include "../../Common/Header/UKinect05.h"

UCKinect05::UCKinect05()
{
   m_pKinectSensor = nullptr;
   m_pColorImageRectData.pBits = nullptr;
}

UCKinect05::~UCKinect05()
{
   Invalidate();
}

void UCKinect05::CreateKinect( BOOL NearModeEnaled )
{
   HRESULT hr = E_FAIL;
   DWORD Flags = NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON;
   int count = 0;

   // マシンに接続されている Kinectセンサー 数を取得する。
   if( FAILED( hr = ::NuiGetSensorCount( &count ) ) )
      throw( UCException( hr, _T("UCKinect05::CreateKinect()でエラーが発生しました。接続されているKinectセンサーの数を取得できません。") ) );

   if( count == 0 )
      throw( UCException( -1, _T("UCKinect05::CreateKinect()でエラーが発生しました。Kinectセンサーが接続されていません。") ) );

   // 指定したインデックスの INuiSensor のインスタンスを作成する
   // ここでは 0 番目のインスタンスのみ作成する
   if( FAILED( hr = ::NuiCreateSensorByIndex( 0, &m_pKinectSensor ) ) )
      throw( UCException( hr, _T("UCKinect05::CreateKinect()でエラーが発生しました。Kinectセンサーが接続されていません。") ) );

   HRESULT status = E_FAIL;

   for( int i=0; i<50; i++ )
   {
      TCHAR msg[256];

      // Kinectセンサー の接続状態を取得する
      status = m_pKinectSensor->NuiStatus();
   
      // 正常
      if( status == S_OK )
         break;
      // Kinectセンサーが接続されているが、初期化中
      // Kinectセンサー接続直後にアプリを起動するとこの状態になるときがある
      else if( status == S_NUI_INITIALIZING )
      {
         _stprintf_s( msg, _T("■□■HRESULT:0x%x [ %s ]\n"), hr, _T(":Kinectセンサーが接続されているが、初期化中\n") );
         OutputDebugString( msg );
      }
      // Kinectセンサーが接続されているが、エラーが発生
      // Kinectセンサー接続直後にアプリを起動するとこの状態になるときがある
      else
      {
         _stprintf_s( msg, _T("■□■HRESULT:0x%x [ %s ]\n"), hr, _T(":Kinectセンサーが接続されているが、エラーが発生\n") );
         OutputDebugString( msg );
      }

      Sleep( 1000 );
   }
   if( status != S_OK )
      throw( UCException( -1, _T("UCKinect05::CreateKinect()でエラーが発生しました。Kinectセンサーが接続されているが、初期化が完了しなかった") ) );

   // Kinectを初期化する。
   if( FAILED( hr = m_pKinectSensor->NuiInitialize( Flags ) ) )
      throw( UCException( -1, _T("UCKinect05::CreateKinect()でエラーが発生しました。Kinectの初期化に失敗した") ) );

   // RGBカメラを使用する場合
   if( Flags & NUI_INITIALIZE_FLAG_USES_COLOR )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_COLOR,                   // カラーデータを取得する
                            m_ImageResolution,                      // イメージストリームの解像度を指定する
                            0, 2, nullptr, 
                            &m_ImageStreamHandle                    // 開始されたイメージストリームのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect05::CreateKinect()でエラーが発生しました。イメージストリームをオープンできなかった") ) );
   }

   // 深度カメラを使用し、プレイヤーIDを使用しない場合
   if( Flags & NUI_INITIALIZE_FLAG_USES_DEPTH )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_DEPTH,                   // 深度データを取得する
                            m_DepthResolution,                      // 深度ストリームの解像度を指定する
                            0, 2, nullptr, 
                            &m_DepthStreamHandle                    // 開始された深度ストリームのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect05::CreateKinect()でエラーが発生しました。深度ストリームをオープンできなかった") ) );
   }

   // 深度カメラを使用し、プレイヤーIDを使用する場合
   else if( Flags & NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX,  // 深度データとプレイヤーIDを取得する
                            m_DepthResolution,                      // 深度ストリームの解像度を指定する
                            0, 2, nullptr, 
                            &m_DepthStreamHandle                    // 開始された深度ストリーム + プレイヤーIDのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect05::CreateKinect()でエラーが発生しました。深度ストリーム + プレイヤーIDをオープンできなかった") ) );
   }

   // Nearモードを有効にする?
   if( NearModeEnaled )
   {
      // Kinect for XBOX は Nearモード は対応していない!
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamSetImageFrameFlags( m_DepthStreamHandle, NUI_IMAGE_STREAM_FLAG_ENABLE_NEAR_MODE ) ) )
         throw( UCException( hr, _T("UCKinect05::CreateKinect()でエラーが発生しました。イメージフレームの設定に失敗した") ) );
   }

   // スケルトントラッキングを有効にする?
   if( Flags & NUI_INITIALIZE_FLAG_USES_SKELETON )
   {
      if( FAILED( hr = m_pKinectSensor->NuiSkeletonTrackingEnable( nullptr, 0 ) ) )
          throw( UCException( hr, _T("UCKinect05::CreateKinect()でエラーが発生しました。スケルトントラッキングを有効にできなかった") ) );
   }

   DWORD Width, Height;
   ::NuiImageResolutionToSize( m_ImageResolution, Width, Height );

   // カラーイメージの色配列の領域確保
   m_pColorImageRectData.pBits = NEW BYTE[ Width * Height * 4 ];

   // カメラ位置を初期位置に向ける
   m_pKinectSensor->NuiCameraElevationSetAngle( 0 );
}

void UCKinect05::Invalidate()
{
   SAFE_DELETE_ARRAY( m_pColorImageRectData.pBits );

   if( m_pKinectSensor != nullptr )
   {
      m_pKinectSensor->NuiShutdown();
     SAFE_RELEASE( m_pKinectSensor );
   }
}

void UCKinect05::NextFrameImageData( NUI_LOCKED_RECT** ppLockedRect )
{
   HRESULT hr = E_FAIL;
   NUI_IMAGE_FRAME ImageFrame;
   NUI_LOCKED_RECT rect;

   // イメージストリームから次のイメージフレームを取得する
   if( FAILED( hr = m_pKinectSensor->NuiImageStreamGetNextFrame( m_ImageStreamHandle, INFINITE, &ImageFrame ) ) )
      throw( UCException( hr, _T("UCKinect05::NextFrameImageData()でエラーが発生しました。次のイメージフレームを取得できなかった") ) );

   // イメージフレームからカラーデータを取得する
   if( FAILED( hr = ImageFrame.pFrameTexture->LockRect( 0, &rect, nullptr, 0 ) ) )
      throw( UCException( hr, _T("UCKinect05::NextFrameImageData()でエラーが発生しました。イメージフレームからイメージデータを取得できなかった") ) );

   // ロックを解除すると参照できなくなるので別変数に格納してからコピーする
   m_pColorImageRectData.Pitch = rect.Pitch;
   m_pColorImageRectData.size = rect.size;
   ::CopyMemory( m_pColorImageRectData.pBits, rect.pBits, sizeof( BYTE ) * rect.size );

   // ロックを解除する
   if( FAILED( hr = ImageFrame.pFrameTexture->UnlockRect( 0 ) ) )
      throw( UCException( hr, _T("UCKinect05::NextFrameImageData()でエラーが発生しました。ロックを解除できなかった") ) );

   // イメージストリームから取得したイメージフレームを解放する
   if( FAILED( hr = m_pKinectSensor->NuiImageStreamReleaseFrame( m_ImageStreamHandle, &ImageFrame ) ) )
      throw( UCException( hr, _T("UCKinect05::NextFrameImageData()でエラーが発生しました。イメージストリームから取得したイメージフレームを解放できなかった") ) );

   // 線形合成した場合でも正しくレンダリングできるようにRGBカメラの結果のアルファ値は常に255にする
   for( INT i=3; i<m_pColorImageRectData.size; i+=4 )
      m_pColorImageRectData.pBits[i] = 255;

   *ppLockedRect = &m_pColorImageRectData;
}

// スケルトンデータを取得する
void UCKinect05::NextFrameSkeletonData( NUI_SKELETON_FRAME** ppSkeletonData )
{
   HRESULT hr = E_FAIL;

   if( FAILED( hr = m_pKinectSensor->NuiSkeletonGetNextFrame( INFINITE, &m_SkeletonFrame ) ) )
      throw( UCException( hr, _T("UCKinect05::NextFrameSkeletonData()でエラーが発生しました。スケルトンの次のフレームのデータを取得できなかった") ) );

   *ppSkeletonData = &m_SkeletonFrame;
}

// スケルトン座標をカラーイメージ座標系に変換する
void UCKinect05::TransformSkeletonToScreenCoordinate( Vector4* pSkeletonPosition,
                                                      NUI_IMAGE_RESOLUTION imageFrameResolution, NUI_IMAGE_RESOLUTION depthFrameResolution,
                                                      LONG* pColorX, LONG* pColorY )
{
   HRESULT hr = E_FAIL;
   FLOAT depthX = 0, depthY = 0;
   *pColorX = *pColorY = 0;

   // スケルトンの座標を深度イメージの座標系に変換する
   ::NuiTransformSkeletonToDepthImage( *pSkeletonPosition, &depthX, &depthY, imageFrameResolution );

   // 深度イメージの座標からカラーイメージの座標を取得する
   if( FAILED( hr = ::NuiImageGetColorPixelCoordinatesFromDepthPixelAtResolution(
                                                               imageFrameResolution, depthFrameResolution,
                                                               nullptr,
                                                               (LONG)depthX, (LONG)depthY,
                                                               0,
                                                               pColorX, pColorY ) ) )
      throw( UCException( hr, _T("TransformSkeletonToColorPixelCoordinate()でエラーが発生しました。カラーイメージの座標を取得できなかった") ) );
}

// カメラを人物の肩の位置が中心になるように傾きを変える
void UCKinect05::MoveCameraIntoTarget()
{
   NUI_SKELETON_FRAME* pSkeletonFrame = nullptr;
   // スケルトンデータを取得する
   NextFrameSkeletonData( &pSkeletonFrame );

   DWORD Width, Height;
   GetImageResolutionToSize( Width, Height );

   LONG ColorX, ColorY, len, lenAbs, angle;

   BOOL TrackedSkeletonFlg = FALSE;

   for( int i=0; i<NUI_SKELETON_COUNT; i++ )
   {
      // スケルトンデータを取得する
      NUI_SKELETON_DATA* skeletonData = &pSkeletonFrame->SkeletonData[i];

      // スケルトンの追跡状態をチェックする
      switch( skeletonData->eTrackingState )
      {
      // スケルトンが追跡されている
      case NUI_SKELETON_TRACKED:
         m_NoTrackedTime = ::timeGetTime();
         TrackedSkeletonFlg = TRUE;

         // カラーイメージ座標系上での肩の位置を取得する
         // 対象とするスケルトンは任意
         TransformSkeletonToScreenCoordinate( &skeletonData->SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_CENTER],
                                              m_ImageResolution, m_DepthResolution,
                                     &ColorX, &ColorY );
         // 肩のY座標と画面の中心座標との差分を計算する
         len = (LONG)Height / 2 - ColorY;
         // Kinectの傾きを変更する角度を計算する
         // 計算方法はぶっちゃけ適当
         lenAbs = abs( len ) / 10;

         // 肩の位置が画面の中心より低いのでKinectを上に向ける
         if( len < -10 )
         {
            // Kinectの傾きを取得する
            m_pKinectSensor->NuiCameraElevationGetAngle( &angle );
               
            angle-=lenAbs;

            if( angle < NUI_CAMERA_ELEVATION_MINIMUM )
               angle = NUI_CAMERA_ELEVATION_MINIMUM;

            // Kinectを傾ける
            m_pKinectSensor->NuiCameraElevationSetAngle( angle );
         }
         // 肩の位置が画面の中心より高いのでKinectを下に向ける
         else if( len > 10 )
         {
            // Kinectの傾きを取得する
            m_pKinectSensor->NuiCameraElevationGetAngle( &angle );

            angle +=lenAbs;

            if( angle > NUI_CAMERA_ELEVATION_MAXIMUM )
               angle = NUI_CAMERA_ELEVATION_MAXIMUM;

            // Kinectを傾ける
            m_pKinectSensor->NuiCameraElevationSetAngle( angle );
         }
         break;

      // プレイヤーの位置情報のみ追跡されている
      case NUI_SKELETON_POSITION_ONLY:
         break;

      // 追跡されていない
      case NUI_SKELETON_NOT_TRACKED:
         break;
      }
   }

   if( TrackedSkeletonFlg == FALSE )
   {
      // すべてのスケルトンが追跡されていない状態が5秒を超えた、またはtimeGetTime()の時間が巻き戻されたか
      if( ::timeGetTime() - m_NoTrackedTime > 5000 || ::timeGetTime() < m_NoTrackedTime )
      {
         // カメラを中心位置に向ける
         m_pKinectSensor->NuiCameraElevationSetAngle( 0 );
         m_NoTrackedTime = ::timeGetTime();
      }
   }
}

::NuiCameraElevationSetAngle()のリファレンスを確認しましたが、Kinectセンサーの摩耗を最小限に抑えるために、可能な限り傾きを変える回数を減らすよう設計するようにとあります。
20秒間内で最大15回まで傾きを変更することが可能で、それ以上は変更をかけてもSDK側で拒否られて変更できません。
そんなわけなので、自動で傾きを変えるのはやめたほうがいいかもしれん。

---main.cpp---  ↑

#include "../../Common/Header/UCommon.h"
#include "../../Common/Header/UException.h"
#include "../../Common/Header/UDirect3D11.h"
#include "../../Common/Header/UGraphicsPipeline.h"
#include "../../Common/Header/UKinect05.h"
#include "UPlane05.h"
#include <NuiApi.h>

// シェーダーオブジェクトを作成するとき、ファイルから読むか、メモリから読むかを切り替える
#if defined(DEBUG) || defined(_DEBUG)
   #define UNCOMPILED_SHADER     // ファイルを読み込んでコンパイルする
#else
   #include "../../Common/HLSL/Sample05_VS_Main.h"
   #include "../../Common/HLSL/Sample05_PS_Main.h"
#endif

// アプリケーション名
TCHAR* AppName = _T("Kinect_Tutrial05");

// Direct3D関連の自作クラス
UCDirect3D11* g_pDirect3D11 = nullptr;

// グラフィックパイプライン関連の自作クラス
UCGraphicsPipeline* g_pGraphicsPipeline = nullptr;

// Kinectセンサーオブジェクト
UCKinect05* g_pKinect = nullptr;

// 平面オブジェクト
UCPlane05* g_pPlane = nullptr;

// メモリ解放
void Invalidate()
{
   SAFE_DELETE( g_pKinect );
   SAFE_DELETE( g_pPlane );
   SAFE_DELETE( g_pGraphicsPipeline );
   SAFE_DELETE( g_pDirect3D11 );
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
   switch( msg )
   {
   case WM_KEYUP:
      // アプリ終了
      if( wParam == VK_ESCAPE )
         ::DestroyWindow( hWnd );
      break;

   case WM_DESTROY:
      ::PostQuitMessage(0);
      break;

   default:
      return ::DefWindowProc( hWnd, msg, wParam, lParam );
   }

   return 0L;
}

// Direct3Dの作成
void CreateDirect3D( HINSTANCE hInstance, DWORD Width, DWORD Height )
{
   DXGI_MODE_DESC* pModeDescArray = nullptr;

   __try
   {
      DXGI_MODE_DESC  ModeDesc;
      UINT num;

      // ディスプレイモード一覧の数を取得する
      g_pDirect3D11->GetDisplayMode( nullptr, &num );

      pModeDescArray = NEW DXGI_MODE_DESC[num];
      // ディスプレイモード一覧を取得する
      g_pDirect3D11->GetDisplayMode( pModeDescArray, &num );

      bool find = false;
      for( UINT i=0; i<num; i++ )
      {
         // 適切な解像度のディスプレイモードを検索する
         if( pModeDescArray[i].Width == Width && pModeDescArray[i].Height == Height )
         {
            find = true;
            ::CopyMemory( &ModeDesc, &pModeDescArray[i], sizeof( DXGI_MODE_DESC ) );
            break;
         }
      }

      if( find == false )
         throw( UCException( -1, _T("InitDirect3D()でエラーが発生しました。適切なディスプレイモードの解像度を取得できません。") ) );

      // ウィンドウの作成およびDirect3D の作成
      g_pDirect3D11->CreateDirect3D11( AppName, hInstance, WndProc, &ModeDesc, TRUE, TRUE );
   }
   __finally
   {
      SAFE_DELETE_ARRAY( pModeDescArray );
   }
}

void CreateGraphicePipeline()
{
   D3D11_INPUT_ELEMENT_DESC layout[] = {
         { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
         { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
   };
   
   // シェーダーを作成する
#if defined(DEBUG) || defined(_DEBUG)
   g_pGraphicsPipeline->CreateVertexShaderFromFile( g_pDirect3D11->m_pD3DDevice, _T("../../Common/HLSL/Sample05.hlsl"), "VS_Main", layout, _countof( layout ) );
   g_pGraphicsPipeline->CreatePixelShaderFromFile(  g_pDirect3D11->m_pD3DDevice, _T("../../Common/HLSL/Sample05.hlsl"), "PS_Main" );
#else
   g_pGraphicsPipeline->CreateVertexShaderFromMemory( g_pDirect3D11->m_pD3DDevice, (LPBYTE)g_VS_Main, sizeof( g_VS_Main ), layout, _countof( layout ) );
   g_pGraphicsPipeline->CreatePixelShaderFromMemory(  g_pDirect3D11->m_pD3DDevice, (LPBYTE)g_PS_Main, sizeof( g_PS_Main ) );
#endif

   // ラスタライザーステートを作成する
   g_pGraphicsPipeline->CreateRasterizerState( g_pDirect3D11->m_pD3DDevice, D3D11_CULL_MODE::D3D11_CULL_BACK );

   // 深度ステンシルステートを作成する
   g_pGraphicsPipeline->CreateDepthStencilState( g_pDirect3D11->m_pD3DDevice, FALSE, D3D11_DEPTH_WRITE_MASK::D3D11_DEPTH_WRITE_MASK_ALL );

   // ブレンドステートを線形合成で作成する
   UCGraphicsPipeline::UEBLEND_STATE BlendStateType[1] = { UCGraphicsPipeline::UEBLEND_STATE::NONE };
   g_pGraphicsPipeline->CreateBlendState( g_pDirect3D11->m_pD3DDevice, BlendStateType, 1 );
}

// メッシュを作成する
void CreateMesh( DWORD Width, DWORD Height )
{
   // RGBカメラのイメージをレンダリングするためのメッシュを作成する
   g_pPlane->CreateMesh( g_pDirect3D11->m_pD3DDevice, Width, Height );
}

void CreateResource( HINSTANCE hInstance )
{
    // Direct3D 関連自作クラスのインスタンスを作成
    // CreateDirect3D()関数内でインスタンスの作成を行うと、C2712 エラーが発生するのでここで行う。
    g_pDirect3D11 = NEW UCDirect3D11();
    g_pGraphicsPipeline = NEW UCGraphicsPipeline();
    g_pPlane = NEW UCPlane05();
    g_pKinect = NEW UCKinect05();

    DWORD Width = 0, Height = 0;

    // Kinectセンサーオブジェクトを作成
    // RGBカメラとプレイヤーIDを使用する
    g_pKinect->CreateKinect( FALSE );

    g_pKinect->GetImageResolutionToSize( Width, Height );

    // Direct3Dの作成
    CreateDirect3D( hInstance, Width, Height );

    // グラフィックパイプラインリソースの作成
    CreateGraphicePipeline();

    // リソースの作成
    CreateMesh( Width, Height );
}

void NextFrame()
{
   NUI_LOCKED_RECT* imageDataFromKinect = nullptr;

   // Kinect側の次のフレームのイメージフレームの作成
   g_pKinect->NextFrameImageData( &imageDataFromKinect );

   // シェーダーリソースビューを作成する
   g_pPlane->NextFrame( g_pDirect3D11->m_pD3DDeviceContext, imageDataFromKinect );

   // カメラを人物の肩の位置が中心になるように傾きを変える
   g_pKinect->MoveCameraIntoTarget();
}

// 描画処理
HRESULT Render()
{
   HRESULT hr = E_FAIL;
   float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };

   // バックバッファをクリアする。
   g_pDirect3D11->ClearBackBuffer( ClearColor ); 

   // 深度バッファをクリアする。
   g_pDirect3D11->ClearDepthStencilView( D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0 );

   // 各種グラフィックパイプラインを設定
   g_pGraphicsPipeline->SetVertexShader( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetPixelShader( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetRasterizerState( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetDepthStencilState( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetBlendState( g_pDirect3D11->m_pD3DDeviceContext );

   // レンダリング
   g_pPlane->Render( g_pDirect3D11->m_pD3DDeviceContext );

   // レンダリングされたイメージをユーザーに表示。
   if( FAILED( hr = g_pDirect3D11->Present( 0, 0 ) ) )
      throw( UCException( hr, _T("IDXGISwapChain::Present()が失敗しました") ) );

   return hr;
}
 
// メイン関数
int APIENTRY _tWinMain( HINSTANCE hInstance,
                        HINSTANCE,
                        LPTSTR,
                        INT )
{
   // メモリリーク検出
   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_EVERY_1024_DF);

   MSG msg = {0};

   try
   {
      CreateResource( hInstance );

      ::ShowWindow( g_pDirect3D11->m_hWnd, SW_SHOW );
      ::UpdateWindow( g_pDirect3D11->m_hWnd );

      // メッセージループ
      do
      { 
         if( ::PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
         {
            ::TranslateMessage(&msg); 
            ::DispatchMessage(&msg); 
         }
         else
         {
            NextFrame();
            Render();
         }
      }while( msg.message != WM_QUIT );
   }
   catch( UCException ex )
   {
      ::MessageBox( nullptr, ex.m_pErrorStr, _T("エラー"), MB_OK );
   }

   Invalidate();
   ::UnregisterClass( AppName, hInstance );

   return msg.wParam;
}

以上です。簡単なネタなので、説明はなしで。

web拍手 by FC2

Prev Top Next

inserted by FC2 system