Main Page | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Namespace Members | Compound Members | File Members

# RotationInterpolationCompressor.cpp

Go to the documentation of this file.

    00001 //------------------------------------------------------------------------------
    00002 // Lamp : Open source game middleware
    00003 // Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
    00004 //
    00005 // This library is free software; you can redistribute it and/or
    00006 // modify it under the terms of the GNU Lesser General Public
    00007 // License as published by the Free Software Foundation; either
    00008 // version 2.1 of the License, or (at your option) any later version.
    00009 //
    00010 // This library is distributed in the hope that it will be useful,
    00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
    00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00013 // Lesser General Public License for more details.
    00014 //
    00015 // You should have received a copy of the GNU Lesser General Public
    00016 // License along with this library; if not, write to the Free Software
    00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    00018 //------------------------------------------------------------------------------
    00019
    00020 /** @file
    00021  * 回転補間圧縮実装
    00022  * @author Junpee
    00023  */
    00024
    00025 #include "LampBasic.h"
    00026 #include "Animation/RotationInterpolator/RotationInterpolationCompressor.h"
    00027 #include "Animation/RotationInterpolator/QuaternionArrayInterpolator.h"
    00028 #include "Animation/RotationInterpolator/EulerArrayInterpolator.h"
    00029 #include "Animation/RotationInterpolator/RotationConstantInterpolator.h"
    00030 #include "Animation/RotationInterpolator/QuaternionLinearInterpolator.h"
    00031 #include "Core/Container/Deque.h"
    00032
    00033 namespace Lamp{
    00034
    00035 //------------------------------------------------------------------------------
    00036 // 生成、破棄
    00037 //------------------------------------------------------------------------------
    00038 // コンストラクタ
    00039 RotationInterpolationCompressor::RotationInterpolationCompressor() :
    00040     tolerance_(0.f), length_(0.f), sourceKeyCount_(0), compressedKeyCount_(0),
    00041     compressedKeySize_(0){
    00042 }
    00043 //------------------------------------------------------------------------------
    00044 // デストラクタ
    00045 RotationInterpolationCompressor::~RotationInterpolationCompressor(){
    00046 }
    00047 //------------------------------------------------------------------------------
    00048 // 圧縮
    00049 //------------------------------------------------------------------------------
    00050 // 圧縮
    00051 RotationInterpolator* RotationInterpolationCompressor::compress(
    00052     EulerArrayInterpolator* source, float tolerance){
    00053     // 四元数回転配列補間に変換して圧縮する
    00054     QuaternionArrayInterpolator* interpolator =
    00055         source->convertQuaternionArrayInterpolator();
    00056     RotationInterpolator* result = compress(interpolator, tolerance);
    00057     delete interpolator;
    00058     return result;
    00059 }
    00060 //------------------------------------------------------------------------------
    00061 // 圧縮
    00062 RotationInterpolator* RotationInterpolationCompressor::compress(
    00063     QuaternionArrayInterpolator* source, float tolerance){
    00064     // 圧縮準備
    00065     compressSetup(source, tolerance);
    00066     RotationInterpolator* result = NULL;
    00067
    00068     // 定数圧縮
    00069     result = compressConstant(source);
    00070     if(result != NULL){ return result; }
    00071
    00072     // 線形圧縮
    00073     result = compressLinear(source);
    00074     if(result != NULL){ return result; }
    00075
    00076     // スプライン系の圧縮と線形圧縮の結果を重み付けした上で比較して出力可能
    00077     // その場合にsetCompressedDataを忘れないように注意
    00078
    00079     // 全ての圧縮に失敗したなら元データのコピーを返す
    00080     result = source->duplicate();
    00081     setCompressedData(getSourceKeyCount(), sourceKeySize_);
    00082
    00083     return result;
    00084 }
    00085 //------------------------------------------------------------------------------
    00086 // 圧縮準備
    00087 void RotationInterpolationCompressor::compressSetup(
    00088     QuaternionArrayInterpolator* source, float tolerance){
    00089     Assert(source != NULL);
    00090     Assert((tolerance >= 0.f) && (tolerance < Math::halfPI));
    00091     tolerance_ = tolerance;
    00092     length_ = source->getLength();
    00093     sourceKeyCount_ = source->getSize();
    00094     Assert(sourceKeyCount_ > 1);
    00095 }
    00096 //------------------------------------------------------------------------------
    00097 // 定数圧縮
    00098 RotationInterpolator* RotationInterpolationCompressor::compressConstant(
    00099     QuaternionArrayInterpolator* source){
    00100     // ターゲットとなる値の算出、最初の値を使用する
    00101     const Quaternion& targetValue = source->getValue(0);
    00102     Assert(targetValue.isUnit());
    00103
    00104     // 誤差チェック
    00105     float toleranceCos = Math::cos(getTolerance());
    00106     for(int i = 0; i < sourceKeyCount_; i++){
    00107         const Quaternion& sourceValue = source->getValue(i);
    00108         Assert(sourceValue.isUnit());
    00109         // 定数圧縮に失敗
    00110         if(targetValue.dotProduct(sourceValue) < toleranceCos){ return NULL; }
    00111     }
    00112
    00113     // 定数圧縮に成功
    00114     RotationConstantInterpolator* result = new RotationConstantInterpolator();
    00115     result->setLength(getLength());
    00116     result->setQuaternion(targetValue);
    00117     setCompressedData(1, sizeof(Quaternion) + sizeof(float));
    00118     return result;
    00119 }
    00120 //------------------------------------------------------------------------------
    00121 // 線形圧縮
    00122 RotationInterpolator* RotationInterpolationCompressor::compressLinear(
    00123     QuaternionArrayInterpolator* source){
    00124     // キーのリストを作成し、誤差が小さいキーから間引いていく
    00125
    00126     // キーリストの作成
    00127     int sourceKeyCount = getSourceKeyCount();
    00128     Deque<LinearKey> keys_(sourceKeyCount);
    00129     for(int i = 0; i < sourceKeyCount; i++){
    00130         LinearKey key;
    00131         key.value_ = source->getValue(i);
    00132         key.time_ = (float)i;
    00133         keys_.pushBack(key);
    00134     }
    00135
    00136     // 全誤差の算出
    00137     int keyCount = keys_.getCount();
    00138     int maxKeyCount = keyCount - 1;
    00139     // 最初と最後の値は操作しない
    00140     keys_[0].errorCos_ = 1.f;
    00141     keys_[maxKeyCount].errorCos_ = 1.f;
    00142     Assert(keys_[0].time_ == 0.f)
    00143     Assert(keys_[maxKeyCount].time_ == getLength());
    00144     Assert(keys_[0].value_.dotProduct(keys_[1].value_) >= 0.f);
    00145     for(int i = 1; i < maxKeyCount; i++){
    00146         LinearKey& key = keys_[i];
    00147         LinearKey& preKey = keys_[i - 1];
    00148         LinearKey& postKey = keys_[i + 1];
    00149         Assert(key.value_.dotProduct(postKey.value_) >= 0.f);
    00150         // 前のキーと後ろのキーの中間の値との誤差
    00151         Quaternion value =
    00152             Quaternion::slerp(preKey.value_, postKey.value_, 0.5f);
    00153         key.errorCos_ = key.value_.dotProduct(value);
    00154     }
    00155
    00156     // 圧縮ループ、キーが2個になったら抜ける
    00157     float toleranceCos = Math::cos(getTolerance());
    00158     while(keys_.getCount() > 2){
    00159         // 誤差が最小のキーを調べる
    00160         int targetIndex = 0;
    00161         float targetErrorCos = -1.f;
    00162         maxKeyCount = keys_.getCount() - 1;
    00163         for(int i = 1; i < maxKeyCount; i++){
    00164             // Cosがより1に近いと誤差が小さい
    00165             // 前と後ろのキーの内積が0以上
    00166 //          if(keys_[i].errorCos_ > targetErrorCos){
    00167             if((keys_[i].errorCos_ > targetErrorCos) &&
    00168                 (keys_[i - 1].value_.dotProduct(keys_[i + 1].value_) >= 0.f)){
    00169                 targetErrorCos = keys_[i].errorCos_;
    00170                 targetIndex = i;
    00171             }
    00172         }
    00173
    00174         // ターゲットが見つからなかった
    00175         if(targetIndex == 0){ break; }
    00176         // 最小誤差が許容範囲外なら圧縮ループを抜ける
    00177         if(targetErrorCos < toleranceCos){ break; }
    00178
    00179         // 最小誤差キーの削除と誤差のアップデート
    00180         keys_.remove(targetIndex);
    00181         // 前のキーが先頭キーでなければ誤差を再計算
    00182         if((targetIndex - 1) > 0){
    00183             recalcLinearError(source, keys_[targetIndex - 2],
    00184                 keys_[targetIndex - 1], keys_[targetIndex]);
    00185         }
    00186         // 後ろのキーが最後尾キーでなければ誤差を再計算
    00187         if(targetIndex < (keys_.getCount() - 1)){
    00188             recalcLinearError(source, keys_[targetIndex - 1],
    00189                 keys_[targetIndex], keys_[targetIndex + 1]);
    00190         }
    00191     }
    00192
    00193     // サイズが小さくなっていなければ失敗、失敗条件を厳しくするのもあり
    00194     keyCount = keys_.getCount();
    00195     int keySize = sizeof(Quaternion) + sizeof(float);
    00196     if((keyCount * keySize) >= getSourceSize()){ return NULL; }
    00197
    00198     // 線形圧縮に成功
    00199     QuaternionLinearInterpolator* result = new QuaternionLinearInterpolator();
    00200     result->setKeyCount(keyCount);
    00201     for(int i = 0; i < keyCount; i++){
    00202         LinearKey& key = keys_[i];
    00203         result->setKey(i, key.time_, key.value_);
    00204     }
    00205     setCompressedData(keyCount, keySize);
    00206     return result;
    00207 }
    00208 //------------------------------------------------------------------------------
    00209 // 線形圧縮誤差の再計算
    00210 void RotationInterpolationCompressor::recalcLinearError(
    00211     QuaternionArrayInterpolator* source,
    00212     LinearKey& preKey, LinearKey& key, LinearKey& postKey){
    00213     int preTime = (int)preKey.time_;
    00214     Quaternion preValue = preKey.value_;
    00215     int postTime = (int)postKey.time_;
    00216     Quaternion postValue = postKey.value_;
    00217     int timeRange = postTime - preTime;
    00218
    00219     // 最大誤差のCosを算出
    00220     float maxErrorCos = 1.f;
    00221     for(int i = 1; i < timeRange; i++){
    00222         int time = preTime + i;
    00223         Quaternion sourceValue = source->getValue(time);
    00224         float rate = (float)i / (float)timeRange;
    00225         Quaternion targetValue = Quaternion::slerp(preValue, postValue, rate);
    00226         float errorCos = sourceValue.dotProduct(targetValue);
    00227         if(errorCos < maxErrorCos){ maxErrorCos = errorCos; }
    00228     }
    00229     key.errorCos_ = maxErrorCos;
    00230 }
    00231 //------------------------------------------------------------------------------
    00232 // 圧縮結果
    00233 //------------------------------------------------------------------------------
    00234 // 結果文字列の取得
    00235 String RotationInterpolationCompressor::getResultString() const{
    00236     String result;
    00237     result.format(
    00238         "%.2f%%  Key %d/%d  Size %.2f/%.2fKb  Length %.0f",
    00239         getCompressionRate() * 100.f,
    00240         getCompressedKeyCount(), getSourceKeyCount(),
    00241         (float)getCompressedSize() / 1024.f, (float)getSourceSize() / 1024.f,
    00242         getLength());
    00243     return result;
    00244 }
    00245 //------------------------------------------------------------------------------
    00246 } // End of namespace Lamp
    00247 //------------------------------------------------------------------------------