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の挿抜を検出して適切に処理するようにします。
Kinectが抜かれたらKinectからカラーイメージの取得をやめて、"KINECT NOT CONNECT"という文字を画面に表示します。
Kinectが再度挿入されたらカラーイメージを取得して画面に表示します。

起動時の挿抜チェックもやろうと思ったのですがうまくいかないので、とりあえず現状ではKinectが挿入された状態でなければアプリが起動しないようにしています。

UCommon.h 共通で使用するヘッダファイル
UException.h 例外処理クラスのヘッダファイル
UDirect3D11.h Direct3Dクラスのヘッダーファイル
UDirect3D11.cpp Direct3Dクラスのソースファイル
UGraphicsPipeline.h グラフィックパイプラインクラスのヘッダーファイル
UGraphicsPipeline.cpp グラフィックパイプラインクラスのソースファイル
UMesh.h メッシュの抽象クラスのヘッダーファイル
UMesh.cpp メッシュの抽象クラスのソースファイル
UDDSLoader.h DDSファイルをロードしてシェーダーリソースビューを作成するサンプルクラスのヘッダファイル。
R8G8B8A8またはR8G8B8X8のみサポートし、ミップマップ、キューブマップ、ボリュームマップは未対応。
UDDSLoader.cpp DDSファイルをロードしてシェーダーリソースビューを作成するサンプルクラスのソースファイル。
UDebugFont.h デバッグ用のテキストを画面上に表示するためのクラスのヘッダファイル。
UDebugFont.cpp デバッグ用のテキストを画面上に表示するためのクラスのソースファイル。
Sample05.hlsl hlslファイル。既出。
UPlane06.h RGBカメラから取得したイメージをレンダリングするためのメッシュクラスのヘッダーファイル
UPlane06.cpp RGBカメラから取得したイメージをレンダリングするためのメッシュクラスのソースファイル
UKinect06.h Kinectクラスのヘッダーファイル
UKinect06.cpp Kinectクラスのソースファイル
main.cpp main関数のソースファイル

---UPlane06.h---  ↑


#ifndef UPLANE06_H
#define UPLANE06_H

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

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

class UCPlane06: 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;
   
   // デバックフォントクラス
   UCDebugFont m_DebugFont;

   // デバックフォントの座標更新するタイミングを計る時に使用する時間を格納する変数
   DWORD m_DebugFontRenderWaitTimer;
   // デバックフォントを表示する座標
   XMFLOAT2 m_DebugFontPosition;

   // Kinectが使用可能か
   BOOL m_KinectConnecting;
   
public:
   UCPlane06();
   ~UCPlane06();
   void Invalidate();
   void CreateMesh( ID3D11Device* pD3DDevice, UINT Width, UINT Height );
   void NextFrame( ID3D11DeviceContext* pD3DDeviceContext, NUI_LOCKED_RECT* pRect );
   void Render( ID3D11DeviceContext* pD3DDeviceContext );
};

#endif

---UPlane06.cpp---  ↑

#include "UPlane06.h"

UCPlane06::UCPlane06()
{
   m_pVertexBuffer = nullptr;
   m_pIndexBuffer = nullptr;
   m_pSRView = nullptr;
   m_pSamplerState = nullptr;
   m_DebugFontRenderWaitTimer = 0;
   m_KinectConnecting = FALSE;
}

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

void UCPlane06::Invalidate()
{
   m_KinectConnecting = FALSE;
   m_DebugFontRenderWaitTimer = 0;
   SAFE_RELEASE( m_pSamplerState );
   SAFE_RELEASE( m_pSRView );
   SAFE_RELEASE( m_pIndexBuffer );
   SAFE_RELEASE( m_pVertexBuffer );
}

void UCPlane06::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 );

   m_DebugFont.CreateMesh( pD3DDevice, 0.04f, 0.14f );
}

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

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

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

      m_KinectConnecting = TRUE;
   }

   else
   {
      if( m_DebugFontRenderWaitTimer == 0 || ::timeGetTime() - m_DebugFontRenderWaitTimer > 2000 || m_DebugFontRenderWaitTimer > ::timeGetTime() )
      {
         m_DebugFontPosition = XMFLOAT2( ( (float)( rand() % 100 ) - 50.0f ) / 100.0f - 0.3f, ( (float)( rand() % 100 ) - 50.0f ) / 60.0f );
         m_DebugFontRenderWaitTimer = ::timeGetTime();
      }

      XMFLOAT4 color = XMFLOAT4( 1, 1, 1, 1 );

      m_DebugFont.NextFrame( _T("KINECT NOT CONNECT"), &m_DebugFontPosition, &color );

      m_KinectConnecting = FALSE;
   }
}

void UCPlane06::Render( ID3D11DeviceContext* pD3DDeviceContext )
{
   if( m_KinectConnecting )
   {
      // 頂点バッファ設定
      UINT stride = sizeof( UCPlane06::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 );
   }

   else
      m_DebugFont.Render( pD3DDeviceContext );
}

Kinectが挿入中のときはカラーイメージをレンダリングし、Kinectが抜かれているときは UCDebugFont クラスを使用しレンダリングします。

---UKinect06.h---  ↑

#ifndef KINECT06_H
#define KINECT06_H

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

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

class UCKinect06
{
private:
   INuiSensor* m_pKinectSensor;
   HANDLE m_ImageStreamHandle, m_DepthStreamHandle, m_streamEventHandle[3];
   NUI_LOCKED_RECT m_pColorImageRectData;
   NUI_SKELETON_FRAME m_SkeletonFrame;

   // Nearモードを使用するか
   BOOL m_NearModeEnaled;

   // Kinectが使用可能か
   BOOL m_ReadyKinect;

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

   // INuiSensorインスタンスのステータスが変更された場合、コールバックされるメンバ関数
   static void CALLBACK StatusChanged( HRESULT hrStatus, const OLECHAR* instanceName, const OLECHAR* uniqueDeviceName, void* pUserData );
   void CALLBACK StatusChanged( HRESULT hrStatus, const OLECHAR* instanceName, const OLECHAR* uniqueDeviceName );

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

   // カラーデータを取得する
   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 CameraElevationSetAngle( LONG Angle );

   // フレーム更新待ち
   void WaitForSingleObject();

   // Kinectが使用可能か
   inline BOOL IsReadyKinect(){ return m_ReadyKinect; };
};

#endif

---UKinect06.cpp---  ↑

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

UCKinect06::UCKinect06()
{
   m_pKinectSensor = nullptr;
   m_pColorImageRectData.pBits = nullptr;
   m_ImageStreamHandle = nullptr;
   m_DepthStreamHandle = nullptr;
   for( int i=0; i<_countof( m_streamEventHandle ); i++ )
      m_streamEventHandle[i] = nullptr;
   m_ReadyKinect = FALSE;
}

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

void UCKinect06::Invalidate()
{
   m_ReadyKinect = FALSE;

   SAFE_DELETE_ARRAY( m_pColorImageRectData.pBits );

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

   if( m_streamEventHandle != nullptr )
   {
      for( int i=0; i<_countof( m_streamEventHandle ); i++ )
      {
         // Setしないとフリーズする
         ::SetEvent( m_streamEventHandle[i] );
         ::CloseHandle( m_streamEventHandle[i] );
         m_streamEventHandle[i] = nullptr;
      }
   }
}

void UCKinect06::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;

   m_NearModeEnaled = NearModeEnaled;

   int count = 0;

   if( m_pKinectSensor == nullptr )
   {
      // INuiSensorインスタンスのステータスが変更された場合、コールバックされる関数を設定する
      ::NuiSetDeviceStatusCallback( StatusChanged, this );

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

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

      // 指定したインデックスの INuiSensor のインスタンスを作成する
      // ここでは 0 番目のインスタンスのみ作成する
      if( FAILED( hr = ::NuiCreateSensorByIndex( 0, &m_pKinectSensor ) ) )
         throw( UCException( hr, _T("UCKinect06::UCKinect06()でエラーが発生しました。INuiSensorの作成に失敗しました") ) );

      // Kinectセンサー の接続状態を取得する
      if( ( hr = m_pKinectSensor->NuiStatus() ) != S_OK )
         throw( UCException( hr, _T("UCKinect06::UCKinect06()でエラーが発生しました。使用可能な状態でありません。") ) );
   }

   for( int i=0; i<_countof( m_streamEventHandle ); i++ )
      m_streamEventHandle[i] = ::CreateEvent( 0, TRUE, FALSE, 0 );

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

   // RGBカメラを使用する場合
   if( Flags & NUI_INITIALIZE_FLAG_USES_COLOR )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_COLOR,                   // カラーデータを取得する
                            m_ImageResolution,                      // イメージストリームの解像度を指定する
                            0, 2, m_streamEventHandle[0], 
                            &m_ImageStreamHandle                    // 開始されたイメージストリームのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect06::CreateKinect()でエラーが発生しました。イメージストリームをオープンできなかった") ) );
   }
   
   // 深度カメラを使用し、プレイヤーIDを使用しない場合
   if( Flags & NUI_INITIALIZE_FLAG_USES_DEPTH )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_DEPTH,                   // 深度データを取得する
                            m_DepthResolution,                      // 深度ストリームの解像度を指定する
                            0, 2, m_streamEventHandle[1], 
                            &m_DepthStreamHandle                    // 開始された深度ストリームのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect06::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, m_streamEventHandle[1], 
                            &m_DepthStreamHandle                    // 開始された深度ストリーム + プレイヤーIDのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect06::CreateKinect()でエラーが発生しました。深度ストリーム + プレイヤーIDをオープンできなかった") ) );
   }
   
   // Nearモードを有効にする?
   if( m_NearModeEnaled )
   {
      // Kinect for XBOX は Nearモード は対応していない!
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamSetImageFrameFlags( m_DepthStreamHandle, NUI_IMAGE_STREAM_FLAG_ENABLE_NEAR_MODE ) ) )
         throw( UCException( hr, _T("UCKinect06::CreateKinect()でエラーが発生しました。イメージフレームの設定に失敗した") ) );
   }

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

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

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

   m_ReadyKinect = TRUE;
}

void UCKinect06::NextFrameImageData( NUI_LOCKED_RECT** ppLockedRect )
{
   if( IsReadyKinect() == FALSE )
      return;

   HRESULT hr = E_FAIL;
   NUI_IMAGE_FRAME ImageFrame;
   NUI_LOCKED_RECT rect;
   DWORD dwMillisecondsToWait = 0;

   // フレームの更新待ちをイベント処理にしていない場合はNuiImageStreamGetNextFrame()で待つ
   if( m_streamEventHandle == nullptr )
      dwMillisecondsToWait = INFINITE;

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

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

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

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

   *ppLockedRect = &m_pColorImageRectData;
}

// スケルトンデータを取得する
void UCKinect06::NextFrameSkeletonData( NUI_SKELETON_FRAME** ppSkeletonData )
{
   if( IsReadyKinect() == FALSE )
      return;

   HRESULT hr = E_FAIL;
   DWORD dwMillisecondsToWait = 0;

   // フレームの更新待ちをイベント処理にしていない場合はNuiSkeletonGetNextFrame()で待つ
   if( m_streamEventHandle == nullptr )
      dwMillisecondsToWait = INFINITE;

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

   *ppSkeletonData = &m_SkeletonFrame;
}

// カメラを傾ける
void UCKinect06::CameraElevationSetAngle( LONG Angle )
{
   if( IsReadyKinect() == FALSE )
      return;

   LONG angle;
   m_pKinectSensor->NuiCameraElevationGetAngle( &angle );

   angle += Angle;

   if( angle < NUI_CAMERA_ELEVATION_MINIMUM )
      angle = NUI_CAMERA_ELEVATION_MINIMUM;
   else if( angle > NUI_CAMERA_ELEVATION_MAXIMUM )
      angle = NUI_CAMERA_ELEVATION_MAXIMUM;

   m_pKinectSensor->NuiCameraElevationSetAngle( angle );
}

// フレーム更新待ち
void UCKinect06::WaitForSingleObject()
{
   if( m_streamEventHandle != nullptr )
   {
      DWORD ret = ::WaitForMultipleObjects( _countof( m_streamEventHandle ), m_streamEventHandle, TRUE, INFINITE );

      // INFINITE 以外の値に変更した場合、タイムアウトを検出できるようにするためにチェックしておく
      if( ret == WAIT_TIMEOUT )
         throw( UCException( -1, _T("WaitForSingleObject()でエラーが発生しました。タイムアウトした。") ) );

      // 指定されたイベントオブジェクトを非シグナル状態に設定します。
      ::ResetEvent( m_streamEventHandle );
   }
}

// INuiSensorインスタンスのステータスが変更された場合、コールバックされるメンバ関数
void UCKinect06::StatusChanged( HRESULT hrStatus, const OLECHAR* instanceName, const OLECHAR* uniqueDeviceName, void* pUserData )
{
   ((UCKinect06*)pUserData)->StatusChanged( hrStatus, instanceName, uniqueDeviceName );
}

void UCKinect06::StatusChanged( HRESULT hrStatus, const OLECHAR* instanceName, const OLECHAR* )
{
   HRESULT hr = E_FAIL;

   if( hrStatus == S_OK )
   {
      if( FAILED( hr = ::NuiCreateSensorById( instanceName, &m_pKinectSensor ) ) )
         throw( UCException( -1, _T("UCKinect06::StatusChanged()でエラーが発生しました。INuiSensorのインスタンスを作成できなかった") ) );

      CreateKinect(m_NearModeEnaled);
   }
   else
   {
      Invalidate();
   }
}

チルトモーターが動いているときにKinectを抜くとフリーズします。どう対処したらいいのかわからないので、Kinectを傾ける処理をキーボード入力による手動処理に変更しました。
これどうにかできるものかのかなあ。

次にKinectの挿抜状態はイベント処理で検出します。コールバック関数はStatusChanged()で、これを::NuiSetDeviceStatusCallback()で登録します。
StatusChanged()内ではKinectの挿抜状態により、Kinectオブジェクトの初期化と解放をそれぞれ行います。
初期化部分は注意が必要なので簡単に説明します。 NuiCreateSensorById() 関数内で INuiSensor インターフェースの作成を行うので、
CreateKinect()関数内では INuiSensor インターフェースが作成済みであるかをチェックするように分岐するように修正しました。

最後にフレームの更新待ちの方法を変更しました。これまではNuiImageStreamGetNextFrame()やNuiSkeletonGetNextFrame()で更新待ちしていましたが、
イメージフレーム、深度フレーム、スケルトンフレームの3回更新待ちするため効率がよくありませんでした。そのため待ち処理を一か所にまとめます。
イベントの登録はNuiImageStreamOpen()とNuiSkeletonTrackingEnable()で行います。更新待ちはWaitForSingleObject()で行います。
NuiImageStreamGetNextFrame()とNuiSkeletonGetNextFrame()で設定する待ち時間は0に変更します。

---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/UKinect06.h"
#include "UPlane06.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_Tutrial06");

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

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

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

// 平面オブジェクト
UCPlane06* 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_KEYDOWN:
      switch( wParam )
      {
      case VK_UP:
         if( g_pKinect && g_pKinect->IsReadyKinect() )
            g_pKinect->CameraElevationSetAngle( 5 );
         break;
      case VK_DOWN:
         if( g_pKinect && g_pKinect->IsReadyKinect() )
            g_pKinect->CameraElevationSetAngle( -5 );
         break;
      }
      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 UCPlane06();
    g_pKinect = NEW UCKinect06();

    DWORD Width = 0, Height = 0;

    // Kinectセンサーオブジェクトを作成
    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->WaitForSingleObject();

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

   // Kinect側の次のフレームのイメージフレームの作成
   g_pPlane->NextFrame( g_pDirect3D11->m_pD3DDeviceContext, imageDataFromKinect );
}

// 描画処理
HRESULT Render()
{
   HRESULT hr = E_FAIL;
   float ClearColor[4] = { 0.3f, 0.3f, 1.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;
}

キーボード入力によりKinectを傾けるように修正したため、ウィンドウプロシージャを変更しました。

さて、次回はやりたかった音声入力だあ。


web拍手 by FC2

Prev Top Next

inserted by FC2 system