05.計算ライブラリ
この項では、
BaseCross64に内蔵されている
計算ライブラリについて説明します。
計算ライブラリといいましても、ライブラリ内で計算用のコードが実装されているのではなく、ほとんどのケースで
DirectXMathの関数を呼び出しているものです。
DirectXMathというのは
XNAMathという
XBox用の計算ライブラリの互換のある関数群で、
WindowsとDirectXという環境で高速演算できる関数群です。ここでは
DirectXMath関数群の特徴と基本的な使い方を述べるとともに、
BaseCross64とのインターフェイス実現のためにどのような実装がされていて、
DirectXMath関数群を使うためにどのような使い方をするのかを述べたいと思います。
■計算ライブラリの役割■
BaseCross64の、行列やベクトル計算は
DirectXMathを使用しています。コンパイラオプションを
/arch:AVXを追加することにより、64ビット環境で
ストリーミング SIMD 拡張命令 2 (いわゆるSSE2)による計算が実装できます(サンプルはすべてこのオプションがついています)。これは
浮動小数点の計算が最適化されている、ということです。3Dプログラムは、複雑な浮動小数点計算が大量に必要なプログラムです。ですから、こういった
ハードウェア依存の計算は、できれば実装したほうが良い機能でしょう。
BaseCross64では、
DirectXMathを利用するにあったっての手間を、ゲームプログラマに、なるべく負担を掛けないように、
bsm::Vec3や
bsm::Mat4x4などの計算クラスがあります。
以下、
bsm::Quat構造体の宣言ですが
namespace basecross {
namespace bsm {
//中略
//--------------------------------------------------------------------------------------
/// Quat(クオータニオン)
//--------------------------------------------------------------------------------------
struct Quat : public XMFLOAT4
{
//--------------------------------------------------------------------------------------
/*!
@brief コンストラクタ
*/
//--------------------------------------------------------------------------------------
inline Quat();
//中略
//--------------------------------------------------------------------------------------
/*!
@brief コンストラクタ
@param[in] v XMFLOAT3構造体
*/
//--------------------------------------------------------------------------------------
explicit inline Quat(const XMFLOAT4& v);
//中略
//--------------------------------------------------------------------------------------
/*!
@brief コンストラクタ(XMVECTORで初期化)
@param[in] vec 値
*/
//--------------------------------------------------------------------------------------
explicit inline Quat(const XMVECTOR& other);
//--------------------------------------------------------------------------------------
/*!
@brief XMVECTORへのキャスト(thisをキャストして返す)
@return XMVECTOR型の値
*/
//--------------------------------------------------------------------------------------
inline operator XMVECTOR() const;
//中略
//--------------------------------------------------------------------------------------
/*!
@brief XMFLOAT4からの代入
@param[in] other 相手
@return *thisの参照
*/
//--------------------------------------------------------------------------------------
inline Quat& operator=(const XMFLOAT4& other);
//--------------------------------------------------------------------------------------
/*!
@brief XMVECTORからの代入
@param[in] other 相手
@return *thisの参照
*/
//--------------------------------------------------------------------------------------
inline Quat& operator=(const XMVECTOR& other);
//中略
};
}
//end bsm
}
//end basecross
こんな感じで宣言されています。namespaceの
bsmは
BaSecrossMathの略です。関数名が単純なので、他クラスと混同がないように
bsmネームスペースに入ってます。ただし、サンプルはあらかじめ
using句によって
bsmが指定されてますので省略されています。
このように、
bsm::Quatは
XMFLOAT4の派生クラスとして作成したうえで
1、XMFLOAT4を引数としたコンストラクタ
2、XMVECTORを引数としたコンストラクタ
3、XMFLOAT4を引数としたコピー
4、XMVECTORを引数としたコピー
5、XMVECTORへのキャスト演算子の多重定義
を作成し、その中で
ロード関数とストア関数が実装されています。
以下、上記の実体です。
namespace basecross {
namespace bsm {
//中略
//--------------------------------------------------------------------------------------
/// Quatインライン関数
//--------------------------------------------------------------------------------------
inline Quat::Quat():
XMFLOAT4()
{
identity();
}
//中略
inline Quat::Quat(const XMFLOAT4& v) :
XMFLOAT4(v)
{
}
//中略
inline Quat::Quat(const XMVECTOR& other) :
XMFLOAT4()
{
XMVECTOR temp = other;
XMStoreFloat4((XMFLOAT4*)this, temp);
}
inline Quat::operator XMVECTOR() const {
XMFLOAT4 temp = *this;
XMVECTOR Vec = XMLoadFloat4(&temp);
return Vec;
}
inline Quat& Quat::operator=(const XMFLOAT4& other) {
(XMFLOAT4)*this = other;
return *this;
}
inline Quat& Quat::operator=(const XMVECTOR& other) {
XMVECTOR temp = other;
XMStoreFloat4((XMFLOAT4*)this, temp);
return *this;
}
}
//end bsm
}
//end basecross
このように
ロードとストアが実装されています。ですので、
bsm::Quat Qt;
//Qtに対する何らかの操作...
//正規化
Qt = XMQuaternionNormalize(Qt);
のように記述できます。
計算クラスの種類
計算ライブラリは、実装は
basecross::bsmというネームスペースに実装があります。
DxLibプロジェクトの
BaseMath.hおよび
BaseMathInc.hが実装ファイルです。
以下のような種類があります。ちょっと気を付けたいのは、
Vec2やVec3などは、
Flt2やFlt3の別名として実装されているところです。
型名 |
用途 |
親クラス |
備考 |
Vec2 |
2次元ベクトル |
XMFLOAT2 |
Flt2の別名 |
Vec3 |
3次元ベクトル |
XMFLOAT3 |
Flt3の別名 |
Vec4 |
4次元ベクトル |
XMFLOAT4 |
Flt4の別名 |
Pt2 |
2次元ポイント |
XMFLOAT2 |
Flt2の別名 |
Pt3 |
3次元ポイント |
XMFLOAT3 |
Flt3の別名 |
Col4 |
RGBAカラー |
XMFLOAT4 |
Flt4の別名 |
Plane4 |
平面方程式 |
XMFLOAT4 |
Flt4の別名 |
Quat |
クォータニオン |
XMFLOAT4 |
オリジナル名 |
Mat3x3 |
3x3行列 |
XMFLOAT3X3 |
オリジナル名 |
Mat4x4 |
4x4行列 |
XMFLOAT4X4 |
オリジナル名 |
この表で、例えば
Vec3は
Flt3の別名で、
Flt3は
XMFLOAT3の派生クラスとして実装されています。
float型の変数が3つある型が、まず、
Flt3型として実装されていて、その
別名として
Vec3およびPt3があるという形になります。つまり
Vec3とPt3は、名前こそ違いますが、内容は全く同じということです。便宜上、
ベクトルあるいは
ポイントといういい方をしたほうが扱いやすい場合は多々あります。
float型の変数が4つある型も
Flt4型として実装されていて、その
別名として
Vec4、Col4、Plane4があるとなってます。クォータニオンだけ特別で
直接XMFLOAT4の派生となっています。
Mat3x3とMat4x4は行列です。(4x3行列の機能はMat4x4で代用できると思います)。
計算クラスの実装
このように、計算クラスは
DirectXMath関数をより使いやすくするためにありますが、あらかじめラッピング実装されている関数があります。
正規化、単位行列(クォータニオン)、行列転置、逆行列などです。
以下に実装済みの関数の使用方法を紹介します。
SimplSample021の
Player.cppにあります
Player::GetMoveVector()関数です。
Vec3 Player::GetMoveVector() const {
Vec3 Angle(0, 0, 0);
auto PtrGameStage = GetStage<GameStage>();
Vec3 CameraEye, CameraAt;
PtrGameStage->GetCamera().GetCameraEyeAt(CameraEye, CameraAt);
//コントローラの取得
auto CntlVec = App::GetApp()->GetInputDevice().GetControlerVec();
if (CntlVec[0].bConnected) {
if (CntlVec[0].fThumbLX != 0 || CntlVec[0].fThumbLY != 0) {
float MoveLength = 0; //動いた時のスピード
//進行方向の向きを計算
Vec3 Front = m_Pos - CameraEye;
Front.y = 0;
Front.normalize();
//進行方向向きからの角度を算出
float FrontAngle = atan2(Front.z, Front.x);
//コントローラの向き計算
float MoveX = CntlVec[0].fThumbLX;
float MoveZ = CntlVec[0].fThumbLY;
Vec2 MoveVec(MoveX, MoveZ);
float MoveSize = length(MoveVec);
//コントローラの向きから角度を計算
float CntlAngle = atan2(-MoveX, MoveZ);
//トータルの角度を算出
float TotalAngle = FrontAngle + CntlAngle;
//角度からベクトルを作成
Angle = Vec3(cos(TotalAngle), 0, sin(TotalAngle));
//正規化する
Angle.normalize();
//移動サイズを設定。
Angle *= MoveSize;
//Y軸は変化させない
Angle.y = 0;
}
}
return Angle;
}
赤くなっているところが、ライブラリでの実装を使用しているところです。例えば
//進行方向の向きを計算
Vec3 Front = m_Pos - CameraEye;
は
ベクトル同士の引き算ですが、実体は
Flt3型として実装され
inline const Flt3 Flt3::operator -(const Flt3 & vec) const
{
return (Flt3)XMVectorSubtract(*this, vec);
}
のように実装されています。単なる引き算であっても、
XMVectorSubtract()関数という
DirectXMath関数を使用する形で実装されています。
また、ちょっと気を付けたいのは
float MoveSize = length(MoveVec);
ここで
length()という関数は
構造体の外部で実装されている
inline float length(const Flt3 & vec)
{
return ((Flt3)XMVector3Length(vec)).x;
}
という関数が呼ばれます。
normalize(正規化を返す)、length(長さを返す)、lengthSqr(長さの2乗を返す)、dot(内積を返す)、cross(外積を返す。Flt3のみ)などの外部関数があります。
そのほか、詳しくは
DxLibプロジェクトの
BaseMath.hおよび
BaseMathInc.hを参照してください。