//------------------------------------------------------------------------------
// 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
 * 変換ボーン実装
 * @author Junpee
 */

#include "System/stdafx.h"
#include "Translator/Model/TranslationBone.h"
#include "Translator/Model/TranslationCharacterModel.h"
#include "Graphics/Model/CharacterModel.h"
#include "Translator/Animation/TranslationAnimationUtility.h"
#include "Animation/VectorInterpolator/VectorArrayInterpolator.h"
#include "Animation/RotationInterpolator/EulerArrayInterpolator.h"

namespace LampForMaya{

//------------------------------------------------------------------------------
// コンストラクタ
TranslationBone::TranslationBone() :
	scaleAnimation_(NULL), rotationAnimation_(NULL),
	translationAnimation_(NULL){
}
//------------------------------------------------------------------------------
// デストラクタ
TranslationBone::~TranslationBone(){
	SafeDelete(translationAnimation_);
	SafeDelete(rotationAnimation_);
	SafeDelete(scaleAnimation_);
}
//------------------------------------------------------------------------------
// 分析
bool TranslationBone::analyze(const MDagPath& dagPath){
	MStatus result;
	// 初期化
	dagPath_ = dagPath;
	MFnDagNode dagNode(dagPath, &result);
	MayaStatusCheck(result);
	object_ = dagPath_.node(&result);
	MayaStatusCheck(result);
	MFnIkJoint joint(object_, &result);
	MayaStatusCheck(result);
	// 名前取得
	name_ = dagNode.name(&result).asChar();
	MayaStatusCheck(result);

	// 子供の取得
	u_int length = dagPath_.length(&result);
	MayaStatusCheck(result);
	MItDag childIterator(MItDag::kBreadthFirst, MFn::kInvalid, &result);
	MayaStatusCheck(result);
	result = childIterator.reset(
		dagPath_, MItDag::kBreadthFirst, MFn::kInvalid);
	MayaStatusCheck(result);
	// 自分自身を読み飛ばす
	childIterator.next();
	MDagPath childPath;
	for( ; !childIterator.isDone(); childIterator.next()){
		result = childIterator.getPath(childPath);
		MayaStatusCheck(result);
		// 一階層下以下になったらループを抜ける
		u_int childLength = childPath.length(&result);
		MayaStatusCheck(result);
		if(childLength > length + 1){ break; }
		// 有効なDagノードかチェック
		if(childPath.node().apiType() != MFn::kJoint){
			// IKエフェクタが含まれている場合がある
			continue;
		}
		// 名前の取得
		MFnDagNode childNode(childPath, &result);
		MayaStatusCheck(result);
		String childName(childNode.name(&result).asChar());
		MayaStatusCheck(result);
		bones_.add(childName);
	}

	// バインドポーズの取得
	MObject bindPoseAttribute = dagNode.attribute("bindPose", &result);
	MayaStatusCheck(result);
	MPlug bindPosePlug(object_, bindPoseAttribute);
	MPlugArray bindPosePlugArray;
	bindPosePlug.connectedTo(bindPosePlugArray, false, true);
	u_int bindPosePlugCount = bindPosePlugArray.length();
	if(bindPosePlugCount != 1){
		MayaErrorOut(String("TranslationBone::analyze() ") + 
			name_ + "に複数のポーズがバインドされています");
		return false;
	}
	u_int logicalIndex = bindPosePlugArray[0].logicalIndex(&result);
	MayaStatusCheck(result);
	MObject bindPoseObject = bindPosePlugArray[0].node(&result);
	MayaStatusCheck(result);
	MFnDependencyNode bindPose(bindPoseObject, &result);
	MayaStatusCheck(result);
	// バインドポーズ行列取得
	MPlug xformPlug = bindPose.findPlug("xformMatrix", &result);
	MayaStatusCheck(result);
	xformPlug.selectAncestorLogicalIndex(logicalIndex);
	MObject xformObject;
	MayaStatusCheck(xformPlug.getValue(xformObject));
	MFnMatrixData xformData(xformObject);
	MMatrix xform = xformData.matrix();
	Matrix44 bindPoseMatrix;
	for(int i = 0; i < 4; i++){
		for(int j = 0; j < 4; j++){
			// 転置しておく
			bindPoseMatrix.m[j][i] = (float)xform(i, j);
		}
	}
	bindPoseMatrix.invert();
	inversePoseMatrix_.set(bindPoseMatrix);

	// ジョイント回転
	MEulerRotation jointRotation;
	result = joint.getOrientation(jointRotation);
	MayaStatusCheck(result);
	if(jointRotation.order != MEulerRotation::kXYZ){
		MayaErrorOut(String("TranslationBone::analyze() "
			"ジョイント回転順序はXYZしかサポートしていません ") + name_);
		return false;
	}
	jointRotation_.set(
		(float)jointRotation.x, (float)jointRotation.y, (float)jointRotation.z);

	// スケール
	double scale[3];
	result = joint.getScale(scale);
	MayaStatusCheck(result);
	scale_.set((float)scale[0], (float)scale[1], (float)scale[2]);

	// 回転
	MEulerRotation rotation;
	result = joint.getRotation(rotation);
	MayaStatusCheck(result);
	if(rotation.order != MEulerRotation::kXYZ){
		MayaErrorOut(String("TranslationBone::analyze() "
			"回転順序はXYZしかサポートしていません ") + name_);
		return false;
	}
	rotation_.set((float)rotation.x, (float)rotation.y, (float)rotation.z);
	originalRotation_ = rotation_;

	// ジョイント回転補正、アニメーション分析後に行う必要がある
	Matrix33 rotationMatrix;
	rotationMatrix.setRotationXYZ(originalRotation_);
	rotationMatrix.addRotationXYZ(jointRotation_);
	if(!rotationMatrix.getRotationXYZ(&rotation_)){
		MayaMessageOut(String("TranslationBone::convertToLamp() "
			"ジョイント回転補正の解が一つでは有りません") + name_);
	}

	// 移動
	MVector translation = joint.translation(MSpace::kTransform, &result);
	MayaStatusCheck(result);
	translation_.set(
		(float)translation.x, (float)translation.y, (float)translation.z);
/*
	DebugOut("\n%s\n", name_.getBytes());
	DebugOut("bindPoseMatrix %s\n", bindPoseMatrix_.toString().getBytes());
	DebugOut("inverseBindPoseMatrix %s\n", inverseBindPoseMatrix_.toString().getBytes());
	DebugOut("jointRotation %s\n", jointRotation_.toString().getBytes());
	DebugOut("scale %s\n", scale_.toString().getBytes());
	DebugOut("rotation %s\n", rotation_.toString().getBytes());
	DebugOut("translation %s\n", translation_.toString().getBytes());
	DebugOut("bones %d  ", bones_.getCount());
	for(int i = 0; i < bones_.getCount(); i++){
		DebugOut(" %s", bones_.get(i).getBytes());
	}
	DebugOut("\n");
//*/
	return true;
}
//------------------------------------------------------------------------------
// モデル行列の構築
void TranslationBone::buildModelMatrix(
	TranslationCharacterModel* characterModel, const Matrix34& parentMatrix){
	inversePoseMatrix_ = inversePoseMatrix_ * parentMatrix;
	for(int i = 0; i < bones_.getCount(); i++){
		TranslationBone* bone = characterModel->searchBone(bones_.get(i));
		bone->buildModelMatrix(characterModel, inversePoseMatrix_);
	}
}
//------------------------------------------------------------------------------
// アニメーションの分析
bool TranslationBone::analyzeAnimation(int startTime, int endTime){
	// スケールアニメーション
	scaleAnimation_ =
		TranslationAnimationUtility::analyzeVectorAnimation(
		object_, "scale", scale_, startTime, endTime);
	// 回転アニメーション
	rotationAnimation_ =
		TranslationAnimationUtility::analyzeRotationAnimation(
		object_, "rotate", originalRotation_, startTime, endTime);
	// ジョイント回転補正
	if(rotationAnimation_ != NULL){
		int size = rotationAnimation_->getSize();
		for(int i = 0; i < size; i++){
			Vector3 rotation = rotationAnimation_->getValue(i);
			Matrix33 rotationMatrix;
			rotationMatrix.setRotationXYZ(rotation);
			rotationMatrix.addRotationXYZ(jointRotation_);
			if(!rotationMatrix.getRotationXYZ(&rotation)){
				MayaMessageOut(String("TranslationBone::analyzeAnimation() "
					"ジョイント回転補正の解が一つでは有りません") + name_);
			}
			rotationAnimation_->setValue(i, rotation);
		}
	}
	// 移動アニメーション
	translationAnimation_ =
		TranslationAnimationUtility::analyzeVectorAnimation(
		object_, "translate", translation_, startTime, endTime);
	return true;
}
//------------------------------------------------------------------------------
// Lampへの変換
bool TranslationBone::convertToLamp(CharacterModel* model){
	// 変換
	Bone* bone = model->createBone(name_);
	bone->setInversePoseMatrix(inversePoseMatrix_);
	bone->setScale(scale_);
	bone->setRotationXYZ(rotation_);
	bone->setTranslation(translation_);
	return true;
}
//------------------------------------------------------------------------------
// ボーンのリンク
bool TranslationBone::boneLink(CharacterModel* model, int index){
	Bone* bone = model->getBone(index);
	for(int i = 0; i < bones_.getCount(); i++){
		Bone* child = model->searchBone(bones_.get(i));
		if(child == NULL){
			MayaErrorOut(String("TranslationBone::boneLink() "
				"ボーンが見つかりません ") + bones_.get(i));
			return false;
		}
		bone->addBone(child);
	}
	return true;
}
//------------------------------------------------------------------------------
// スケールアニメーションの取得
VectorInterpolator* TranslationBone::getScaleAnimation(
	int startTime, int endTime){
	if(scaleAnimation_ == NULL){ return NULL; }
	int size = endTime - startTime + 1;
	VectorArrayInterpolator* array = new VectorArrayInterpolator();
	array->setSize(size);
	for(int i = 0; i < size; i++){
		array->setValue(i, scaleAnimation_->getValue(startTime + i));
	}
	return array;
}
//------------------------------------------------------------------------------
// 回転アニメーションの取得
RotationInterpolator* TranslationBone::getRotationAnimation(
	int startTime, int endTime){
	if(rotationAnimation_ == NULL){ return NULL; }
	int size = endTime - startTime + 1;
	EulerArrayInterpolator* array = new EulerArrayInterpolator();
	array->setSize(size);
	for(int i = 0; i < size; i++){
		array->setValue(i, rotationAnimation_->getValue(startTime + i));
	}
	return array;
}
//------------------------------------------------------------------------------
// 移動アニメーションの取得
VectorInterpolator* TranslationBone::getTranslationAnimation(
	int startTime, int endTime){
	if(translationAnimation_ == NULL){ return NULL; }
	int size = endTime - startTime + 1;
	VectorArrayInterpolator* array = new VectorArrayInterpolator();
	array->setSize(size);
	for(int i = 0; i < size; i++){
		array->setValue(i, translationAnimation_->getValue(startTime + i));
	}
	return array;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
