/* Copyright (C) 2005-2007 Feeling Software Inc. Portions of the code are: Copyright (C) 2005-2007 Sony Computer Entertainment America MIT License: http://www.opensource.org/licenses/mit-license.php */ #include "StdAfx.h" #include "FCDocument/FCDAnimationCurve.h" #include "FCDocument/FCDAnimationChannel.h" #include "FCDocument/FCDAnimationCurveTools.h" #include "FCDocument/FCDAnimationKey.h" #include "FCDocument/FCDAnimationMultiCurve.h" #define SMALL_DELTA 0.001f namespace FCDAnimationCurveTools { // Non-standard constructor used to merge together animation curves FCDAnimationMultiCurve* MergeCurves(const FCDAnimationCurveConstList& toMerge, const FloatList& defaultValues) { size_t dimension = toMerge.size(); if (dimension == 0) return NULL; // Look for the document pointer and select the parent curve. FCDocument* document = NULL; int32 targetElement = -1; FUDaeInfinity::Infinity preInfinity = FUDaeInfinity::CONSTANT, postInfinity = FUDaeInfinity::CONSTANT; for (size_t i = 0; i < dimension; ++i) { if (toMerge[i] != NULL) { document = const_cast(toMerge[i]->GetDocument()); targetElement = toMerge[i]->GetTargetElement(); preInfinity = toMerge[i]->GetPreInfinity(); postInfinity = toMerge[i]->GetPostInfinity(); break; } } if (document == NULL) return NULL; // Allocate the output multiCurve. FCDAnimationMultiCurve* multiCurve = new FCDAnimationMultiCurve(document, (uint32) dimension); multiCurve->SetTargetElement(targetElement); multiCurve->SetPreInfinity(preInfinity); multiCurve->SetPostInfinity(postInfinity); // Calculate the merged input keys and their wanted interpolations. FloatList mergedInputs; UInt32List mergedInterpolations; for (size_t i = 0; i < dimension; ++i) { const FCDAnimationCurve* curve = toMerge[i]; if (curve == NULL) continue; const FCDAnimationKey** curveKeys = curve->GetKeys(); // Merge each curve's keys, which should already be sorted, into the multi-curve's size_t multiCurveKeyCount = mergedInputs.size(), m = 0; size_t curveKeyCount = curve->GetKeyCount(), c = 0; while (m < multiCurveKeyCount && c < curveKeyCount) { if (IsEquivalent(mergedInputs[m], curveKeys[c]->input)) { if (mergedInterpolations[m] != curveKeys[c]->interpolation) mergedInterpolations[m] = FUDaeInterpolation::BEZIER; ++c; ++m; } else if (mergedInputs[m] < curveKeys[c]->input) { ++m; } else { // Insert this new key within the merged list. mergedInputs.insert(mergedInputs.begin() + m, curveKeys[c]->input); mergedInterpolations.insert(mergedInterpolations.begin() + m, curveKeys[c]->interpolation); ++multiCurveKeyCount; ++m; ++c; } } while (c < curveKeyCount) { // Insert all these extra keys at the end of the merged list. mergedInputs.push_back(curveKeys[c]->input); mergedInterpolations.push_back(curveKeys[c]->interpolation); ++c; } } size_t keyCount = mergedInputs.size(); // Create the multi-dimensional keys. for (size_t i = 0; i < keyCount; ++i) { FCDAnimationMKey* key = multiCurve->AddKey((FUDaeInterpolation::Interpolation) mergedInterpolations[i]); key->input = mergedInputs[i]; } FCDAnimationMKey** keys = multiCurve->GetKeys(); // Merge the curves one by one into the multi-curve for (size_t i = 0; i < dimension; ++i) { const FCDAnimationCurve* curve = toMerge[i]; if (curve == NULL || curve->GetKeyCount() == 0) { // No curve, or an empty curve, set the default value on all the keys float defaultValue = (i < defaultValues.size()) ? defaultValues[i] : 0.0f; for (size_t k = 0; k < keyCount; ++k) { keys[k]->output[i] = defaultValue; if (keys[k]->interpolation == FUDaeInterpolation::BEZIER) { float previousSpan = (k > 0 ? mergedInputs[k] - mergedInputs[k - 1] : 1.0f) / 3.0f; float nextSpan = (k < keyCount - 1 ? mergedInputs[k + 1] - mergedInputs[k] : 1.0f) / 3.0f; FCDAnimationMKeyBezier* bkey = (FCDAnimationMKeyBezier*) keys[k]; bkey->inTangent[i] = FMVector2(keys[k]->input - previousSpan, defaultValue); bkey->outTangent[i] = FMVector2(keys[k]->input + nextSpan, defaultValue); } } continue; } // Merge in this curve's values, sampling when the multi-curve's key is not present in the curve. // Calculate and retrieve the tangents in a polar-like form. const FCDAnimationKey** curveKeys = curve->GetKeys(); size_t curveKeyCount = curve->GetKeyCount(); for (size_t k = 0, c = 0; k < keyCount; ++k) { float input = keys[k]->input; float previousSpan = (k > 0 ? input - keys[k - 1]->input : 1.0f) / 3.0f; float nextSpan = (k < keyCount - 1 ? keys[k + 1]->input - input : 1.0f) / 3.0f; if (c >= curveKeyCount || !IsEquivalent(keys[k]->input, curveKeys[c]->input)) { // Sample the curve float value = keys[k]->output[i] = curve->Evaluate(input); if (keys[k]->interpolation == FUDaeInterpolation::BEZIER) { // Calculate the slope at the sampled point. // Since the curve should be smooth: the in/out tangents should be equal. FCDAnimationMKeyBezier* bkey = (FCDAnimationMKeyBezier*) keys[k]; float slope = (value - curve->Evaluate(input - SMALL_DELTA)) / SMALL_DELTA; bkey->inTangent[i] = FMVector2(input - previousSpan, value - slope * previousSpan); bkey->outTangent[i] = FMVector2(input + nextSpan, value + slope * nextSpan); } else if (keys[k]->interpolation == FUDaeInterpolation::TCB) { // Don't fool around: just set default values. FCDAnimationMKeyTCB* tkey = (FCDAnimationMKeyTCB*) keys[k]; tkey->tension[i] = tkey->continuity[i] = tkey->bias[i] = 0.5f; tkey->easeIn[i] = tkey->easeOut[i] = 0.0f; } } else { // Keys match, grab the value directly keys[k]->output[i] = curveKeys[c]->output; // Check the wanted interpolation type to retrieve/calculate the extra necessary information. if (keys[k]->interpolation == FUDaeInterpolation::BEZIER) { float oldPreviousSpan = (c > 0 ? curveKeys[c]->input - curveKeys[c - 1]->input : 1.0f) / 3.0f; float oldNextSpan = (c < curveKeyCount - 1 ? curveKeys[c + 1]->input - curveKeys[c]->input : 1.0f) / 3.0f; // Calculate the new tangent: keep the slope proportional FCDAnimationMKeyBezier* bkey = (FCDAnimationMKeyBezier*) keys[k]; if (curveKeys[c]->interpolation == FUDaeInterpolation::BEZIER) { FCDAnimationKeyBezier* bkey2 = (FCDAnimationKeyBezier*) curveKeys[c]; FMVector2 absolute(bkey->input, bkey->output[i]); bkey->inTangent[i] = absolute + (bkey2->inTangent - absolute) / oldPreviousSpan * previousSpan; bkey->outTangent[i] = absolute + (bkey2->outTangent - absolute) / oldNextSpan * nextSpan; } else { // Default to flat tangents. bkey->inTangent[i] = FMVector2(bkey->input - previousSpan, bkey->output[i]); bkey->outTangent[i] = FMVector2(bkey->input + nextSpan, bkey->output[i]); } } else if (keys[k]->interpolation == FUDaeInterpolation::TCB) { FCDAnimationMKeyTCB* tkey = (FCDAnimationMKeyTCB*) keys[k]; if (curveKeys[c]->interpolation == FUDaeInterpolation::TCB) { FCDAnimationKeyTCB* tkey2 = (FCDAnimationKeyTCB*) curveKeys[c]; tkey->tension[i] = tkey2->tension; tkey->continuity[i] = tkey2->continuity; tkey->bias[i] = tkey2->bias; tkey->easeIn[i] = tkey2->easeIn; tkey->easeOut[i] = tkey2->easeOut; } else { // Default to flat values. tkey->tension[i] = tkey->continuity[i] = tkey->bias[i] = 0.5f; tkey->easeIn[i] = tkey->easeOut[i] = 0.0f; } } // Go to the next existing key in the 1D curve. ++c; } } } return multiCurve; } // Collapse this multi-dimensional curve into a one-dimensional curve, given a collapsing function FCDAnimationCurve* Collapse(const FCDAnimationMultiCurve* curve, FCDCollapsingFunction collapse) { size_t keyCount = curve->GetKeyCount(); size_t dimension = curve->GetDimension(); if (keyCount == 0 || dimension== 0) return NULL; if (collapse == NULL) collapse = Average; const FCDAnimationMKey** inKeys = curve->GetKeys(); // Create the output one-dimensional curve and create the keys. FCDAnimationCurve* out = new FCDAnimationCurve(const_cast(curve->GetDocument()), NULL); for (size_t i = 0; i < keyCount; ++i) { out->AddKey((FUDaeInterpolation::Interpolation) inKeys[i]->interpolation); } FCDAnimationKey** outKeys = out->GetKeys(); // Copy the key data over, collapsing the values float* buffer = new float[dimension]; for (size_t i = 0; i < keyCount; ++i) { outKeys[i]->input = inKeys[i]->input; // Collapse the values and the tangents for (uint32 j = 0; j < dimension; ++j) buffer[j] = inKeys[i]->output[j]; outKeys[i]->output = (*collapse)(buffer, (uint32) dimension); if (outKeys[i]->interpolation == FUDaeInterpolation::BEZIER) { FCDAnimationMKeyBezier* inBKey = (FCDAnimationMKeyBezier*) inKeys + i; FCDAnimationKeyBezier* outBKey = (FCDAnimationKeyBezier*) outKeys + i; for (uint32 j = 0; j < dimension; ++j) buffer[j] = inBKey->inTangent[j].v; outBKey->inTangent = FMVector2(inBKey->inTangent[0].u, (*collapse)(buffer, (uint32) dimension)); for (uint32 j = 0; j < dimension; ++j) buffer[j] = inBKey->outTangent[j].v; outBKey->outTangent = FMVector2(inBKey->outTangent[0].u, (*collapse)(buffer, (uint32) dimension)); } } SAFE_DELETE_ARRAY(buffer); return out; } float TakeFirst(float* values, uint32 count) { return (count > 0) ? *values : 0.0f; } float Average(float* values, uint32 count) { float v = 0.0f; for (uint32 i = 0; i < count; ++i) v += values[i]; v /= float(count); return v; } };