/*---------------------------------------------------------------------------* Project: NintendoWare File: gfx_CalculatedTransform.h Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. The content herein is highly confidential and should be handled accordingly. $Revision: 31311 $ *---------------------------------------------------------------------------*/ #ifndef NW_GFX_CALCULATED_TRANSFORM_H_ #define NW_GFX_CALCULATED_TRANSFORM_H_ #include #include #include #include #include namespace nw { namespace gfx { #define NN_MATH_USE_ANONYMOUS_STRUCT //--------------------------------------------------------------------------- //! @brief 計算済みトランスフォームを表すクラスです。 //--------------------------------------------------------------------------- class CalculatedTransform { public: //! ゼロベクトルを判定する境界値です。 //! //! :private static const f32 s_VecSquareLenTol; //---------------------------------------- //! @name 定数定義 //@{ //! 変換情報についての付加情報ビットフラグの定義です。 enum Flag { // NOTE: CreativeStudio側の、Graphics.Scenes.CalculatedTransform.csで定義されているフラグは、 // ここで定義されているものと一致していることが期待されます。 // ビットシフトがずれたりした場合は、両方とも同じように変更してください。 //! @details :private FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED_SHIFT = 0, //! @details :private FLAG_IS_VALID_SHIFT = 1, //! @details :private FLAG_IS_IGNORE_TRANSLATE_SHIFT = 2, //! @details :private FLAG_IS_IGNORE_SCALE_SHIFT = 3, //! @details :private FLAG_IS_IGNORE_ROTATE_SHIFT = 4, //! @details :private FLAG_IS_IDENTITY_SHIFT = 5, //! @details :private FLAG_IS_ROTATE_TRANSLATE_ZERO_SHIFT = 6, //! @details :private FLAG_IS_ROTATE_ZERO_SHIFT = 7, //! @details :private FLAG_IS_TRANSLATE_ZERO_SHIFT = 8, //! @details :private FLAG_IS_SCALE_ONE_SHIFT = 9, //! @details :private FLAG_IS_UNIFORM_SCALE_SHIFT = 10, //! @details :private FLAG_IS_DIRTY_SHIFT = 11, //! @details :private FLAG_FORCE_VIEW_CALCULATION_ENABLED_SHIFT = 12, //! @details :private FLAG_CONVERTED_FOR_BLEND_SHIFT = 30, //! ワールドマトリクスの計算処理を行うのであれば、1になります。 FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED = 0x1 << FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED_SHIFT, //! 有効な変換情報であれば、1になります。 FLAG_IS_VALID = 0x1 << FLAG_IS_VALID_SHIFT, //! 平行移動計算を無視できるのであれば、1になります。 FLAG_IS_IGNORE_TRANSLATE = 0x1 << FLAG_IS_IGNORE_TRANSLATE_SHIFT, //! スケール計算を無視できるのであれば、1になります。 FLAG_IS_IGNORE_SCALE = 0x1 << FLAG_IS_IGNORE_SCALE_SHIFT, //! 回転計算を無視できるのであれば、1になります。 FLAG_IS_IGNORE_ROTATE = 0x1 << FLAG_IS_IGNORE_ROTATE_SHIFT, //! 正規化されていれば、1となります。 FLAG_IS_IDENTITY = 0x1 << FLAG_IS_IDENTITY_SHIFT, //! 回転と平行移動が0であれば、1となります。 FLAG_IS_ROTATE_TRANSLATE_ZERO = 0x1 << FLAG_IS_ROTATE_TRANSLATE_ZERO_SHIFT, //! 回転が0であれば、1となります。 FLAG_IS_ROTATE_ZERO = 0x1 << FLAG_IS_ROTATE_ZERO_SHIFT, //! 平行移動が0であれば、1となります。 FLAG_IS_TRANSLATE_ZERO = 0x1 << FLAG_IS_TRANSLATE_ZERO_SHIFT, //! スケール値全てが1であれば、1となります。 FLAG_IS_SCALE_ONE = 0x1 << FLAG_IS_SCALE_ONE_SHIFT, //! スケール値全てが均一であれば、1となります。 FLAG_IS_UNIFORM_SCALE = 0x1 << FLAG_IS_UNIFORM_SCALE_SHIFT, //! 値が変更されれば、1となります。 FLAG_IS_DIRTY = 0x1 << FLAG_IS_DIRTY_SHIFT, //! 必ず UpdateView を計算するのであれば、1となります。 FLAG_FORCE_VIEW_CALCULATION_ENABLED = 0x1 << FLAG_FORCE_VIEW_CALCULATION_ENABLED_SHIFT, //! アニメーションブレンド用の空間に変換されていれば、1になります。 FLAG_CONVERTED_FOR_BLEND = 0x1 << FLAG_CONVERTED_FOR_BLEND_SHIFT, //! 計算の無視を制御するフラグ群です。 FLAG_IS_IGNORE_ALL = FLAG_IS_IGNORE_SCALE | FLAG_IS_IGNORE_ROTATE | FLAG_IS_IGNORE_TRANSLATE, //! ビットフラグの初期値です。 FLAG_DEFAULT = FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED | FLAG_IS_DIRTY }; //! @brief 単位行列を取得します。 static const CalculatedTransform& Identity() { static const CalculatedTransform identity( FLAG_IS_WORLDMATRIX_CALCULATION_ENABLED | FLAG_IS_IDENTITY | FLAG_IS_ROTATE_TRANSLATE_ZERO | FLAG_IS_ROTATE_ZERO | FLAG_IS_TRANSLATE_ZERO | FLAG_IS_SCALE_ONE | FLAG_IS_UNIFORM_SCALE); return identity; } //@} public: //---------------------------------------- //! @name 作成 //@{ //! コンストラクタです。 CalculatedTransform(bit32 flags) : m_TransformMatrix(math::MTX34::Identity()), m_Scale(1.0f, 1.0f, 1.0f), m_Flags(flags) { } //! コンストラクタです。 CalculatedTransform() : m_TransformMatrix(math::MTX34::Identity()), m_Scale(1.0f, 1.0f, 1.0f), m_Flags(FLAG_DEFAULT) { } //! @brief コンストラクタです。 //! 内部で Setup を呼び出します。 //! //! @param[in] resBone 初期化に用いるボーンです。 //! CalculatedTransform(const ResBone bone) : m_Flags(FLAG_DEFAULT) { // m_TransformMatrix と m_Scale は Setup で設定されます。 Setup(bone); } //! コピーコンストラクタです。 CalculatedTransform(const CalculatedTransform& transform) : m_TransformMatrix(transform.m_TransformMatrix), m_Scale(transform.m_Scale), m_Flags(transform.m_Flags) { } //! @brief リソースボーンから計算済みトランスフォームをセットアップします。 //! //! @param[in] bone リソースボーンです。 //! void Setup(const ResBone bone); //@} //---------------------------------------- //! @name 取得/設定 //@{ //! @brief 座標変換を表す行列を取得します。 //! 更新をした場合、必ず Dirty フラグを設定してください。 math::MTX34& DirectTransformMatrix() { return this->m_TransformMatrix; } //! @brief 座標変換を表す行列を取得します。 //! 自分のスケールは掛かっておらず、親のスケールは掛かっています //! //! @return トランスフォームのマトリクスです。 const math::MTX34& TransformMatrix() const { return this->m_TransformMatrix; } //! @brief 座標変換を表す行列を設定します。 //! Dirty フラグを設定します。 void SetTransformMatrix(const math::MTX34& transformMatrix) { math::MTX34Copy(&this->m_TransformMatrix, transformMatrix); this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //! @brief 座標変換を表す行列を設定します。 //! Dirty フラグを設定します。 void SetTransformMatrix( f32 x00, f32 x01, f32 x02, f32 x03, f32 x10, f32 x11, f32 x12, f32 x13, f32 x20, f32 x21, f32 x22, f32 x23) { this->m_TransformMatrix.f._00 = x00; this->m_TransformMatrix.f._10 = x10; this->m_TransformMatrix.f._20 = x20; this->m_TransformMatrix.f._01 = x01; this->m_TransformMatrix.f._11 = x11; this->m_TransformMatrix.f._21 = x21; this->m_TransformMatrix.f._02 = x02; this->m_TransformMatrix.f._12 = x12; this->m_TransformMatrix.f._22 = x22; this->m_TransformMatrix.f._03 = x03; this->m_TransformMatrix.f._13 = x13; this->m_TransformMatrix.f._23 = x23; this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //! @brief 回転行列を設定します。 //! Dirty フラグを設定します。 template void SetRotateMatrix(const TMatrix& rotateMatrix) { this->m_TransformMatrix.f._00 = rotateMatrix.f._00; this->m_TransformMatrix.f._10 = rotateMatrix.f._10; this->m_TransformMatrix.f._20 = rotateMatrix.f._20; this->m_TransformMatrix.f._01 = rotateMatrix.f._01; this->m_TransformMatrix.f._11 = rotateMatrix.f._11; this->m_TransformMatrix.f._21 = rotateMatrix.f._21; this->m_TransformMatrix.f._02 = rotateMatrix.f._02; this->m_TransformMatrix.f._12 = rotateMatrix.f._12; this->m_TransformMatrix.f._22 = rotateMatrix.f._22; this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //! @brief 回転行列を設定します。 //! Dirty フラグを設定します。 void SetRotateMatrix( f32 x00, f32 x01, f32 x02, f32 x10, f32 x11, f32 x12, f32 x20, f32 x21, f32 x22) { this->m_TransformMatrix.f._00 = x00; this->m_TransformMatrix.f._10 = x10; this->m_TransformMatrix.f._20 = x20; this->m_TransformMatrix.f._01 = x01; this->m_TransformMatrix.f._11 = x11; this->m_TransformMatrix.f._21 = x21; this->m_TransformMatrix.f._02 = x02; this->m_TransformMatrix.f._12 = x12; this->m_TransformMatrix.f._22 = x22; this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //! @brief スケーリング変換ベクトルを取得します。 //! 更新をした場合、必ず Dirty フラグを設定してください。 math::VEC3& DirectScale() { return m_Scale; } //! スケーリング変換ベクトルを取得します。 const math::VEC3& Scale() const { return m_Scale; } //! @brief スケーリング変換ベクトルを設定します。 //! Dirty フラグを設定します。 void SetScale(const math::VEC3& scale) { m_Scale = scale; this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //! @brief スケーリング変換ベクトルを設定します。 //! Dirty フラグを設定します。 void SetScale(f32 fx, f32 fy, f32 fz) { m_Scale.Set(fx, fy, fz); this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //! @brief 平行移動を設定します。 //! Dirty フラグを設定します。 void SetTranslate(f32 x, f32 y, f32 z) { this->m_TransformMatrix.f._03 = x; this->m_TransformMatrix.f._13 = y; this->m_TransformMatrix.f._23 = z; this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //! @brief 平行移動を設定します。 //! Dirty フラグを設定します。 void SetTranslate(const math::VEC3& translate) { SetTranslate(translate.x, translate.y, translate.z); // SetTranslate 内で呼び出しているので不要 //this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //! @brief 平行移動を取得します。 void GetTranslate(math::VEC3* translate) const { NW_NULL_ASSERT(translate); translate->x = this->m_TransformMatrix.f._03; translate->y = this->m_TransformMatrix.f._13; translate->z = this->m_TransformMatrix.f._23; } //! @brief 平行移動を取得します。 math::VEC3 GetTranslate() const { return math::VEC3( this->m_TransformMatrix.f._03, this->m_TransformMatrix.f._13, this->m_TransformMatrix.f._23); } //! @brief 回転を設定します。 //! Dirty フラグを設定します。 void SetRotateXYZ(f32 x, f32 y, f32 z) { math::VEC3 translate; this->GetTranslate(&translate); nw::math::MTX34RotXYZRad(&this->m_TransformMatrix, x, y, z); this->SetTranslate(translate); // SetTranslate 内で呼び出しているので不要 //this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //! @brief 回転と平行移動を設定します。 //! 回転順は XYZ となります。 //! Dirty フラグを設定します。 void SetRotateAndTranslate(const math::VEC3& rotate, const math::VEC3& translate) { nw::math::MTX34RotXYZRad(&this->m_TransformMatrix, rotate.x, rotate.y, rotate.z); this->SetTranslate(translate); // SetTranslate 内で呼び出しているので不要 //this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //! @brief トランスフォームを設定します。 //! Dirty フラグを設定します。 void SetTransform(const math::Transform3& transform) { this->m_Scale = transform.scale; SetRotateAndTranslate(transform.rotate, transform.translate); // SetRotateAndTranslate 内で呼び出しているので不要 //this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } //@} //---------------------------------------- //! @name ユーティリティ関数 //@{ //! @brief 行列の回転成分を正規直交化します。 //! Dirty フラグを設定します。 //! //! @return 正しく変換できれば true を返します。 //! bool NormalizeRotateMatrix() { if (!this->IsEnabledFlags(CalculatedTransform::FLAG_IS_IGNORE_ROTATE)) { // 回転行列を正規直交化します。 // 回転行列の 3 行目を外積で計算 math::VEC3* v0 = reinterpret_cast(&this->m_TransformMatrix.m[0]); math::VEC3* v1 = reinterpret_cast(&this->m_TransformMatrix.m[1]); math::VEC3* v2 = reinterpret_cast(&this->m_TransformMatrix.m[2]); (void)math::VEC3Cross(v2, v0, v1); float lengthSquareV0 = v0->LengthSquare(); float lengthSquareV2 = v2->LengthSquare(); if (lengthSquareV0 < s_VecSquareLenTol || lengthSquareV2 < s_VecSquareLenTol) { // 回転軸がゼロベクトル return false; } else { *v0 *= 1.0f / nn::math::FSqrt(lengthSquareV0); *v2 *= 1.0f / nn::math::FSqrt(lengthSquareV2); (void)math::VEC3Cross(v1, v2, v0); } this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } return true; } //! @brief 行列の回転成分をクォータニオンに変換します。 //! 行列の 00, 01, 02, 10 成分にクォータニオンの x, y, z, w が格納されます。 //! Dirty フラグを設定します。 //! //! @return 正しく変換できれば true を返します。 //! bool RotateMatrixToQuaternion() { if (!this->IsEnabledFlags(CalculatedTransform::FLAG_IS_IGNORE_ROTATE)) { // 行列の回転成分をクォータニオンに変換します。 math::QUAT q; math::MTX34ToQUAT(&q, &this->m_TransformMatrix); this->m_TransformMatrix.f._00 = q.x; this->m_TransformMatrix.f._01 = q.y; this->m_TransformMatrix.f._02 = q.z; this->m_TransformMatrix.f._10 = q.w; this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } return true; } //! @brief クォータニオンを行列の回転成分に変換します。 //! 行列の 00, 01, 02, 10 成分をクォータニオンの x, y, z, w とみなします。 //! Dirty フラグを設定します。 //! //! @return 正しく変換できれば true を返します。 //! bool QuaternionToRotateMatrix() { if (!this->IsEnabledFlags(CalculatedTransform::FLAG_IS_IGNORE_ROTATE)) { // クォータニオンを行列の回転成分に変換します。 math::VEC3 t = this->m_TransformMatrix.GetColumn(3); math::QUAT q(this->m_TransformMatrix.f._00, this->m_TransformMatrix.f._01, this->m_TransformMatrix.f._02, this->m_TransformMatrix.f._10); math::QUATToMTX34(&this->m_TransformMatrix, &q); this->m_TransformMatrix.SetColumn(3, t); // 移動成分を復元 math::VEC3* v0 = reinterpret_cast(&this->m_TransformMatrix.m[0]); math::VEC3* v1 = reinterpret_cast(&this->m_TransformMatrix.m[1]); math::VEC3* v2 = reinterpret_cast(&this->m_TransformMatrix.m[2]); if (math::VEC3SquareLen(v0) < s_VecSquareLenTol || math::VEC3SquareLen(v1) < s_VecSquareLenTol || math::VEC3SquareLen(v2) < s_VecSquareLenTol) { // ブレンド結果の回転軸がゼロベクトル return false; } this->EnableFlags(CalculatedTransform::FLAG_IS_DIRTY); } return true; } //! @brief 行列の回転成分にゼロベクトルがあれば補正します。 //! //! @param[in] useDefaultMtx デフォルトの行列を使用するかどうかです。 //! false なら回転成分を単位行列にします。 //! @param[in] defaultMtx デフォルトの行列です。 //! //! :private void AdjustZeroRotateMatrix(const bool useDefaultMtx, const math::MTX34& defaultMtx) { if (!this->IsEnabledFlags(CalculatedTransform::FLAG_IS_IGNORE_ROTATE)) { const math::MTX34& m = this->TransformMatrix(); const math::VEC3* v0 = reinterpret_cast(&m.m[0]); const math::VEC3* v1 = reinterpret_cast(&m.m[1]); const math::VEC3* v2 = reinterpret_cast(&m.m[2]); if (math::VEC3SquareLen(v0) < s_VecSquareLenTol || math::VEC3SquareLen(v1) < s_VecSquareLenTol || math::VEC3SquareLen(v2) < s_VecSquareLenTol) { if (useDefaultMtx) { this->SetRotateMatrix(defaultMtx); } else { this->SetRotateMatrix( 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f); } } } } //@} //---------------------------------------- //! @name フラグ取得/設定 //@{ //! フラグの値を直接取得します。 bit32 GetFlags() const { return m_Flags; } //! フラグの値を直接設定します。 void SetFlags(bit32 flags) { m_Flags = flags; } //! 指定のフラグの値を直接設定します。 //! //! @param[in] flags フラグを指定します。 //! @param[in] values フラグに設定する値です。 //! void RestoreFlags(bit32 flags, bit32 values) { m_Flags = (m_Flags & ~flags) | (values & flags); } //! 任意のフラグが有効になっているか取得します。 bool IsEnabledFlags(bit32 flags) const { return ut::CheckFlag(m_Flags, flags); } //! 任意のフラグのいずれかが有効になっているか取得します。 bool IsEnabledFlagsOr(bit32 flags) const { return ut::CheckFlagOr(m_Flags, flags); } //! 任意のフラグを有効に設定します。 void EnableFlags(bit32 flags) { m_Flags = ut::EnableFlag(m_Flags, flags); } //! 任意のフラグを無効に設定します。 void DisableFlags(bit32 flags) { m_Flags = ut::DisableFlag(m_Flags, flags); } //! 任意のフラグの有効/無効を設定します。 void EnableFlags(bit32 flags, bool enable) { if (enable) { EnableFlags(flags); } else { DisableFlags(flags); } } //! 全てのフラグを無効にします。 void ResetFlags() { m_Flags = 0x0; } //! 全てのトランスフォームのフラグを無効にします。 void ResetTransformFlags() { m_Flags &= (~(FLAG_IS_IDENTITY | FLAG_IS_ROTATE_TRANSLATE_ZERO | FLAG_IS_ROTATE_ZERO | FLAG_IS_TRANSLATE_ZERO | FLAG_IS_SCALE_ONE | FLAG_IS_UNIFORM_SCALE)); } //! @brief トランスフォーム情報からすべてのフラグを更新します。 //! //! UpdateRotateFlags() の代わりに UpdateRotateFlagsStrictly() を呼び出します。 void UpdateFlagsStrictly(); //! @brief トランスフォーム情報からすべてのフラグを更新します。 //! //! UpdateRotateFlags() を呼び出すため、 //! 回転行列のノルム 1 でない場合、フラグが不正になることがあります。 void UpdateFlags(); //! トランスフォーム情報からスケールのフラグのみを更新します。 void UpdateScaleFlags() { if (this->IsEnabledFlags(FLAG_IS_IGNORE_SCALE)) { return; } this->DisableFlags(FLAG_IS_UNIFORM_SCALE | FLAG_IS_SCALE_ONE); const math::VEC3& scale = this->m_Scale; if (scale.x == scale.y && scale.x == scale.z) { this->EnableFlags(FLAG_IS_UNIFORM_SCALE); if (scale.x == 1.0f) { this->EnableFlags(FLAG_IS_SCALE_ONE); } } } //! @brief トランスフォーム情報から回転のフラグのみを更新します。 //! //! 回転行列部分のすべての成分をチェックしてフラグを更新します。 //! そのため、UpdateRotateFlags() に比べてわずかながら低速です。 void UpdateRotateFlagsStrictly(); //! @brief トランスフォーム情報から回転のフラグのみを更新します。 //! //! 回転行列のノルムが 1 であるという前提で、 //! 対角成分の 11 成分と 22 成分のみをチェックしてフラグ更新を行ないます。 //! そのため、UpdateRotateFlagsStrictly() に比べてわずかながら高速です。 //! ノルム 1 でない場合はフラグ更新が不正になることがあります。 void UpdateRotateFlags(); //! トランスフォーム情報から平行移動のフラグのみを更新します。 void UpdateTranslateFlags(); //! 各成分のフラグから行列全体のフラグを更新します。 void UpdateCompositeFlags(); //@} private: math::MTX34 m_TransformMatrix; math::VEC3 m_Scale; bit32 m_Flags; friend class WorldMatrixUpdater; friend class TransformAnimEvaluator; friend class TransformAnimBlendOp; }; } // gfx } // nw #endif // NW_GFX_CALCULATED_TRANSFORM_H_