//------------------------------------------------------------------------------
// 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 "LampBasic.h"
#include "Animation/Utility/AnimationCompressor.h"
#include "Animation/VectorInterpolator/VectorInterpolationCompressor.h"
#include "Animation/RotationInterpolator/RotationInterpolationCompressor.h"
#include "Animation/RotationInterpolator/QuaternionArrayInterpolator.h"
#include "Animation/RotationInterpolator/EulerArrayInterpolator.h"
#include "Animation/System/AnimationSet.h"
#include "Animation/Camera/CameraAnimation.h"
#include "Animation/SceneNode/SceneNodeAnimation.h"
#include "Animation/Model/CharacterModelAnimation.h"

namespace Lamp{

//------------------------------------------------------------------------------
// コンストラクタ
AnimationCompressor::AnimationCompressor() :
	scaleTolerance_(0.01f), rotationTolerance_(Math::PI * 0.01f),
	translationTolerance_(0.1f), scaleCompressor_(NULL),
	rotationCompressor_(NULL), translationCompressor_(NULL){
}
//------------------------------------------------------------------------------
// デストラクタ
AnimationCompressor::~AnimationCompressor(){
	Assert(scaleCompressor_ == NULL);
	Assert(rotationCompressor_ == NULL);
	Assert(translationCompressor_ == NULL);
}
//------------------------------------------------------------------------------
// 圧縮
//------------------------------------------------------------------------------
// 圧縮
void AnimationCompressor::compress(Animation* animation){
	// 前処理
	scaleCompressor_ = new VectorInterpolationCompressor();
	rotationCompressor_ = new RotationInterpolationCompressor();
	translationCompressor_ = new VectorInterpolationCompressor();
	// 圧縮
	compressAnimation(animation);
	// 後始末
	SafeDelete(translationCompressor_);
	SafeDelete(rotationCompressor_);
	SafeDelete(scaleCompressor_);
}
//------------------------------------------------------------------------------
// アニメーションの圧縮
void AnimationCompressor::compressAnimation(Animation* animation){
	if(animation->isAnimationSet()){
		compressAnimationSet(animation->castAnimationSet());
	}else if(animation->isCameraAnimation()){
		compressCameraAnimation(animation->castCameraAnimation());
	}else if(animation->isSceneNodeAnimation()){
		compressSceneNodeAnimation(animation->castSceneNodeAnimation());
	}else if(animation->isCharacterModelAnimation()){
		compressCharacterModelAnimation(
			animation->castCharacterModelAnimation());
	}else{ Assert(false); }
}
//------------------------------------------------------------------------------
// アニメーションセットの圧縮
void AnimationCompressor::compressAnimationSet(AnimationSet* animation){
	Assert(animation != NULL);
	int count = animation->getAnimationCount();
	for(int i = 0; i < count; i++){
		compressAnimation(animation->getAnimation(i));
	}
}
//------------------------------------------------------------------------------
// カメラアニメーションの圧縮
void AnimationCompressor::compressCameraAnimation(CameraAnimation* animation){
	Assert(animation != NULL);
	CameraAnimationData* data = animation->getCameraAnimationData();
	int sequenceCount = data->getSequenceCount();
	for(int i = 0; i < sequenceCount; i++){
		data->setRotation(i, compressRotation(data->getRotation(i)));
		data->setTranslation(i, compressTranslation(data->getTranslation(i)));
	}
}
//------------------------------------------------------------------------------
// シーンノードアニメーションの圧縮
void AnimationCompressor::compressSceneNodeAnimation(
	SceneNodeAnimation* animation){
	Assert(animation != NULL);
	SceneNodeAnimationData* data = animation->getSceneNodeAnimationData();
	int sequenceCount = data->getSequenceCount();
	for(int i = 0; i < sequenceCount; i++){
		data->setScale(i, compressScale(data->getScale(i)));
		data->setRotation(i, compressRotation(data->getRotation(i)));
		data->setTranslation(i, compressTranslation(data->getTranslation(i)));
	}
}
//------------------------------------------------------------------------------
// キャラクタモデルアニメーションの圧縮
void AnimationCompressor::compressCharacterModelAnimation(
	CharacterModelAnimation* animation){
	Assert(animation != NULL);
	CharacterModelAnimationData* data =
		animation->getCharacterModelAnimationData();
	int sequenceCount = data->getSequenceCount();
	int boneCount = data->getBoneCount();
	for(int i = 0; i < sequenceCount; i++){
		for(int j = 0; j < boneCount; j++){
			data->setScale(i, j, compressScale(data->getScale(i, j)));
			data->setRotation(i, j, compressRotation(data->getRotation(i, j)));
			data->setTranslation(
				i, j, compressTranslation(data->getTranslation(i, j)));
		}
	}
}
//------------------------------------------------------------------------------
// ユーティリティ
//------------------------------------------------------------------------------
// スケールの圧縮
VectorInterpolator* AnimationCompressor::compressScale(
	VectorInterpolator* interpolator){
	// NULLならNULLを返す
	if(interpolator == NULL){ return NULL; }
	VectorArrayInterpolator* array =
		interpolator->castVectorArrayInterpolator();
	// 配列補間でなければ複製を返す
	if(array == NULL){ return interpolator->duplicate(); }
	float maxValue = interpolator->getBoundingBox().getSize().maximumValue();
	return scaleCompressor_->compress(array, scaleTolerance_);
}
//------------------------------------------------------------------------------
// 回転の圧縮
RotationInterpolator* AnimationCompressor::compressRotation(
	RotationInterpolator* interpolator){
	// NULLならNULLを返す
	if(interpolator == NULL){ return NULL; }
	QuaternionArrayInterpolator* array =
		interpolator->castQuaternionArrayInterpolator();
	if(array == NULL){
		EulerArrayInterpolator* eulerArray =
			interpolator->castEulerArrayInterpolator();
		// 配列補間でなければ複製を返す
		if(eulerArray == NULL){ return interpolator->duplicate(); }
		array = eulerArray->convertQuaternionArrayInterpolator();
	}
	return rotationCompressor_->compress(array, rotationTolerance_);
}
//------------------------------------------------------------------------------
// 移動の圧縮
VectorInterpolator* AnimationCompressor::compressTranslation(
	VectorInterpolator* interpolator){
	// NULLならNULLを返す
	if(interpolator == NULL){ return NULL; }
	VectorArrayInterpolator* array =
		interpolator->castVectorArrayInterpolator();
	// 配列補間でなければ複製を返す
	if(array == NULL){ return interpolator->duplicate(); }
	return translationCompressor_->compress(array, translationTolerance_);
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
