Fix bug where xform fields that were not specified for motion interpolation were still getting interpolated.

Carry logic from this fix to writing Xform Xmls as well.

Motion was not supported on some Xform fields as it was originally copied from flam3. Allow it on all but xaos now.

Fix clamping range for m_ColorSpeed at the end of ApplyMotion() to match the values we accept elsewhere.
This commit is contained in:
mfeemster 2015-03-25 20:47:57 -07:00
parent 53db8907ff
commit 3d206c1d22
7 changed files with 107 additions and 50 deletions

View File

@ -157,6 +157,21 @@ bool Affine2D<T>::IsZero() const
(IsClose<T>(F(), 0));
}
/// <summary>
/// Determine whether this affine transform was deliberately set to all empty values.
/// </summary>
/// <returns>True if all 6 elements equal zero, else false.</returns>
template <typename T>
bool Affine2D<T>::IsEmpty() const
{
return (IsClose<T>(A(), EMPTYFIELD)) &&
(IsClose<T>(B(), EMPTYFIELD)) &&
(IsClose<T>(C(), EMPTYFIELD)) &&
(IsClose<T>(D(), EMPTYFIELD)) &&
(IsClose<T>(E(), EMPTYFIELD)) &&
(IsClose<T>(F(), EMPTYFIELD));
}
/// <summary>
/// Rotate this affine transform around its origin by the specified angle in degrees.
/// </summary>

View File

@ -74,6 +74,7 @@ public:
void MakeID();
bool IsID() const;
bool IsZero() const;
bool IsEmpty() const;
void Rotate(T angle);
void Translate(const v2T& v);
void RotateScaleXTo(const v2T& v);

View File

@ -74,6 +74,7 @@ namespace EmberNs
#define TMAX std::numeric_limits<T>::max()
#define FLOAT_MAX_TAN 8388607.0f
#define FLOAT_MIN_TAN -FLOAT_MAX_TAN
#define EMPTYFIELD -9999
typedef std::chrono::high_resolution_clock Clock;
#ifndef byte

View File

@ -493,14 +493,14 @@ private:
os << " <xform weight=\"" << xform.m_Weight << "\" ";
}
if (!doMotion || xform.m_ColorX != EMPTYFIELD) os << "color=\"" << xform.m_ColorX << "\" ";
//if (!doMotion || xform.m_ColorY != EMPTYFIELD) os << "color=\"" << xform.m_ColorX << " " << xform.m_ColorY << "\" ";
if (!doMotion || xform.m_DirectColor != EMPTYFIELD) os << "var_color=\"" << xform.m_DirectColor << "\" ";
if (!doMotion || xform.m_ColorSpeed != EMPTYFIELD) os << "color_speed=\"" << xform.m_ColorSpeed << "\" ";
//os << "symmetry=\"" << fabs(xform.m_ColorSpeed - 1) * 2 << "\" ";//Legacy support.
if (!doMotion)
{
os << "color=\"" << xform.m_ColorX << "\" ";
//os << "color=\"" << xform.m_ColorX << " " << xform.m_ColorY << "\" ";
os << "var_color=\"" << xform.m_DirectColor << "\" ";
os << "color_speed=\"" << xform.m_ColorSpeed << "\" ";
//os << "symmetry=\"" << fabs(xform.m_ColorSpeed - 1) * 2 << "\" ";//Legacy support.
string s = xform.m_Name;
std::replace(s.begin(), s.end(), ' ', '_');
@ -536,20 +536,20 @@ private:
}
}
if (!doMotion || (doMotion && !xform.m_Affine.IsZero()))
if (!doMotion || (doMotion && !xform.m_Affine.IsZero() && !xform.m_Affine.IsEmpty()))
{
os << "coefs=\"" << xform.m_Affine.A() << " " << xform.m_Affine.D() << " " << xform.m_Affine.B() << " "
<< xform.m_Affine.E() << " " << xform.m_Affine.C() << " " << xform.m_Affine.F() << "\"";
}
if ((!doMotion && !xform.m_Post.IsID()) || (doMotion && !xform.m_Post.IsZero()))
if ((!doMotion && !xform.m_Post.IsID()) || (doMotion && !xform.m_Post.IsZero() && !xform.m_Post.IsEmpty()))
{
os << " post=\"" << xform.m_Post.A() << " " << xform.m_Post.D() << " " << xform.m_Post.B() << " "
<< xform.m_Post.E() << " " << xform.m_Post.C() << " " << xform.m_Post.F() << "\"";
}
//Original only printed xaos values that were not 1. Here, print them all out if any are present.
if (!isFinal && !doMotion && xform.XaosPresent())
if (!isFinal && !doMotion && xform.XaosPresent())//Applying motion to xaos not supported.
{
os << " chaos=\"";
@ -559,8 +559,7 @@ private:
os << "\"";
}
if (!doMotion)
os << " opacity=\"" << xform.m_Opacity << "\"";
if (!doMotion || xform.m_Opacity != EMPTYFIELD) os << " opacity=\"" << xform.m_Opacity << "\"";
if (!doMotion && !xform.m_Motion.empty())
{

View File

@ -32,12 +32,13 @@ class EMBER_API Xform
{
public:
/// <summary>
/// Default constructor which calls Init() to set default values.
/// Pre and post affine are defaulted to the identity matrix.
/// Default constructor which calls Init() to set default or out of bounds values.
/// When useDefaults is true, Pre and post affine are defaulted to the identity matrix.
/// </summary>
Xform()
/// <param name="useDefaults">Use reasonable default if true, else use out of bounds values.</param>
Xform(bool useDefaults = true)
{
Init();
Init(useDefaults);
}
/// <summary>
@ -196,36 +197,73 @@ public:
/// <summary>
/// Init default values.
/// Non default values are used to signify an uninitialized state. This is useful for
/// doing motion interpolation where we don't want to apply motion to all fields. By setting
/// unreasonable values before parsing, then only assigning the ones the motion tags specified,
/// it is clear which fields are intended to have motion applied to them.
/// </summary>
void Init()
/// <param name="useDefaults">Use reasonable default if true, else use out of bounds values.</param>
void Init(bool useDefaults = true)
{
static size_t count = 0;
m_Weight = 0;
m_ColorSpeed = T(0.5);
m_Animate = 1;
m_ColorX = T(count & 1);
m_ColorY = 0;
m_DirectColor = 1;
m_Opacity = 1;
if (useDefaults)
{
m_Weight = 0;
m_ColorSpeed = T(0.5);
m_Animate = 1;
m_ColorX = T(count & 1);
m_ColorY = 0;
m_DirectColor = 1;
m_Opacity = 1;
m_Affine.A(1);
m_Affine.B(0);
m_Affine.C(0);
m_Affine.D(0);
m_Affine.E(1);
m_Affine.F(0);
m_Affine.A(1);
m_Affine.B(0);
m_Affine.C(0);
m_Affine.D(0);
m_Affine.E(1);
m_Affine.F(0);
m_Post.A(1);
m_Post.B(0);
m_Post.C(0);
m_Post.D(0);
m_Post.E(1);
m_Post.F(0);
m_Post.A(1);
m_Post.B(0);
m_Post.C(0);
m_Post.D(0);
m_Post.E(1);
m_Post.F(0);
m_Wind[0] = 0;
m_Wind[1] = 0;
m_MotionFreq = 0;
}
else
{
m_Weight = EMPTYFIELD;
m_ColorSpeed = EMPTYFIELD;
m_Animate = EMPTYFIELD;
m_ColorX = EMPTYFIELD;
m_ColorY = EMPTYFIELD;
m_DirectColor = EMPTYFIELD;
m_Opacity = EMPTYFIELD;
m_Affine.A(EMPTYFIELD);
m_Affine.B(EMPTYFIELD);
m_Affine.C(EMPTYFIELD);
m_Affine.D(EMPTYFIELD);
m_Affine.E(EMPTYFIELD);
m_Affine.F(EMPTYFIELD);
m_Post.A(EMPTYFIELD);
m_Post.B(EMPTYFIELD);
m_Post.C(EMPTYFIELD);
m_Post.D(EMPTYFIELD);
m_Post.E(EMPTYFIELD);
m_Post.F(EMPTYFIELD);
m_Wind[0] = EMPTYFIELD;
m_Wind[1] = EMPTYFIELD;
m_MotionFreq = EMPTYFIELD;
}
m_Wind[0] = 0;
m_Wind[1] = 0;
m_MotionFreq = 0;
m_MotionFunc = MOTION_SIN;
m_Motion.clear();
@ -674,7 +712,12 @@ public:
}
//Why are we not using template with member var addr as arg here?//TODO
#define APPMOT(x) do { x += mot[i].x * Interpolater<T>::MotionFuncs(func, freq * blend); } while (0)
#define APPMOT(x) \
do \
{ \
if (currentMot.x != EMPTYFIELD) \
x += currentMot.x * Interpolater<T>::MotionFuncs(func, freq * blend); \
} while (0)
/// <summary>
/// Apply the motion functions from the passed in xform to this xform.
@ -683,15 +726,13 @@ public:
/// <param name="blend">The time blending value 0-1</param>
void ApplyMotion(Xform<T>& xform, T blend)
{
Xform<T>* mot = xform.m_Motion.data();
//Loop over the motion elements and add their contribution to the original vals.
for (size_t i = 0; i < xform.m_Motion.size(); i++)
{
//Original only pulls these from the first motion xform which is a bug. Want to pull it from each one.
Xform<T>* currentMot = &xform.m_Motion[i];
intmax_t freq = currentMot->m_MotionFreq;
eMotion func = currentMot->m_MotionFunc;
Xform<T>& currentMot = xform.m_Motion[i];
intmax_t freq = currentMot.m_MotionFreq;
eMotion func = currentMot.m_MotionFunc;
//Clamp these to the appropriate range after all are applied.
APPMOT(m_Weight);
@ -702,9 +743,9 @@ public:
APPMOT(m_ColorSpeed);
APPMOT(m_Animate);
for (size_t j = 0; j < currentMot->TotalVariationCount(); j++)//For each variation in the motion xform.
for (size_t j = 0; j < currentMot.TotalVariationCount(); j++)//For each variation in the motion xform.
{
Variation<T>* motVar = currentMot->GetVariation(j);//Get the variation, which may or may not be present in this xform.
Variation<T>* motVar = currentMot.GetVariation(j);//Get the variation, which may or may not be present in this xform.
ParametricVariation<T>* motParVar = dynamic_cast<ParametricVariation<T>*>(motVar);
Variation<T>* var = GetVariationById(motVar->VariationId());//See if the variation in the motion xform was present in the xform.
@ -752,7 +793,7 @@ public:
//ClampRef<T>(m_ColorY, 0, 1);
ClampRef<T>(m_DirectColor, 0, 1);
ClampRef<T>(m_Opacity, 0, 1);//Original didn't clamp these, but do it here for correctness.
ClampRef<T>(m_ColorSpeed, 0, 1);
ClampRef<T>(m_ColorSpeed, -1, 1);
ClampGte0Ref<T>(m_Weight);
}

View File

@ -1018,7 +1018,7 @@ private:
{
if (!Compare(motionNode->name, "motion"))
{
Xform<T> xform;
Xform<T> xform(false);//Will only have valid values in fields parsed for motion, all others will be EMPTYFIELD.
if (!ParseXform(motionNode, xform, true))
m_ErrorReport.push_back(string(loc) + " : Error parsing motion xform");

View File

@ -1956,12 +1956,12 @@ int _tmain(int argc, _TCHAR* argv[])
//std::complex<double> cd, cd2;
//cd2 = sin(cd);
/*
t.Tic();
TestCasting();
t.Toc("TestCasting()");
t.Tic();
/*t.Tic();
VariationList<float> vlf;
t.Toc("Creating VariationList<float>");