DirectX Graphicsコンポーネントを作る
DirectX TKのグラフィックスを使いやすくするためにDirectX Graphicsコンポーネントを作成し、フレームワークをGithubに公開します。
ここでDirectX Graphicsクラス宣言の重要な点をまとめます。
DirectX TKのクラス構成はそのまま使用する
当然のことですが、DirectX TKは「Windows Update」が行われるとDirectX TKも更新されます。DirectX TKが提供されるクラス構成を修正すると、メンテナンスが面倒になります。従って、DirectX TKのクラス構成はそのまま使用するようにします。
Graphicsコンポーネントを追加する
DirectX Graphicsのクラスをシングルトンにする場合も、Graphicsコンポーネントのクラスを新規に追加して、クラス内部でDirectX TKで提供されるクラスをラップして使用します。
using namespaceは使用しない
基本的に using namespace は使用しません。 using namespace は「名前空間の競合が発生する」「クラスライブラリで提供されたクラスと自分が実装したクラスの区別が曖昧になる」など、大規模なゲーム開発の場面では様々な問題を引き起こす可能性があります。ヘッダーファイルに宣言された関数の戻り値の型や引数は、名前空間を指定した型の宣言を行います。唯一、メンバー関数内部で名前空間を指定した変数宣言により、プログラムの可読性が損なわれる場合に限り、using namespaceを使用する場合はあります。
外部に公開する関数はクラスの前方に宣言する
クラスのメンバー関数を外部に公開する場合には、「公開する関数(アクセサを含む)をクラスの前方で宣言すると公開する関数を認識し易く」なります。非公開のメンバー関数やメンバー変数は外部に公開することはないため、クラスの後方で宣言するようにします。
プリコンパイルドヘッダーを有効にする
大規模なプロジェクトをビルドする場合にはプリコンパイル済みヘッダーを有効にします。
プリコンパイル済みヘッダーは「ヘッダーまたはヘッダーに含まれるファイルが変更された場合にコンパイルされ、ソースコードのみ変更された場合はビルド時にプリコンパイル済みヘッダーのコンパイルはスキップ」されます。
プリコンパイル済みヘッダーの指定はプロジェクトのプロパティを開き、「構成プロパティ」「C/C++」「プリコンパイル済みヘッダー」を有効にします。
「pch.h」ファイルにGraphicsクラスのインクルードをおこない、「Graphics.cpp」ファイルの先頭で「pch.h」ファイルをインクルードします。
#pragma once
#ifndef GRAPHICS_DEFINED
#define GRAPHICS_DEFINED
#include "DeviceResources.h"
#include "Animation.h"
class Graphics final
{
public:
// Graphicsクラスのインスタンスを取得する
static Graphics* const GetInstance();
// スクリーンサイズを取得する
void GetScreenSize(int& width, int& height);
// スクリーンサイズを設定する
void SetScreenSize(const int& width, const int& height);
// デバイスリソースを取得する
DX::DeviceResources* GetDeviceResources() const { return m_deviceResources; }
// PrimitiveBatchクラスのインスタンスを取得する
DirectX::PrimitiveBatch<DirectX::VertexPositionColor>* GetPrimitiveBatch() const { return m_primitiveBatch.get(); }
// InputLayoutクラスのインスタンスを取得する
ID3D11InputLayout* GetInputLayout() const { return m_inputLayout.Get(); }
// BasicEffectクラスのインスタンス取得する
DirectX::BasicEffect* GetBasicEffect() { return m_basicEffect.get(); }
// CommonStatesクラスのインスタンスを取得する
DirectX::CommonStates* GetCommonStates() const { return m_commonStates.get(); }
// SpriteBatchクラスのインスタンスを取得する
DirectX::SpriteBatch* GetSpriteBatch() { return m_spriteBatch.get(); }
// SpriteFontクラスのインスタンスを取得する
DirectX::SpriteFont* GetFont() { return m_spriteFont.get(); }
// EffectFactoryクラスのインスタンスを取得する
DirectX::EffectFactory* GetFX() const { return m_effectFactory.get(); }
// ビュー行列を設定する
void SetViewMatrix(const DirectX::SimpleMath::Matrix& view) { m_view = view; }
// ビュー行列を取得する
const DirectX::SimpleMath::Matrix& GetViewMatrix() { return m_view; };
// 射影行列を設定する
void SetProjectionMatrix(const DirectX::SimpleMath::Matrix& projection) { m_projection = projection; }
// 射影行列を取得する
const DirectX::SimpleMath::Matrix& GetProjectionMatrix() { return m_projection; };
public:
// 初期化する
void Initialize(DX::DeviceResources* deviceResources, const int& width, const int& height);
// 文字列を描画する
void DrawString(const float& x, const float& y, const wchar_t* str);
// プリミティブ描画を開始する
void DrawPrimitiveBegin(const DirectX::SimpleMath::Matrix& view, const DirectX::SimpleMath::Matrix& projection);
// プリミティブ描画を終了する
void DrawPrimitiveEnd();
// モデルを描画する
void DrawModel(const DirectX::Model* model, const DirectX::SimpleMath::Matrix& world, const bool& depthBuffer = true);
// アニメーションモデルを描画する
void DrawModel(
const DirectX::Model* model,
const DX::AnimationSDKMESH* animationSDKMESH,
const DirectX::ModelBone::TransformArray* transformArray,
const DirectX::SimpleMath::Matrix& world
);
private:
// コンストラクタ
Graphics();
// 代入は許容しない
void operator=(const Graphics& object) = delete;
// コピーコンストラクタは許容しない
Graphics(const Graphics& object) = delete;
// デストラクタ
~Graphics();
private:
// Graphicsクラスのインスタンスへのポインタ
static std::unique_ptr<Graphics> m_directXGraphics;
// デバイスリソースへのポインタ
DX::DeviceResources* m_deviceResources;
// コモンステート
std::unique_ptr<DirectX::CommonStates> m_commonStates;
// スプライトバッチ
std::unique_ptr<DirectX::SpriteBatch> m_spriteBatch;
// スプライトフォント
std::unique_ptr<DirectX::SpriteFont> m_spriteFont;
// ベーシックエフェクト
std::unique_ptr<DirectX::BasicEffect> m_basicEffect;
// プリミティブバッチ
std::unique_ptr<DirectX::PrimitiveBatch<DirectX::VertexPositionColor>> m_primitiveBatch;
// エフェクトファクトリー
std::unique_ptr<DirectX::EffectFactory> m_effectFactory;
// ラスタライザーステート
Microsoft::WRL::ComPtr<ID3D11RasterizerState> m_rasterrizerState;
// 入力レイアウト
Microsoft::WRL::ComPtr<ID3D11InputLayout> m_inputLayout;
// スクリーンサイズ
int m_screenW;
int m_screenH;
// ビュー行列
DirectX::SimpleMath::Matrix m_view;
// 射影行列
DirectX::SimpleMath::Matrix m_projection;
// デバイス
ID3D11Device* m_device;
// デバイスコンテキスト
ID3D11DeviceContext* m_context;
};
#endif // GRAPHICS_DEFINED
Graphicsクラスの実装は次のようになります。
#include "pch.h"
#include "Graphics.h"
std::unique_ptr<Graphics> Graphics::m_graphics = nullptr;
// DirectX Graphicsクラスのインスタンスを取得する
Graphics* const Graphics::GetInstance()
{
if (m_graphics == nullptr)
{
// DirectX Graphicsクラスのインスタンスを生成する
m_graphics.reset(new Graphics());
}
// DirectX Graphicsクラスのインスタンスを返す
return m_graphics.get();
}
// コンストラクタ
Graphics::Graphics()
:
m_deviceResources(nullptr), // デバイスリソース
m_commonStates(nullptr), // コモンステート
m_spriteBatch(nullptr), // スプライトバッチ
m_spriteFont(nullptr), // スプライトフォント
m_basicEffect(nullptr), // ベーシックエフェクト
m_primitiveBatch(nullptr), // プリミティブバッチ
m_rasterrizerState(nullptr), // ラスタライザーステート
m_effectFactory(nullptr), // エフェクトファクトリ
m_inputLayout(nullptr), // 入力レイアウト
m_screenW(0), // スクリーン幅
m_screenH(0), // スクリーン高
m_view{}, // ビュー行列
m_projection{}, // 射影行列
m_device(nullptr), // デバイス
m_context(nullptr) // デバイスコンテキスト
{
}
// デストラクタ
Graphics::~Graphics()
{
}
// 初期化する
void Graphics::Initialize(
DX::DeviceResources* deviceResources,
const int& width,
const int& height
)
{
// スクリーンサイズを設定する
SetScreenSize(width, height);
// デバイスリソースを設定する
m_deviceResources = deviceResources;
// デバイスを取得する
m_device = m_deviceResources->GetD3DDevice();
// デバイスコンテキストを取得する
m_context = m_deviceResources->GetD3DDeviceContext();
// コモンステートを生成する
m_commonStates = std::make_unique<DirectX::CommonStates>(m_device);
// スプライトバッチを生成する
m_spriteBatch = std::make_unique<DirectX::SpriteBatch>(m_context);
// ベーシックエフェクトを生成する
m_basicEffect = std::make_unique<DirectX::BasicEffect>(m_device);
// スプライトフォントを生成する
m_spriteFont = std::make_unique<DirectX::SpriteFont>(m_device, L"resources\\font\\SegoeUI_18.spritefont");
// プリミティブバッチを生成する
m_primitiveBatch = std::make_unique<DirectX::PrimitiveBatch<DirectX::VertexPositionColor>>(m_context);
// 入力レイアウトを生成する
m_basicEffect->SetVertexColorEnabled(true);
// テクスチャを無効にする
m_basicEffect->SetTextureEnabled(false);
void const* shaderByteCode;
size_t byteCodeLength;
m_basicEffect->GetVertexShaderBytecode(&shaderByteCode, &byteCodeLength);
// 入力レイアウトを生成する
m_device->CreateInputLayout(
DirectX::VertexPositionColor::InputElements,
DirectX::VertexPositionColor::InputElementCount,
shaderByteCode, byteCodeLength,
m_inputLayout.ReleaseAndGetAddressOf()
);
CD3D11_RASTERIZER_DESC rasterizerStateDesc(
D3D11_FILL_SOLID, D3D11_CULL_NONE, FALSE,
D3D11_DEFAULT_DEPTH_BIAS, D3D11_DEFAULT_DEPTH_BIAS_CLAMP,
D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, TRUE, FALSE, FALSE, TRUE);
// ラスタライズステートを生成する
m_device->CreateRasterizerState(&rasterizerStateDesc, m_rasterrizerState.ReleaseAndGetAddressOf());
// エフェクトファクトリを生成する
m_effectFactory = std::make_unique<DirectX::EffectFactory>(m_device);
// リソースディレクトリを設定する
// m_fx->SetDirectory(L"resources\\cmo");
}
// 文字列を描画する
void Graphics::DrawString(const float& x, const float& y, const wchar_t* str)
{
// 文字列を描画する
m_spriteFont->DrawString(m_spriteBatch.get(), str, DirectX::SimpleMath::Vector2(x, y));
}
// プリミティブ描画を開始する
void Graphics::DrawPrimitiveBegin(
const DirectX::SimpleMath::Matrix& view,
const DirectX::SimpleMath::Matrix& projection
)
{
m_context->OMSetBlendState(m_commonStates->Opaque(), nullptr, 0xFFFFFFFF);
m_context->OMSetDepthStencilState(m_commonStates->DepthNone(), 0);
m_context->RSSetState(m_commonStates->CullNone());
//m_context->RSSetState(m_rasterrizeState.Get());
// ビュー行列を設定する
m_basicEffect->SetView(view);
// プロジェクション行列を設定する
m_basicEffect->SetProjection(projection);
// ワールド行列を設定する
m_basicEffect->SetWorld(DirectX::SimpleMath::Matrix::Identity);
// 頂点カラーを有効にする
m_basicEffect->SetVertexColorEnabled(true);
// テクスチャを有効にする
m_basicEffect->SetTextureEnabled(false);
// 入力レイアウトを設定する
m_basicEffect->Apply(m_context);
// 入力レイアウトを設定する
m_context->IASetInputLayout(m_inputLayout.Get());
// プリミティブ描画を開始する
m_primitiveBatch->Begin();
}
// プリミティブ描画を終了する
void Graphics::DrawPrimitiveEnd()
{
// プリミティブ描画を終了する
m_primitiveBatch->End();
}
// 線分を描画する(XZ平面)
void Graphics::DrawLine(
const DirectX::SimpleMath::Vector2& position,
const DirectX::SimpleMath::Vector2& vector,
const DirectX::FXMVECTOR& color
)
{
// 頂点カラーを設定する
DirectX::VertexPositionColor vertex[2] =
{
{ DirectX::SimpleMath::Vector3(position.x, 0.0f, position.y), color },
{ DirectX::SimpleMath::Vector3(position.x + vector.x, 0.0f, position.y + vector.y), color },
};
// 線分を描画する
m_primitiveBatch->DrawLine(vertex[0], vertex[1]);
}
// ベクトルを描画する(XZ平面)
void Graphics::DrawVector(
const DirectX::SimpleMath::Vector2& position,
const DirectX::SimpleMath::Vector2& vector,
const DirectX::FXMVECTOR& color
)
{
using namespace DirectX::SimpleMath;
const float cosTheta = cosf(DirectX::XMConvertToRadians(20.0f));
const float sinTheta = sinf(DirectX::XMConvertToRadians(20.0f));
// 矢印のベクトルのサイズを設定する
Vector2 arrow = -vector;
// 正規化する
arrow.Normalize();
// 矢印のサイズを設定する
arrow *= 3.0f;
// 右矢 X: (xcosθ- ysinθ) Y: (xsinθ+ ycosθ)
Vector2 arrowR = Vector2(arrow.x * cosTheta - arrow.y * sinTheta, arrow.x * sinTheta + arrow.y * cosTheta);
// 左矢 X: (xcosθ- ysinθ) Y: (xsinθ+ ycosθ)
Vector2 arrowL = Vector2(arrow.x * cosTheta + arrow.y * sinTheta, -arrow.x * sinTheta + arrow.y * cosTheta);
// 線分を描画する
DrawLine(position, vector, color);
// 右矢を描画する
DrawLine(position + vector, arrowR, color);
// 左矢を描画する
DrawLine(position + vector, arrowL, color);
}
// 円を描画する(XZ平面)
void Graphics::DrawCircle(
const DirectX::SimpleMath::Vector2& center,
const float& radius,
const DirectX::FXMVECTOR& color,
const int& split
)
{
using namespace DirectX::SimpleMath;
// 角度を初期化する
float angle = 0.0f;
// 始点を宣言する
Vector2 position1 = center + Vector2(cosf(angle), sinf(angle)) * radius;
for (int index = 0; index < split; index++)
{
// 始点を設定する
Vector2 position0 = position1;
// 角度を計算する
angle += DirectX::XM_2PI / (float)split;
// 次点を計算する
position1 = center + Vector2(cosf(angle), sinf(angle)) * radius;
// 円を描画する
DrawLine(position0, position1 - position0, color);
}
}
// モデルを描画する
void Graphics::DrawModel(
const DirectX::Model* model,
const DirectX::SimpleMath::Matrix& world,
const bool& depthBuffer
)
{
if (depthBuffer)
{
// モデルを描画する
model->Draw(m_context, *m_commonStates.get(), world, m_view, m_projection);
}
else
{
// モデルを描画する
model->Draw(
m_context,
*m_commonStates.get(),
world,
m_view,
m_projection,
false,
[&]() { m_context->OMSetDepthStencilState(m_commonStates->DepthNone(), 0); }
);
}
}
// アニメーションモデルを描画する
void Graphics::DrawModel(
const DirectX::Model* model,
const DX::AnimationSDKMESH* animationSDKMESH,
const DirectX::ModelBone::TransformArray* transformArray,
const DirectX::SimpleMath::Matrix& world
)
{
// ボーン配列のサイズを取得する
size_t bones = model->bones.size();
// アニメーションにモデル、ボーン数、ボーンを適用する
animationSDKMESH->Apply(*model, bones, transformArray->get());
// コモンステートを取得する
DirectX::CommonStates* commonState = Graphics::GetInstance()->GetCommonStates();
// アニメーションを描画する
model->DrawSkinned(m_context, *commonState, bones, transformArray->get(), world, m_view, m_projection);
}
作成したプロジェクトはGithubにアップロードしています。
ShozoTanaka/Direct3D-Framework (github.com)