//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * 3×4行列ヘッダ
 * @author Junpee
 */

#ifndef MATRIX43_H_
#define MATRIX43_H_

#include <Core/Primitive/Vector3.h>
#include <Core/Primitive/Quaternion.h>

namespace Lamp{

class Matrix33;
class Matrix44;

//------------------------------------------------------------------------------
/**
 * 3×4行列
 *
 * 右手座標系を使用します。
 * このクラスは継承しないで下さい。
 * m30, m31, m32, m33要素が演算に必要な場合、0.f, 0.f, 0.f, 1.fと仮定します。
 */
class Matrix34{
public:
	//--------------------------------------------------------------------------
	// メンバ変数
	//--------------------------------------------------------------------------
	/// メンバ変数
	union{
		/// 各要素
		struct{
			/// 00要素
			float m00;
			/// 01要素
			float m01;
			/// 02要素
			float m02;
			/// 03要素
			float m03;
			/// 10要素
			float m10;
			/// 11要素
			float m11;
			/// 12要素
			float m12;
			/// 13要素
			float m13;
			/// 20要素
			float m20;
			/// 21要素
			float m21;
			/// 22要素
			float m22;
			/// 23要素
			float m23;
		};

		/// 行列値
		float m[3][4];

		/// 配列
		float array[12];
	};

	//--------------------------------------------------------------------------
	// 定数
	//--------------------------------------------------------------------------
	/// ゼロ行列
	static const Matrix34 zero;

	/// 単位行列
	static const Matrix34 unit;

	//--------------------------------------------------------------------------
	// コンストラクタ
	//--------------------------------------------------------------------------
	/**
	 * コンストラクタ
	 *
	 * このコンストラクタは初期値の設定を行わないため値は不定です。
	 */
	Matrix34(){}

	/**
	 * コンストラクタ
	 * @param i00 m00の初期値
	 * @param i01 m01の初期値
	 * @param i02 m02の初期値
	 * @param i03 m03の初期値
	 * @param i10 m10の初期値
	 * @param i11 m11の初期値
	 * @param i12 m12の初期値
	 * @param i13 m13の初期値
	 * @param i20 m20の初期値
	 * @param i21 m21の初期値
	 * @param i22 m22の初期値
	 * @param i23 m23の初期値
	 */
	inline Matrix34(
		float i00, float i01, float i02, float i03,
		float i10, float i11, float i12, float i13,
		float i20, float i21, float i22, float i23) :
		m00(i00), m01(i01), m02(i02), m03(i03),
		m10(i10), m11(i11), m12(i12), m13(i13),
		m20(i20), m21(i21), m22(i22), m23(i23){
	}

	/**
	 * コンストラクタ
	 * @param source 初期値配列
	 */
	inline explicit Matrix34(const float* const source) :
		m00(source[ 0]), m01(source[ 1]), m02(source[ 2]), m03(source[ 3]),
		m10(source[ 4]), m11(source[ 5]), m12(source[ 6]), m13(source[ 7]),
		m20(source[ 8]), m21(source[ 9]), m22(source[10]), m23(source[11]){
	}

	//--------------------------------------------------------------------------
	// 値の設定
	//--------------------------------------------------------------------------
	/**
	 * 値の設定
	 * @param s00 m00の設定値
	 * @param s01 m01の設定値
	 * @param s02 m02の設定値
	 * @param s03 m03の設定値
	 * @param s10 m10の設定値
	 * @param s11 m11の設定値
	 * @param s12 m12の設定値
	 * @param s13 m13の設定値
	 * @param s20 m20の設定値
	 * @param s21 m21の設定値
	 * @param s22 m22の設定値
	 * @param s23 m23の設定値
	 */
	inline void set(
		float s00, float s01, float s02, float s03,
		float s10, float s11, float s12, float s13,
		float s20, float s21, float s22, float s23){
		m00 = s00; m01 = s01; m02 = s02; m03 = s03;
		m10 = s10; m11 = s11; m12 = s12; m13 = s13;
		m20 = s20; m21 = s21; m22 = s22; m23 = s23;
	}

	/**
	 * 値の設定
	 * @param source 設定値配列
	 */
	inline void set(const float* const source){
		m00 = source[ 0]; m01 = source[ 1]; m02 = source[ 2]; m03 = source[ 3];
		m10 = source[ 4]; m11 = source[ 5]; m12 = source[ 6]; m13 = source[ 7];
		m20 = source[ 8]; m21 = source[ 9]; m22 = source[10]; m23 = source[11];
	}

	/**
	 * ゼロ行列設定
	 */
	inline void setZero(){
		set(0.f, 0.f, 0.f, 0.f,
			0.f, 0.f, 0.f, 0.f,
			0.f, 0.f, 0.f, 0.f);
	}

	/**
	 * 単位行列設定
	 */
	inline void setUnit(){
		set(1.f, 0.f, 0.f, 0.f,
			0.f, 1.f, 0.f, 0.f,
			0.f, 0.f, 1.f, 0.f);
	}

	/**
	 * 3×3行列の設定
	 * @param source 設定元行列
	 */
	void set(const Matrix33& source);

	/**
	 * 4×4行列の設定
	 * @param source 設定元行列
	 */
	void set(const Matrix44& source);

	//--------------------------------------------------------------------------
	// スケール
	//--------------------------------------------------------------------------
	/**
	 * スケールの設定
	 * @param scaleX Xスケール値
	 * @param scaleY Yスケール値
	 * @param scaleZ Zスケール値
	 */
	inline void setScale(float scaleX, float scaleY, float scaleZ){
		set(scaleX,    0.f,    0.f,    0.f,
			   0.f, scaleY,    0.f,    0.f,
			   0.f,    0.f, scaleZ,    0.f);
	}

	/**
	 * スケールの設定
	 * @param scale スケール値
	 */
	inline void setScale(const Vector3& scale){
		setScale(scale.x, scale.y, scale.z);
	}

	/**
	 * スケールの追加
	 * @param scaleX Xスケール値
	 * @param scaleY Yスケール値
	 * @param scaleZ Zスケール値
	 */
	inline void addScale(float scaleX, float scaleY, float scaleZ){
		Matrix34 matrix;
		matrix.setScale(scaleX, scaleY, scaleZ);
		(*this) = matrix * (*this);
	}

	/**
	 * スケールの追加
	 * @param scale スケール値
	 */
	inline void addScale(const Vector3& scale){
		Matrix34 matrix;
		matrix.setScale(scale.x, scale.y, scale.z);
		(*this) = matrix * (*this);
	}

	//--------------------------------------------------------------------------
	// 回転
	//--------------------------------------------------------------------------
	/**
	 * X軸回転の設定
	 * @param radian ラジアン単位での回転角度
	 */
	inline void setRotationX(float radian){
		float sin = Math::sin(radian);
		float cos = Math::cos(radian);
		set( 1.f,  0.f,  0.f, 0.f,
			 0.f,  cos, -sin, 0.f,
			 0.f,  sin,  cos, 0.f);
	}

	/**
	 * X軸回転の追加
	 * @param radian ラジアン単位での回転角度
	 */
	inline void addRotationX(float radian){
		Matrix34 matrix;
		matrix.setRotationX(radian);
		(*this) = matrix * (*this);
	}

	//--------------------------------------------------------------------------
	/**
	 * Y軸回転の設定
	 * @param radian ラジアン単位での回転角度
	 */
	inline void setRotationY(float radian){
		float sin = Math::sin(radian);
		float cos = Math::cos(radian);
		set( cos,  0.f,  sin, 0.f,
			 0.f,  1.f,  0.f, 0.f,
			-sin,  0.f,  cos, 0.f);
	}

	/**
	 * Y軸回転の追加
	 * @param radian ラジアン単位での回転角度
	 */
	inline void addRotationY(float radian){
		Matrix34 matrix;
		matrix.setRotationY(radian);
		(*this) = matrix * (*this);
	}

	//--------------------------------------------------------------------------
	/**
	 * Z軸回転の設定
	 * @param radian ラジアン単位での回転角度
	 */
	inline void setRotationZ(float radian){
		float sin = Math::sin(radian);
		float cos = Math::cos(radian);
		set( cos, -sin,  0.f, 0.f,
			 sin,  cos,  0.f, 0.f,
			 0.f,  0.f,  1.f, 0.f);
	}

	/**
	 * Z軸回転の追加
	 * @param radian ラジアン単位での回転角度
	 */
	inline void addRotationZ(float radian){
		Matrix34 matrix;
		matrix.setRotationZ(radian);
		(*this) = matrix * (*this);
	}

	//--------------------------------------------------------------------------
	/**
	 * 軸指定回転の設定
	 * @param axis 回転軸
	 * @param radian ラジアン単位での回転角度
	 */
	inline void setRotationAxis(const Vector3& axis, float radian){
		Assert(axis.isUnit());
		float sin = Math::sin(radian);
		float cos = Math::cos(radian);
		float invCos = 1.f - cos;
		float xyInv = axis.x * axis.y * invCos;
		float xzInv = axis.x * axis.z * invCos;
		float yzInv = axis.y * axis.z * invCos;
		float xSin = axis.x * sin;
		float ySin = axis.y * sin;
		float zSin = axis.z * sin;
		set((axis.x * axis.x * invCos + cos), (xyInv - zSin), (xzInv + ySin),
			0.f,
			(xyInv + zSin), (axis.y * axis.y * invCos + cos), (yzInv - xSin),
			0.f,
			(xzInv - ySin), (yzInv + xSin), (axis.z * axis.z * invCos + cos),
			0.f);
	}

	/**
	 * 軸指定回転の追加
	 * @param axis 回転軸
	 * @param radian ラジアン単位での回転角度
	 */
	inline void addRotationAxis(const Vector3& axis, float radian){
		Matrix34 matrix;
		matrix.setRotationAxis(axis, radian);
		(*this) = matrix * (*this);
	}

	/**
	 * 軸指定回転の取得
	 * @param axis [out]回転軸を格納するベクトルへのポインタ
	 * @param radian [out]ラジアン単位での回転角度を格納するfloatへのポインタ
	 */
	inline void getRotationAxis(Vector3* axis, float* radian) const{
		float radianResult = Math::acos(0.5f * ((m00 + m11 + m22) - 1.f));
		*radian = radianResult;
		if(radianResult > 0.f){
			if(radianResult < Math::PI){
				axis->set(m21 - m12, m02 - m20, m10 - m01);
				axis->normalize();
			}else{
				if(m00 >= m11){
					if(m00 >= m22){
						axis->x = 0.5f * Math::sqrt(m00 - m11 - m22 + 1.f);
						float halfInverse = 0.5f / axis->x;
						axis->y = halfInverse * m01;
						axis->z = halfInverse * m02;
					}else{
						axis->z = 0.5f * Math::sqrt(m22 - m00 - m11 + 1.f);
						float halfInverse = 0.5f / axis->z;
						axis->x = halfInverse * m02;
						axis->y = halfInverse * m12;
					}
				}else{
					if(m11 >= m22){
						axis->y = 0.5f * Math::sqrt(m11 - m00 - m22 + 1.f);
						float halfInverse = 0.5f / axis->y;
						axis->x = halfInverse * m01;
						axis->z = halfInverse * m12;
					}else{
						axis->z = 0.5f * Math::sqrt(m22 - m00 - m11 + 1.f);
						float halfInverse = 0.5f / axis->z;
						axis->x = halfInverse * m02;
						axis->y = halfInverse * m12;
					}
				}
			}
		}else{
			axis->set(1.f, 0.f, 0.f);
		}
	}

	//--------------------------------------------------------------------------
	/**
	 * 四元数回転の設定
	 * @param quaternion 四元数
	 */
	inline void setRotationQuaternion(const Quaternion& quaternion){
		Assert(quaternion.isUnit());
		float x2 = quaternion.x + quaternion.x;
		float y2 = quaternion.y + quaternion.y;
		float z2 = quaternion.z + quaternion.z;
		float xx2 = quaternion.x * x2;
		float xy2 = quaternion.x * y2;
		float xz2 = quaternion.x * z2;
		float yy2 = quaternion.y * y2;
		float yz2 = quaternion.y * z2;
		float zz2 = quaternion.z * z2;
		float wx2 = quaternion.w * x2;
		float wy2 = quaternion.w * y2;
		float wz2 = quaternion.w * z2;
		m00 = 1.f - (yy2 + zz2);
		m01 = xy2 - wz2;
		m02 = xz2 + wy2;
		m03 = 0.f;

		m10 = xy2 + wz2;
		m11 = 1.f - (xx2 + zz2);
		m12 = yz2 - wx2;
		m13 = 0.f;

		m20 = xz2 - wy2;
		m21 = yz2 + wx2;
		m22 = 1.f - (xx2 + yy2);
		m23 = 0.f;
	}

	/**
	 * 四元数回転の追加
	 * @param quaternion 四元数
	 */
	inline void addRotationQuaternion(const Quaternion& quaternion){
		Matrix34 matrix;
		matrix.setRotationQuaternion(quaternion);
		(*this) = matrix * (*this);
	}

	/**
	 * 四元数回転の取得
	 * @return 回転を表す四元数
	 */
	inline Quaternion getRotationQuaternion() const{
		Quaternion result;
		float trace = m00 + m11 + m22;
		if(trace > 0.f){
			float scale = Math::sqrt(trace + 1.f);
			result.w = scale * 0.5f;
			scale = 0.5f / scale;
			result.x = (m21 - m12) * scale;
			result.y = (m02 - m20) * scale;
			result.z = (m10 - m01) * scale;
		}else{
			int i = 0;
			if(m11 > m00){ i = 1; }
			if(m22 > m[i][i]){ i = 2; }
			int nextTable[] = { 1, 2, 0 };
			int j = nextTable[i];
			int k = nextTable[j];
			float scale = Math::sqrt(m[i][i] - m[j][j] - m[k][k] + 1.f);
			result.array[i] = 0.5f * scale;
			scale = 0.5f / scale;
			result.w = (m[k][j] - m[j][k]) * scale;
			result.array[j] = (m[j][i] + m[i][j]) * scale;
			result.array[k] = (m[k][i] + m[i][k]) * scale;
		}
		return result;
	}

	//--------------------------------------------------------------------------
	// 三軸指定回転
	//--------------------------------------------------------------------------
	/**
	 * XYZ軸回転の設定
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	inline void setRotationXYZ(const Vector3& radian){
		float sinX = Math::sin(radian.x);
		float cosX = Math::cos(radian.x);
		float sinY = Math::sin(radian.y);
		float cosY = Math::cos(radian.y);
		float sinZ = Math::sin(radian.z);
		float cosZ = Math::cos(radian.z);
		m00 = cosY * cosZ;
		m01 = sinX * sinY * cosZ - cosX * sinZ;
		m02 = cosX * sinY * cosZ + sinX * sinZ;
		m03 = 0.f;

		m10 = cosY * sinZ;
		m11 = sinX * sinY * sinZ + cosX * cosZ;
		m12 = cosX * sinY * sinZ - sinX * cosZ;
		m13 = 0.f;

		m20 = -sinY;
		m21 = sinX * cosY;
		m22 = cosX * cosY;
		m23 = 0.f;
	}

	/**
	 * XYZ軸回転の追加
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	inline void addRotationXYZ(const Vector3& radian){
		Matrix34 matrix;
		matrix.setRotationXYZ(radian);
		(*this) = matrix * (*this); 
	}

	/**
	 * XYZ軸回転の取得
	 * @param radian [out] 各軸におけるラジアン単位での回転角度
	 * @return 答えが単一であればtrue
	 */
	inline bool getRotationXYZ(Vector3* radian) const{
		float yRadian = Math::asin(-m20);
		radian->y = yRadian;
		if(yRadian < Math::halfPI){
			if(yRadian > -Math::halfPI){
				radian->x = Math::atan2(m21, m22);
				radian->z = Math::atan2(m10, m00);
				return true;
			}else{
				radian->x = -Math::atan2(m01, m11);
				radian->z = 0.f;
				return false;
			}
		}else{
			radian->x = Math::atan2(m01, m11);
			radian->z = 0.f;
			return false;
		}
	}

	//--------------------------------------------------------------------------
	/**
	 * XZY軸回転の設定
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	// 展開すれば高速化できる
	inline void setRotationXZY(const Vector3& radian){
		setRotationX(radian.x);
		addRotationZ(radian.z);
		addRotationY(radian.y);
	}

	/**
	 * XZY軸回転の追加
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	inline void addRotationXZY(const Vector3& radian){
		Matrix34 matrix;
		matrix.setRotationXZY(radian);
		(*this) = matrix * (*this); 
	}

	/**
	 * XZY軸回転の取得
	 * @param radian [out] 各軸におけるラジアン単位での回転角度
	 * @return 答えが単一であればtrue
	 */
	inline bool getRotationXZY(Vector3* radian) const{
		float zRadian = Math::asin(m10);
		radian->z = zRadian;
		if(zRadian < Math::halfPI){
			if(zRadian > -Math::halfPI){
				radian->x = Math::atan2(-m12, m11);
				radian->y = Math::atan2(-m20, m00);
				return true;
			}else{
				radian->x = -Math::atan2(m02, m22);
				radian->y = 0.f;
				return false;
			}
		}else{
			radian->x = Math::atan2(m02, m22);
			radian->y = 0.f;
			return false;
		}
	}

	//--------------------------------------------------------------------------
	/**
	 * YXZ軸回転の設定
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	// 展開すれば高速化できる
	inline void setRotationYXZ(const Vector3& radian){
		setRotationY(radian.y);
		addRotationX(radian.x);
		addRotationZ(radian.z);
	}

	/**
	 * YXZ軸回転の追加
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	inline void addRotationYXZ(const Vector3& radian){
		Matrix34 matrix;
		matrix.setRotationYXZ(radian);
		(*this) = matrix * (*this); 
	}

	/**
	 * YXZ軸回転の取得
	 * @param radian [out] 各軸におけるラジアン単位での回転角度
	 * @return 答えが単一であればtrue
	 */
	inline bool getRotationYXZ(Vector3* radian) const{
		float xRadian = Math::asin(m21);
		radian->x = xRadian;
		if(xRadian < Math::halfPI){
			if(xRadian > -Math::halfPI){
				radian->y = Math::atan2(-m20, m22);
				radian->z = Math::atan2(-m01, m11);
				return true;
			}else{
				radian->y = -Math::atan2(-m10, m00);
				radian->z = 0.f;
				return false;
			}
		}else{
			radian->y = Math::atan2(-m10, m00);
			radian->z = 0.f;
			return false;
		}
	}

	//--------------------------------------------------------------------------
	/**
	 * YZX軸回転の設定
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	// 展開すれば高速化できる
	inline void setRotationYZX(const Vector3& radian){
		setRotationY(radian.y);
		addRotationZ(radian.z);
		addRotationX(radian.x);
	}

	/**
	 * YZX軸回転の追加
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	inline void addRotationYZX(const Vector3& radian){
		Matrix34 matrix;
		matrix.setRotationYZX(radian);
		(*this) = matrix * (*this); 
	}

	/**
	 * YZX軸回転の取得
	 * @param radian [out] 各軸におけるラジアン単位での回転角度
	 * @return 答えが単一であればtrue
	 */
	inline bool getRotationYZX(Vector3* radian) const{
		float zRadian = Math::asin(-m01);
		radian->z = zRadian;
		if(zRadian < Math::halfPI){
			if(zRadian > -Math::halfPI){
				radian->y = Math::atan2(m02, m00);
				radian->x = Math::atan2(m21, m11);
				return true;
			}else{
				radian->y = -Math::atan2(m12, m22);
				radian->x = 0.f;
				return false;
			}
		}else{
			radian->y = Math::atan2(m12, m22);
			radian->x = 0.f;
			return false;
		}
	}

	//--------------------------------------------------------------------------
	/**
	 * ZXY軸回転の設定
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	// 展開すれば高速化できる
	inline void setRotationZXY(const Vector3& radian){
		setRotationZ(radian.z);
		addRotationX(radian.x);
		addRotationY(radian.y);
	}

	/**
	 * ZXY軸回転の追加
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	inline void addRotationZXY(const Vector3& radian){
		Matrix34 matrix;
		matrix.setRotationZXY(radian);
		(*this) = matrix * (*this); 
	}

	/**
	 * ZXY軸回転の取得
	 * @param radian [out] 各軸におけるラジアン単位での回転角度
	 * @return 答えが単一であればtrue
	 */
	inline bool getRotationZXY(Vector3* radian) const{
		float xRadian = Math::asin(-m12);
		radian->x = xRadian;
		if(xRadian < Math::halfPI){
			if(xRadian > -Math::halfPI){
				radian->z = Math::atan2(m10, m11);
				radian->y = Math::atan2(m02, m22);
				return true;
			}else{
				radian->z = -Math::atan2(m20, m00);
				radian->y = 0.f;
				return false;
			}
		}else{
			radian->z = Math::atan2(m20, m00);
			radian->y = 0.f;
			return false;
		}
	}

	//--------------------------------------------------------------------------
	/**
	 * ZYX軸回転の設定
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	// 展開すれば高速化できる
	inline void setRotationZYX(const Vector3& radian){
		setRotationZ(radian.z);
		addRotationY(radian.y);
		addRotationX(radian.x);
	}

	/**
	 * ZYX軸回転の追加
	 * @param radian 各軸におけるラジアン単位での回転角度
	 */
	inline void addRotationZYX(const Vector3& radian){
		Matrix34 matrix;
		matrix.setRotationZYX(radian);
		(*this) = matrix * (*this); 
	}

	/**
	 * ZYX軸回転の取得
	 * @param radian [out] 各軸におけるラジアン単位での回転角度
	 * @return 答えが単一であればtrue
	 */
	inline bool getRotationZYX(Vector3* radian) const{
		float yRadian = Math::asin(m02);
		radian->y = yRadian;
		if(yRadian < Math::halfPI){
			if(yRadian > -Math::halfPI){
				radian->z = Math::atan2(-m01, m00);
				radian->x = Math::atan2(-m12, m22);
				return true;
			}else{
				radian->z = -Math::atan2(-m10, m20);
				radian->x = 0.f;
				return false;
			}
		}else{
			radian->z = Math::atan2(m10, -m20);
			radian->x = 0.f;
			return false;
		}
	}

	//--------------------------------------------------------------------------
	// 移動
	//--------------------------------------------------------------------------
	/**
	 * 移動の設定
	 * @param translationX X移動値
	 * @param translationY Y移動値
	 * @param translationZ Z移動値
	 */
	inline void setTranslation(
		float translationX, float translationY, float translationZ){
		set(1.f, 0.f, 0.f, translationX,
			0.f, 1.f, 0.f, translationY,
			0.f, 0.f, 1.f, translationZ);
	}

	/**
	 * 移動の設定
	 * @param translation 移動値
	 */
	inline void setTranslation(const Vector3& translation){
		setTranslation(translation.x, translation.y, translation.z);
	}

	/**
	 * 移動の追加
	 * @param translationX X移動値
	 * @param translationY Y移動値
	 * @param translationZ Z移動値
	 */
	inline void addTranslation(
		float translationX, float translationY, float translationZ){
		Matrix34 matrix;
		matrix.setTranslation(translationX, translationY, translationZ);
		(*this) = matrix * (*this);
	}

	/**
	 * 移動の追加
	 * @param translation 移動値
	 */
	inline void addTranslation(const Vector3& translation){
		Matrix34 matrix;
		matrix.setTranslation(translation.x, translation.y, translation.z);
		(*this) = matrix * (*this);
	}

	/**
	 * 移動の取得
	 * @return 移動値
	 */
	inline Vector3 getTranslation() const{ return Vector3(m03, m13, m23); }

	//--------------------------------------------------------------------------
	// 変換
	//--------------------------------------------------------------------------
	/**
	 * XYZ回転の変換設定
	 *
	 * XYZ回転、移動の順に変換する行列を作成します
	 * @param radian 各軸におけるラジアン単位での回転角度
	 * @param translation 移動値
	 */
	inline void setTransformationXYZ(
		const Vector3& radian, const Vector3& translation){
		float sinX = Math::sin(radian.x);
		float cosX = Math::cos(radian.x);
		float sinY = Math::sin(radian.y);
		float cosY = Math::cos(radian.y);
		float sinZ = Math::sin(radian.z);
		float cosZ = Math::cos(radian.z);
		m00 = cosY * cosZ;
		m01 = sinX * sinY * cosZ - cosX * sinZ;
		m02 = cosX * sinY * cosZ + sinX * sinZ;
		m03 = translation.x;

		m10 = cosY * sinZ;
		m11 = sinX * sinY * sinZ + cosX * cosZ;
		m12 = cosX * sinY * sinZ - sinX * cosZ;
		m13 = translation.y;

		m20 = -sinY;
		m21 = sinX * cosY;
		m22 = cosX * cosY;
		m23 = translation.z;
	}

	/**
	 * XYZ回転の変換追加
	 *
	 * XYZ回転、移動の順に変換を追加します
	 * @param radian 各軸におけるラジアン単位での回転角度
	 * @param translation 移動値
	 */
	inline void addTransformationXYZ(
		const Vector3& radian, const Vector3& translation){
		Matrix34 matrix;
		matrix.setTransformationXYZ(radian, translation);
		(*this) = matrix * (*this); 
	}

	//--------------------------------------------------------------------------
	/**
	 * XYZ回転の変換設定
	 *
	 * スケール、XYZ回転、移動の順に変換する行列を作成します
	 * @param scale スケール値
	 * @param radian 各軸におけるラジアン単位での回転角度
	 * @param translation 移動値
	 */
	inline void setTransformationXYZ(const Vector3& scale,
		const Vector3& radian, const Vector3& translation){
		float sinX = Math::sin(radian.x);
		float cosX = Math::cos(radian.x);
		float sinY = Math::sin(radian.y);
		float cosY = Math::cos(radian.y);
		float sinZ = Math::sin(radian.z);
		float cosZ = Math::cos(radian.z);
		m00 = scale.x * (cosY * cosZ);
		m01 = scale.y * (sinX * sinY * cosZ - cosX * sinZ);
		m02 = scale.z * (cosX * sinY * cosZ + sinX * sinZ);
		m03 = translation.x;

		m10 = scale.x * (cosY * sinZ);
		m11 = scale.y * (sinX * sinY * sinZ + cosX * cosZ);
		m12 = scale.z * (cosX * sinY * sinZ - sinX * cosZ);
		m13 = translation.y;

		m20 = scale.x * (-sinY);
		m21 = scale.y * (sinX * cosY);
		m22 = scale.z * (cosX * cosY);
		m23 = translation.z;
	}

	/**
	 * XYZ回転の変換追加
	 *
	 * スケール、XYZ回転、移動の順に変換を追加します
	 * @param scale スケール値
	 * @param radian 各軸におけるラジアン単位での回転角度
	 * @param translation 移動値
	 */
	inline void addTransformationXYZ(const Vector3& scale,
		const Vector3& radian, const Vector3& translation){
		Matrix34 matrix;
		matrix.setTransformationXYZ(scale, radian, translation);
		(*this) = matrix * (*this); 
	}

	//--------------------------------------------------------------------------
	/**
	 * 四元数回転の変換設定
	 *
	 * 四元数回転、移動の順に変換する行列を作成します
	 * @param translation 移動値
	 * @param quaternion 四元数
	 */
	inline void setTransformationQuaternion(
		const Quaternion& quaternion, const Vector3& translation){
		Assert(quaternion.isUnit());
		float x2 = quaternion.x + quaternion.x;
		float y2 = quaternion.y + quaternion.y;
		float z2 = quaternion.z + quaternion.z;
		float xx2 = quaternion.x * x2;
		float xy2 = quaternion.x * y2;
		float xz2 = quaternion.x * z2;
		float yy2 = quaternion.y * y2;
		float yz2 = quaternion.y * z2;
		float zz2 = quaternion.z * z2;
		float wx2 = quaternion.w * x2;
		float wy2 = quaternion.w * y2;
		float wz2 = quaternion.w * z2;
		m00 = 1.f - (yy2 + zz2);
		m01 = xy2 - wz2;
		m02 = xz2 + wy2;
		m03 = translation.x;

		m10 = xy2 + wz2;
		m11 = 1.f - (xx2 + zz2);
		m12 = yz2 - wx2;
		m13 = translation.y;

		m20 = xz2 - wy2;
		m21 = yz2 + wx2;
		m22 = 1.f - (xx2 + yy2);
		m23 = translation.z;

	}

	/**
	 * 四元数回転の変換追加
	 *
	 * 四元数回転、移動の順に変換を追加します
	 * @param translation 移動値
	 * @param quaternion 四元数
	 */
	inline void addTransformationQuaternion(
		const Quaternion& quaternion, const Vector3& translation){
		Matrix34 matrix;
		matrix.setTransformationQuaternion(quaternion, translation);
		(*this) = matrix * (*this); 
	}

	//--------------------------------------------------------------------------
	/**
	 * 四元数回転の変換設定
	 *
	 * スケール、四元数回転、移動の順に変換する行列を作成します
	 * @param scale スケール値
	 * @param translation 移動値
	 * @param quaternion 四元数
	 */
	inline void setTransformationQuaternion(const Vector3& scale,
		const Quaternion& quaternion, const Vector3& translation){
		Assert(quaternion.isUnit());
		float x2 = quaternion.x + quaternion.x;
		float y2 = quaternion.y + quaternion.y;
		float z2 = quaternion.z + quaternion.z;
		float xx2 = quaternion.x * x2;
		float xy2 = quaternion.x * y2;
		float xz2 = quaternion.x * z2;
		float yy2 = quaternion.y * y2;
		float yz2 = quaternion.y * z2;
		float zz2 = quaternion.z * z2;
		float wx2 = quaternion.w * x2;
		float wy2 = quaternion.w * y2;
		float wz2 = quaternion.w * z2;
		m00 = scale.x * (1.f - (yy2 + zz2));
		m01 = scale.y * (xy2 - wz2);
		m02 = scale.z * (xz2 + wy2);
		m03 = translation.x;

		m10 = scale.x * (xy2 + wz2);
		m11 = scale.y * (1.f - (xx2 + zz2));
		m12 = scale.z * (yz2 - wx2);
		m13 = translation.y;

		m20 = scale.x * (xz2 - wy2);
		m21 = scale.y * (yz2 + wx2);
		m22 = scale.z * (1.f - (xx2 + yy2));
		m23 = translation.z;

	}

	/**
	 * 四元数回転の変換追加
	 *
	 * スケール、四元数回転、移動の順に変換を追加します
	 * @param scale スケール値
	 * @param translation 移動値
	 * @param quaternion 四元数
	 */
	inline void addTransformationQuaternion(const Vector3& scale,
		const Quaternion& quaternion, const Vector3& translation){
		Matrix34 matrix;
		matrix.setTransformationQuaternion(scale, quaternion, translation);
		(*this) = matrix * (*this); 
	}

	//--------------------------------------------------------------------------
	// 乗算
	//--------------------------------------------------------------------------
	/**
	 * 行列乗算
	 *
	 * 左側の行列から順に計算が適用されます。
	 * @param mtx 乗算する行列
	 * @return 乗算された行列
	 */
	inline Matrix34 operator *(const Matrix34& mtx) const{
		return Matrix34(
			(m00 * mtx.m00) + (m01 * mtx.m10) + (m02 * mtx.m20),
			(m00 * mtx.m01) + (m01 * mtx.m11) + (m02 * mtx.m21),
			(m00 * mtx.m02) + (m01 * mtx.m12) + (m02 * mtx.m22),
			(m00 * mtx.m03) + (m01 * mtx.m13) + (m02 * mtx.m23) + m03,
			(m10 * mtx.m00) + (m11 * mtx.m10) + (m12 * mtx.m20),
			(m10 * mtx.m01) + (m11 * mtx.m11) + (m12 * mtx.m21),
			(m10 * mtx.m02) + (m11 * mtx.m12) + (m12 * mtx.m22),
			(m10 * mtx.m03) + (m11 * mtx.m13) + (m12 * mtx.m23) + m13,
			(m20 * mtx.m00) + (m21 * mtx.m10) + (m22 * mtx.m20),
			(m20 * mtx.m01) + (m21 * mtx.m11) + (m22 * mtx.m21),
			(m20 * mtx.m02) + (m21 * mtx.m12) + (m22 * mtx.m22),
			(m20 * mtx.m03) + (m21 * mtx.m13) + (m22 * mtx.m23) + m23);
	}

	/**
	 * 代入行列乗算
	 *
	 * 左側の行列から順に計算が適用されます。
	 * @param mtx 乗算する行列
	 * @return 乗算された行列
	 */
	inline Matrix34& operator *=(Matrix34 mtx){
		float old00 = m00;
		float old01 = m01;
		float old02 = m02;
		m00 = (old00 * mtx.m00) + (old01 * mtx.m10) + (old02 * mtx.m20);
		m01 = (old00 * mtx.m01) + (old01 * mtx.m11) + (old02 * mtx.m21);
		m02 = (old00 * mtx.m02) + (old01 * mtx.m12) + (old02 * mtx.m22);
		m03 = (old00 * mtx.m03) + (old01 * mtx.m13) + (old02 * mtx.m23) + m03;
		float old10 = m10;
		float old11 = m11;
		float old12 = m12;
		m10 = (old10 * mtx.m00) + (old11 * mtx.m10) + (old12 * mtx.m20);
		m11 = (old10 * mtx.m01) + (old11 * mtx.m11) + (old12 * mtx.m21);
		m12 = (old10 * mtx.m02) + (old11 * mtx.m12) + (old12 * mtx.m22);
		m13 = (old10 * mtx.m03) + (old11 * mtx.m13) + (old12 * mtx.m23) + m13;
		float old20 = m20;
		float old21 = m21;
		float old22 = m22;
		m20 = (old20 * mtx.m00) + (old21 * mtx.m10) + (old22 * mtx.m20);
		m21 = (old20 * mtx.m01) + (old21 * mtx.m11) + (old22 * mtx.m21);
		m22 = (old20 * mtx.m02) + (old21 * mtx.m12) + (old22 * mtx.m22);
		m23 = (old20 * mtx.m03) + (old21 * mtx.m13) + (old22 * mtx.m23) + m23;
		return *this;
	}

	/**
	 * ベクトル乗算
	 * @param vector 乗算するベクトル
	 * @return 乗算されたベクトル
	 */
	inline Vector3 operator *(const Vector3& vector) const{
		return Vector3(
			vector.x * m00 + vector.y * m01 + vector.z * m02 + m03,
			vector.x * m10 + vector.y * m11 + vector.z * m12 + m13,
			vector.x * m20 + vector.y * m21 + vector.z * m22 + m23);
	}

	/**
	 * 3x3のベクトル乗算
	 * @param vector 3x3の乗算をするベクトル
	 * @return 3x3の乗算されたベクトル
	 */
	inline Vector3 multiply33(const Vector3& vector) const{
		return Vector3(
			vector.x * m00 + vector.y * m01 + vector.z * m02,
			vector.x * m10 + vector.y * m11 + vector.z * m12,
			vector.x * m20 + vector.y * m21 + vector.z * m22);
	}

	/**
	 * スカラー乗算
	 * @param value 乗算するスカラー
	 * @return 乗算された行列
	 */
	inline Matrix34 operator *(float value) const{
		return Matrix34(
			m00 * value, m01 * value, m02 * value, m03 * value,
			m10 * value, m11 * value, m12 * value, m13 * value,
			m20 * value, m21 * value, m22 * value, m23 * value);
	}

	/**
	 * スカラー乗算
	 * @param value 乗算するスカラー
	 * @return 乗算された行列
	 */
	inline Matrix34& operator *=(float value){
		m00 *= value;
		m01 *= value;
		m02 *= value;
		m03 *= value;
		m10 *= value;
		m11 *= value;
		m12 *= value;
		m13 *= value;
		m20 *= value;
		m21 *= value;
		m22 *= value;
		m23 *= value;
		return *this;
	}

	//--------------------------------------------------------------------------
	// 行列演算
	//--------------------------------------------------------------------------
	/**
	 * 転置
	 *
	 * m30、m31、m32は0クリアされます。
	 */
	inline void transpose(){
		float swap;
		swap = m01; m01 = m10; m10 = swap;
		swap = m02; m02 = m20; m20 = swap;
		swap = m12; m12 = m21; m21 = swap;
		m03 = m13 = m23 = 0.f;
	}

	/**
	 * 行列式
	 * @return 行列式
	 */
	inline float determinant() const{
		return
			m00 * (m11 * m22 - m12 * m21) -
			m01 * (m10 * m22 - m12 * m20) +
			m02 * (m10 * m21 - m11 * m20);
	}

	/**
	 * 逆行列
	 * @return 行列式
	 */
	inline float invert(){
		Matrix34 invertMatrix;
		// 行列式を出す
		invertMatrix.m00 = m11 * m22 - m12 * m21;
		invertMatrix.m10 = -(m10 * m22 - m12 * m20);
		invertMatrix.m20 = m10 * m21 - m11 * m20;
		float determ =
			m00 * invertMatrix.m00 +
			m01 * invertMatrix.m10 +
			m02 * invertMatrix.m20;
		Assert(determ > Math::epsilon);
		// 各要素の算出
		invertMatrix.m01 = -(m01 * m22 - m02 * m21);
		invertMatrix.m02 = m01 * m12 - m02 * m11;
		invertMatrix.m03 = -(
			m01 * (m12 * m23 - m22 * m13) -
			m02 * (m11 * m23 - m21 * m13) +
			m03 * (m11 * m22 - m21 * m12));

		invertMatrix.m11 = m00 * m22 - m02 * m20;
		invertMatrix.m12 = -(m00 * m12 - m02 * m10);
		invertMatrix.m13 = (
			m00 * (m12 * m23 - m22 * m13) -
			m02 * (m10 * m23 - m20 * m13) +
			m03 * (m10 * m22 - m20 * m12));

		invertMatrix.m21 = -(m00 * m21 - m01 * m20);
		invertMatrix.m22 = m00 * m11 - m01 * m10;
		invertMatrix.m23 = -(
			m00 * (m11 * m23 - m21 * m13) -
			m01 * (m10 * m23 - m20 * m13) +
			m03 * (m10 * m21 - m20 * m11));

		// 行列式の逆数を掛ける
		float invDeterm = 1.f / determ;
		invertMatrix *= invDeterm;
		(*this) = invertMatrix;
		return determ;
	}

	/**
	 * 逆行列
	 * @param invertMatrix [out] 逆行列を格納する行列へのポインタ
	 * @return 行列式
	 */
	inline float invert(Matrix34* invertMatrix) const{
		Assert(invertMatrix != NULL);
		Assert(invertMatrix != this);
		// 行列式を出す
		invertMatrix->m00 = m11 * m22 - m12 * m21;
		invertMatrix->m10 = -(m10 * m22 - m12 * m20);
		invertMatrix->m20 = m10 * m21 - m11 * m20;
		float determ =
			m00 * invertMatrix->m00 +
			m01 * invertMatrix->m10 +
			m02 * invertMatrix->m20;
		Assert(determ > Math::epsilon);
		// 各要素の算出
		invertMatrix->m01 = -(m01 * m22 - m02 * m21);
		invertMatrix->m02 = m01 * m12 - m02 * m11;
		invertMatrix->m03 = -(
			m01 * (m12 * m23 - m22 * m13) -
			m02 * (m11 * m23 - m21 * m13) +
			m03 * (m11 * m22 - m21 * m12));

		invertMatrix->m11 = m00 * m22 - m02 * m20;
		invertMatrix->m12 = -(m00 * m12 - m02 * m10);
		invertMatrix->m13 = (
			m00 * (m12 * m23 - m22 * m13) -
			m02 * (m10 * m23 - m20 * m13) +
			m03 * (m10 * m22 - m20 * m12));

		invertMatrix->m21 = -(m00 * m21 - m01 * m20);
		invertMatrix->m22 = m00 * m11 - m01 * m10;
		invertMatrix->m23 = -(
			m00 * (m11 * m23 - m21 * m13) -
			m01 * (m10 * m23 - m20 * m13) +
			m03 * (m10 * m21 - m20 * m11));

		// 行列式の逆数を掛ける
		float invDeterm = 1.f / determ;
		(*invertMatrix) *= invDeterm;
		return determ;
	}

	//--------------------------------------------------------------------------
	/**
	 * 変換行列の逆行列
	 *
	 * 変換行列には回転と移動しか含まれていないと仮定する
	 */
	inline void invertTransformation(){
		// 回転行列を転置
		float swap;
		swap = m01; m01 = m10; m10 = swap;
		swap = m02; m02 = m20; m20 = swap;
		swap = m12; m12 = m21; m21 = swap;
		// 移動値を逆変換
		Vector3 trans(-m03, -m13, -m23);
		m03 = m00 * trans.x + m01 * trans.y + m02 * trans.z;
		m13 = m10 * trans.x + m11 * trans.y + m12 * trans.z;
		m23 = m20 * trans.x + m21 * trans.y + m22 * trans.z;
	}

	/**
	 * 変換行列の逆行列
	 *
	 * 変換行列には回転と移動しか含まれていないと仮定する
	 * @param invertMatrix [out] 逆行列を格納する行列へのポインタ
	 */
	inline void invertTransformation(Matrix34* invertMatrix) const{
		// 回転行列を転置
		invertMatrix->m00 = m00;
		invertMatrix->m01 = m10;
		invertMatrix->m02 = m20;
		invertMatrix->m10 = m01;
		invertMatrix->m11 = m11;
		invertMatrix->m12 = m21;
		invertMatrix->m20 = m02;
		invertMatrix->m21 = m12;
		invertMatrix->m22 = m22;
		// 移動値を逆変換
		Vector3 trans(-m03, -m13, -m23);
		invertMatrix->m03 = m00 * trans.x + m10 * trans.y + m20 * trans.z;
		invertMatrix->m13 = m01 * trans.x + m11 * trans.y + m21 * trans.z;
		invertMatrix->m23 = m02 * trans.x + m12 * trans.y + m22 * trans.z;
	}

	//--------------------------------------------------------------------------
	// 論理演算
	//--------------------------------------------------------------------------
	/**
	 * 行列が同じかどうか
	 * @param target 比較する行列
	 * @return 同じ値であればtrueを返す
	 */
	inline bool operator ==(const Matrix34& target) const{
		return (
			(m00 == target.m00) && (m01 == target.m01) &&
			(m02 == target.m02) && (m03 == target.m03) &&
			(m10 == target.m10) && (m11 == target.m11) &&
			(m12 == target.m12) && (m13 == target.m13) &&
			(m20 == target.m20) && (m21 == target.m21) &&
			(m22 == target.m22) && (m23 == target.m23));
	}

	/**
	 * 行列が同じかどうか
	 * @param target 比較する行列
	 * @param epsilon 誤差
	 * @return 誤差の範囲内で同じ値であればtrueを返す
	 */
	inline bool epsilonEquals(const Matrix34& target, float epsilon) const{
		Assert(epsilon >= 0.f);
		return (
			(Math::abs(m00 - target.m00) <= epsilon) &&
			(Math::abs(m01 - target.m01) <= epsilon) &&
			(Math::abs(m02 - target.m02) <= epsilon) &&
			(Math::abs(m03 - target.m03) <= epsilon) &&
			(Math::abs(m10 - target.m10) <= epsilon) &&
			(Math::abs(m11 - target.m11) <= epsilon) &&
			(Math::abs(m12 - target.m12) <= epsilon) &&
			(Math::abs(m13 - target.m13) <= epsilon) &&
			(Math::abs(m20 - target.m20) <= epsilon) &&
			(Math::abs(m21 - target.m21) <= epsilon) &&
			(Math::abs(m22 - target.m22) <= epsilon) &&
			(Math::abs(m23 - target.m23) <= epsilon));
	}

	/**
	 * 行列が同じでないかどうか
	 * @param target 比較する行列
	 * @return 同じでない値であればtrueを返す
	 */
	inline bool operator !=(const Matrix34& target) const{
		return (
			(m00 != target.m00) || (m01 != target.m01) ||
			(m02 != target.m02) || (m03 != target.m03) ||
			(m10 != target.m10) || (m11 != target.m11) ||
			(m12 != target.m12) || (m13 != target.m13) ||
			(m20 != target.m20) || (m21 != target.m21) ||
			(m22 != target.m22) || (m23 != target.m23));
	}

	/**
	 * 行列が同じでないかどうか
	 * @param target 比較する行列
	 * @param epsilon 誤差
	 * @return 誤差の範囲内で同じでない値であればtrueを返す
	 */
	inline bool notEpsilonEquals(const Matrix34& target, float epsilon) const{
		Assert(epsilon >= 0.f);
		return (
			(Math::abs(m00 - target.m00) > epsilon) ||
			(Math::abs(m01 - target.m01) > epsilon) ||
			(Math::abs(m02 - target.m02) > epsilon) ||
			(Math::abs(m03 - target.m03) > epsilon) ||
			(Math::abs(m10 - target.m10) > epsilon) ||
			(Math::abs(m11 - target.m11) > epsilon) ||
			(Math::abs(m12 - target.m12) > epsilon) ||
			(Math::abs(m13 - target.m13) > epsilon) ||
			(Math::abs(m20 - target.m20) > epsilon) ||
			(Math::abs(m21 - target.m21) > epsilon) ||
			(Math::abs(m22 - target.m22) > epsilon) ||
			(Math::abs(m23 - target.m23) > epsilon));
	}

	//--------------------------------------------------------------------------
	// その他
	//--------------------------------------------------------------------------
	/**
	 * 文字列化
	 * @return 行列の文字列表記
	 */
	inline String toString() const{
		String returnString;
		returnString.format(
			"{\n  ( %.8f, %.8f, %.8f, %.8f )\n  ( %.8f, %.8f, %.8f, %.8f )\n"
			"  ( %.8f, %.8f, %.8f, %.8f )\n}",
			m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23);
		return returnString;
	}

	//--------------------------------------------------------------------------
private:

};

//------------------------------------------------------------------------------
} // End of namespace Lamp
#endif // End of MATRIX43_H_
//------------------------------------------------------------------------------
