Merge branch 'motion-goodies' of https://github.com/gh2k/fractorium

This commit is contained in:
mfeemster 2015-07-06 20:22:52 -07:00
commit 3a9ce0928f
11 changed files with 454 additions and 22 deletions

View File

@ -52,5 +52,6 @@ HEADERS += \
../../../Source/Ember/Variations05.h \
../../../Source/Ember/VariationsDC.h \
../../../Source/Ember/Xform.h \
../../../Source/Ember/XmlToEmber.h
../../../Source/Ember/XmlToEmber.h \
../../../Source/Ember/FlameMotion.h

View File

@ -5,6 +5,7 @@
#include "PaletteList.h"
#include "SpatialFilter.h"
#include "TemporalFilter.h"
#include "FlameMotion.h"
/// <summary>
/// Ember class.
@ -183,6 +184,11 @@ public:
if (ember.m_Edits != nullptr)
m_Edits = xmlCopyDoc(ember.m_Edits, 1);
m_FlameMotionElements.clear();
for(int i = 0; i < ember.m_FlameMotionElements.size(); ++i)
m_FlameMotionElements.push_back(ember.m_FlameMotionElements[i]);
return *this;
}
@ -472,6 +478,8 @@ public:
{
for (size_t i = 0; i < TotalXformCount(); i++)
GetTotalXform(i)->DeleteMotionElements();
m_FlameMotionElements.clear();
}
/// <summary>
@ -1285,6 +1293,82 @@ public:
point.m_Z -= m_CamZPos;
}
#define APP_FMP(x) x += param.second * Interpolater<T>::MotionFuncs(motion.m_MotionFunc, motion.m_MotionFreq * (blend + motion.m_MotionOffset))
/// <summary>
/// Update ember parameters based on stored motion elements
/// </summary>
/// <param name="blend">The time percentage value which dictates how much of a percentage of 360 degrees it should be rotated and the time position for the motion elements</param>
void ApplyFlameMotion(T blend)
{
for (size_t i = 0; i < m_FlameMotionElements.size(); ++i)
{
const FlameMotion<T> &motion = m_FlameMotionElements[i];
for (size_t j = 0; j < motion.m_MotionParams.size(); ++j)
{
const pair<eFlameMotionParam, T> &param = motion.m_MotionParams[j];
switch (param.first)
{
case FLAME_MOTION_ZOOM:
APP_FMP(m_Zoom);
break;
case FLAME_MOTION_ZPOS:
APP_FMP(m_CamZPos);
break;
case FLAME_MOTION_PERSPECTIVE:
APP_FMP(m_CamPerspective);
break;
case FLAME_MOTION_YAW:
APP_FMP(m_CamYaw);
break;
case FLAME_MOTION_PITCH:
APP_FMP(m_CamPitch);
break;
case FLAME_MOTION_DEPTH_BLUR:
APP_FMP(m_CamDepthBlur);
break;
case FLAME_MOTION_CENTER_X:
APP_FMP(m_CenterX);
break;
case FLAME_MOTION_CENTER_Y:
APP_FMP(m_CenterY);
break;
case FLAME_MOTION_ROTATE:
APP_FMP(m_Rotate);
break;
case FLAME_MOTION_HUE:
APP_FMP(m_Hue);
break;
case FLAME_MOTION_BRIGHTNESS:
APP_FMP(m_Brightness);
break;
case FLAME_MOTION_GAMMA:
APP_FMP(m_Gamma);
break;
case FLAME_MOTION_GAMMA_THRESH:
APP_FMP(m_GammaThresh);
break;
case FLAME_MOTION_HIGHLIGHT_POWER:
APP_FMP(m_HighlightPower);
break;
case FLAME_MOTION_BACKGROUND_R:
APP_FMP(m_Background.r);
break;
case FLAME_MOTION_BACKGROUND_G:
APP_FMP(m_Background.g);
break;
case FLAME_MOTION_BACKGROUND_B:
APP_FMP(m_Background.b);
break;
case FLAME_MOTION_VIBRANCY:
APP_FMP(m_Vibrancy);
break;
}
}
}
}
/// <summary>
/// Clear this ember and set to either reasonable or unreasonable default values.
/// </summary>
@ -1683,6 +1767,9 @@ public:
//The 0-based position of this ember in the file it was contained in.
size_t m_Index;
//The list of motion elements for the top-level flame params
vector<FlameMotion<T>> m_FlameMotionElements;
private:
/// <summary>
/// The type of scaling used when resizing.

View File

@ -114,10 +114,16 @@ enum eInterp : uint { EMBER_INTERP_LINEAR = 0, EMBER_INTERP_SMOOTH = 1 };
enum eAffineInterp : uint { INTERP_LINEAR = 0, INTERP_LOG = 1, INTERP_COMPAT = 2, INTERP_OLDER = 3 };
enum ePaletteMode : uint { PALETTE_STEP = 0, PALETTE_LINEAR = 1 };
enum ePaletteInterp : uint { INTERP_HSV = 0, INTERP_SWEEP = 1 };
enum eMotion : uint { MOTION_SIN = 1, MOTION_TRIANGLE = 2, MOTION_HILL = 3 };
enum eMotion : uint { MOTION_SIN = 1, MOTION_TRIANGLE = 2, MOTION_HILL = 3, MOTION_SAW = 4 };
enum eProcessAction : uint { NOTHING = 0, ACCUM_ONLY = 1, FILTER_AND_ACCUM = 2, KEEP_ITERATING = 3, FULL_RENDER = 4 };
enum eProcessState : uint { NONE = 0, ITER_STARTED = 1, ITER_DONE = 2, FILTER_DONE = 3, ACCUM_DONE = 4 };
enum eInteractiveFilter : uint { FILTER_LOG = 0, FILTER_DE = 1 };
enum eScaleType : uint { SCALE_NONE = 0, SCALE_WIDTH = 1, SCALE_HEIGHT = 2 };
enum eRenderStatus : uint { RENDER_OK = 0, RENDER_ERROR = 1, RENDER_ABORT = 2 };
enum eFlameMotionParam : uint {
FLAME_MOTION_NONE = 0, FLAME_MOTION_ZOOM = 1, FLAME_MOTION_ZPOS = 2, FLAME_MOTION_PERSPECTIVE = 3, FLAME_MOTION_YAW = 4, FLAME_MOTION_PITCH = 5, FLAME_MOTION_DEPTH_BLUR = 6,
FLAME_MOTION_CENTER_X = 7, FLAME_MOTION_CENTER_Y = 8, FLAME_MOTION_ROTATE = 9, FLAME_MOTION_HUE = 10, FLAME_MOTION_BRIGHTNESS = 11, FLAME_MOTION_GAMMA = 12,
FLAME_MOTION_GAMMA_THRESH = 13, FLAME_MOTION_HIGHLIGHT_POWER = 14, FLAME_MOTION_BACKGROUND_R = 15, FLAME_MOTION_BACKGROUND_G = 16,
FLAME_MOTION_BACKGROUND_B = 17, FLAME_MOTION_VIBRANCY = 18
};
}

View File

@ -221,6 +221,9 @@ public:
os << "\">\n";
for (i = 0; i < ember.m_FlameMotionElements.size(); ++i)
os << " " << ToString(ember.m_FlameMotionElements[i]);
//This is a grey area, what to do about symmetry to avoid duplicating the symmetry xforms when reading back?//TODO//BUG.
//if (ember.m_Symmetry)
// os << " <symmetry kind=\"" << ember.m_Symmetry << "\"/>\n";
@ -488,6 +491,11 @@ private:
os << "motion_function=\"triangle\" ";
else if (xform.m_MotionFunc== MOTION_HILL)
os << "motion_function=\"hill\" ";
else if (xform.m_MotionFunc== MOTION_SAW)
os << "motion_function=\"saw\" ";
if (xform.m_MotionOffset != 0)
os << "motion_offset=\"" << xform.m_MotionOffset << "\" ";
}
else
{
@ -717,6 +725,113 @@ private:
return os.str();
}
/// <summary>
/// Convert a FlameMotion element to an xml string
/// </summary>
/// <param name="motion">The FlameMotion object to convert to XML</param>
string ToString(const FlameMotion<T> &motion)
{
ostringstream os;
os << "<flame_motion motion_frequency=\"" << motion.m_MotionFreq << "\" ";
if (motion.m_MotionOffset > 0)
os << "motion_offset=\"" << motion.m_MotionOffset << "\" ";
os << "motion_func=";
switch(motion.m_MotionFunc)
{
case MOTION_SIN:
os << "\"sin\"";
break;
case MOTION_HILL:
os << "\"hill\"";
break;
case MOTION_TRIANGLE:
os << "\"triangle\"";
break;
case MOTION_SAW:
os << "\"saw\"";
break;
}
T r = 0.0;
T g = 0.0;
T b = 0.0;
T cx = 0.0;
T cy = 0.0;
for(int i = 0; i < motion.m_MotionParams.size(); ++i)
{
switch(motion.m_MotionParams[i].first)
{
case FLAME_MOTION_ZOOM:
os << " zoom=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_ZPOS:
os << " cam_zpos=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_PERSPECTIVE:
os << " cam_persp=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_YAW:
os << " cam_yaw=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_PITCH:
os << " cam_pitch=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_DEPTH_BLUR:
os << " cam_dof=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_CENTER_X:
cx = motion.m_MotionParams[i].second;
break;
case FLAME_MOTION_CENTER_Y:
cy = motion.m_MotionParams[i].second;
break;
case FLAME_MOTION_ROTATE:
os << " rotate=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_HUE:
os << " hue=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_BRIGHTNESS:
os << " brightness=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_GAMMA:
os << " gamma=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_GAMMA_THRESH:
os << " gamma_threshold=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_HIGHLIGHT_POWER:
os << " highlight_power=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_BACKGROUND_R:
r = motion.m_MotionParams[i].second;
break;
case FLAME_MOTION_BACKGROUND_G:
g = motion.m_MotionParams[i].second;
break;
case FLAME_MOTION_BACKGROUND_B:
b = motion.m_MotionParams[i].second;
break;
case FLAME_MOTION_VIBRANCY:
os << " vibrancy=\"" << motion.m_MotionParams[i].second << "\"";
break;
}
}
if ( r != 0.0 || g != 0.0 || b != 0.0 )
os << " background=\"" << r << " " << g << " " << b << "\"";
if ( cx != 0.0 || cy != 0.0 )
os << " center=\"" << cx << " " << cy << "\"";
os << "/>\n";
return os.str();
}
void AddFilenameWithoutAmpersand(xmlNodePtr node, string& filename)
{
if (filename.find_first_of('&') != std::string::npos)

View File

@ -0,0 +1,66 @@
#pragma once
#include "EmberDefines.h"
namespace EmberNs
{
/// <summary>
/// FlameMotion elements allow for motion of the flame parameters such as zoom, yaw, pitch and friends
/// The values in these elements can be used to modify flame parameters during rotation in much the same
/// way as motion elements on xforms do.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class EMBER_API FlameMotion
{
public:
FlameMotion()
{
m_MotionFreq = 0;
m_MotionFunc = MOTION_SIN;
m_MotionOffset = 0;
}
FlameMotion(const FlameMotion<T> &other)
{
operator=<T>(other);
}
template <typename U>
FlameMotion(const FlameMotion<U> &other)
{
operator=<U>(other);
}
FlameMotion<T>& operator = (const FlameMotion<T>& other)
{
if (this != &other)
FlameMotion<T>::operator=<T>(other);
return *this;
}
template <typename U>
FlameMotion &operator = (const FlameMotion<U> &other)
{
m_MotionParams.clear();
for (int i = 0; i < other.m_MotionParams.size(); ++i)
m_MotionParams.push_back(pair<eFlameMotionParam, T>(other.m_MotionParams[i].first, T(other.m_MotionParams[i].second)));
m_MotionFunc = other.m_MotionFunc;
m_MotionFreq = T(other.m_MotionFreq);
m_MotionOffset = T(other.m_MotionOffset);
return *this;
}
T m_MotionFreq;
T m_MotionOffset;
eMotion m_MotionFunc;
vector<pair<eFlameMotionParam, T>> m_MotionParams;
};
}

View File

@ -883,10 +883,14 @@ public:
return fr;
}
else//MOTION_HILL
else if (funcNum == MOTION_HILL)
{
return ((1 - cos(T(2.0) * T(M_PI) * timeVal)) * T(0.5));
}
else //MOTION_SAW
{
return (T(2.0) * fmod(timeVal - T(0.5), T(1.0)) - T(1.0));
}
}
/*

View File

@ -1010,11 +1010,13 @@ public:
if (!xform1->m_Motion.empty())
xform2->ApplyMotion(*xform1, blend);
xform2->DeleteMotionElements();
}
rotated.ApplyFlameMotion(blend);
rotated.RotateAffines(-blend * 360);//Rotate the affines.
rotated.DeleteMotionElements(); // delete all motion elements from the looped flame
}
/// <summary>

View File

@ -165,6 +165,7 @@ public:
m_Wind[1] = T(xform.m_Wind[1]);
m_MotionFreq = xform.m_MotionFreq;
m_MotionFunc = xform.m_MotionFunc;
m_MotionOffset = xform.m_MotionOffset;
ClearAndDeleteVariations();
@ -234,6 +235,7 @@ public:
m_Wind[0] = 0;
m_Wind[1] = 0;
m_MotionFreq = 0;
m_MotionOffset = 0;
}
else
{
@ -262,6 +264,7 @@ public:
m_Wind[0] = EMPTYFIELD;
m_Wind[1] = EMPTYFIELD;
m_MotionFreq = EMPTYFIELD;
m_MotionOffset = EMPTYFIELD;
}
m_MotionFunc = MOTION_SIN;
@ -716,7 +719,7 @@ public:
do \
{ \
if (currentMot.x != EMPTYFIELD) \
x += currentMot.x * Interpolater<T>::MotionFuncs(func, freq * blend); \
x += currentMot.x * Interpolater<T>::MotionFuncs(func, freq * (blend + offset)); \
} while (0)
/// <summary>
@ -731,8 +734,9 @@ public:
{
//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;
T freq = currentMot.m_MotionFreq;
eMotion func = currentMot.m_MotionFunc;
T offset = currentMot.m_MotionOffset;
//Clamp these to the appropriate range after all are applied.
APPMOT(m_Weight);
@ -753,13 +757,13 @@ public:
if (!var)//It wasn't present, so add it and set the weight.
{
Variation<T>* newVar = motVar->Copy();
newVar->m_Weight = motVar->m_Weight * Interpolater<T>::MotionFuncs(func, freq * blend);
newVar->m_Weight = motVar->m_Weight * Interpolater<T>::MotionFuncs(func, freq * (blend + offset));
AddVariation(newVar);
var = newVar;//Use this below for params.
}
else//It was present, so apply the motion func to the weight.
{
var->m_Weight += motVar->m_Weight * Interpolater<T>::MotionFuncs(func, freq * blend);
var->m_Weight += motVar->m_Weight * Interpolater<T>::MotionFuncs(func, freq * (blend + offset));
}
//At this point, we've added if needed, or just applied the motion func to the weight.
@ -773,7 +777,7 @@ public:
for (size_t k = 0; k < motParVar->ParamCount(); k++)
{
if (!motParams[k].IsPrecalc())
*(params[k].Param()) += motParams[k].ParamVal() * Interpolater<T>::MotionFuncs(func, freq * blend);
*(params[k].Param()) += motParams[k].ParamVal() * Interpolater<T>::MotionFuncs(func, freq * (blend + offset));
}
}
}
@ -1157,6 +1161,7 @@ public:
ss << "Wind: " << m_Wind[0] << ", " << m_Wind[1] << endl;
ss << "Motion Frequency: " << m_MotionFreq << endl;
ss << "Motion Func: " << m_MotionFunc << endl;
ss << "Motion Offset: " << m_MotionOffset << endl;
const_cast<Xform<T>*>(this)->AllVarsFunc([&] (vector<Variation<T>*>& variations, bool& keepGoing)
{
@ -1232,7 +1237,8 @@ public:
T m_Animate;//Whether or not this xform rotates during animation. 0 means stationary, > 0 means rotate. Use T instead of bool so it can be interpolated.
T m_Wind[2];
eMotion m_MotionFunc;
intmax_t m_MotionFreq;
T m_MotionFreq;
T m_MotionOffset;
vector<Xform<T>> m_Motion;
string m_Name;

View File

@ -978,7 +978,7 @@ private:
if (theXform)
{
//Check for non-zero motion params.
if (theXform->m_MotionFreq != 0)//Original checked for motion func being non-zero, but it was set to MOTION_SIN (1) in Xform::Init(), so don't check for 0 here.
if (abs(theXform->m_MotionFreq) > 0.0)//Original checked for motion func being non-zero, but it was set to MOTION_SIN (1) in Xform::Init(), so don't check for 0 here.
{
m_ErrorReport.push_back(string(loc) + " : Motion parameters should not be specified in regular, non-motion xforms");
}
@ -1005,6 +1005,113 @@ private:
editNode = xmlCopyNode(childNode, 1);
xmlDocSetRootElement(currentEmber.m_Edits, editNode);
}
else if (!Compare(childNode->name, "flame_motion"))
{
FlameMotion<T> motion;
att = childNode->properties;
if (att == nullptr)
{
m_ErrorReport.push_back(string(loc) + " : <flame_motion> element has no attributes");
return false;
}
for (curAtt = att; curAtt; curAtt = curAtt->next)
{
attStr = reinterpret_cast<char*>(xmlGetProp(childNode, curAtt->name));
if (ParseAndAssignFloat(curAtt->name, attStr, "motion_frequency", motion.m_MotionFreq, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "motion_offset", motion.m_MotionOffset, ret)) { }
else if (!Compare(curAtt->name, "motion_function"))
{
string func(attStr);
if ( func == "sin" )
motion.m_MotionFunc = MOTION_SIN;
else if ( func == "triangle" )
motion.m_MotionFunc = MOTION_TRIANGLE;
else if ( func == "hill" )
motion.m_MotionFunc = MOTION_HILL;
else if ( func == "saw" )
motion.m_MotionFunc = MOTION_SAW;
else
{
m_ErrorReport.push_back(string(loc) + " : invalid flame motion function " + func);
return false;
}
}
else if (!Compare(curAtt->name, "zoom"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_ZOOM, motion);
else if (!Compare(curAtt->name, "cam_zpos"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_ZPOS, motion);
else if (!Compare(curAtt->name, "cam_persp"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_PERSPECTIVE, motion);
else if (!Compare(curAtt->name, "cam_yaw"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_YAW, motion);
else if (!Compare(curAtt->name, "cam_pitch"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_PITCH, motion);
else if (!Compare(curAtt->name, "cam_dof"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_DEPTH_BLUR, motion);
else if (!Compare(curAtt->name, "rotate"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_ROTATE, motion);
else if (!Compare(curAtt->name, "hue"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_HUE, motion);
else if (!Compare(curAtt->name, "brightness"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_BRIGHTNESS, motion);
else if (!Compare(curAtt->name, "gamma"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_GAMMA, motion);
else if (!Compare(curAtt->name, "gamma_threshold"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_GAMMA_THRESH, motion);
else if (!Compare(curAtt->name, "highlight_power"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_HIGHLIGHT_POWER, motion);
else if (!Compare(curAtt->name, "vibrancy"))
ret = ret && AttToFlameMotionFloat(att, attStr, FLAME_MOTION_VIBRANCY, motion);
else if (!Compare(curAtt->name, "background"))
{
double r, g, b;
if (sscanf_s(attStr, "%lf %lf %lf", &r, &g, &b) != 3)
{
m_ErrorReport.push_back(string(loc) + " : Invalid flame motion background attribute " + string(attStr));
xmlFree(attStr);
return false;
}
if (r != 0)
motion.m_MotionParams.push_back(pair<eFlameMotionParam, T>(FLAME_MOTION_BACKGROUND_R, T(r)));
if (g != 0)
motion.m_MotionParams.push_back(pair<eFlameMotionParam, T>(FLAME_MOTION_BACKGROUND_G, T(g)));
if (b != 0)
motion.m_MotionParams.push_back(pair<eFlameMotionParam, T>(FLAME_MOTION_BACKGROUND_B, T(b)));
}
else if (!Compare(curAtt->name, "center"))
{
double cx, cy;
if (sscanf_s(attStr, "%lf %lf", &cx, &cy) != 2)
{
m_ErrorReport.push_back(string(loc) + " : Invalid flame motion center attribute " + string(attStr));
xmlFree(attStr);
return false;
}
if (cx != 0)
motion.m_MotionParams.push_back(pair<eFlameMotionParam, T>(FLAME_MOTION_CENTER_X, T(cx)));
if (cy != 0)
motion.m_MotionParams.push_back(pair<eFlameMotionParam, T>(FLAME_MOTION_CENTER_Y, T(cy)));
}
else
{
m_ErrorReport.push_back(string(loc) + " : Unknown flame motion attribute " + string(CCX(curAtt->name)));
xmlFree(attStr);
return false;
}
xmlFree(attStr);
}
currentEmber.m_FlameMotionElements.push_back(motion);
}
}
//if (!newLinear)
@ -1017,6 +1124,32 @@ private:
return m_ErrorReport.empty();
}
/// <summary>
/// Parse a floating point value from an xml attribute and add the value to a FlameMotion object
/// </summary>
/// <param name="att">The current attribute</param>
/// <param name="attStr">The attribute value to parse</param>
/// <param name="param">The flame motion parameter type</param>
/// <param name="motion">The flame motion element to add the parameter to</param>
/// <returns>True if there were no errors, else false.</returns>
bool AttToFlameMotionFloat(xmlAttrPtr att, const char *attStr, eFlameMotionParam param, FlameMotion<T> &motion)
{
const char* loc = __FUNCTION__;
bool r = false;
T val = 0.0;
if (Atof(attStr, val))
{
motion.m_MotionParams.push_back(pair<eFlameMotionParam, T>(param, val));
r = true;
} else {
m_ErrorReport.push_back(string(loc) + " : Failed to parse float value for flame motion attribute \"" + string(CCX(att->name)) + "\" : " + string(attStr) + "");
}
return r;
}
/// <summary>
/// Parse an xform element.
/// </summary>
@ -1054,9 +1187,8 @@ private:
else if (ParseAndAssignFloat(curAtt->name, attStr, "animate", xform.m_Animate, success)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "opacity", xform.m_Opacity, success)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "var_color", xform.m_DirectColor, success)) { }
//Parse simple int reads.
else if (ParseAndAssignInt(curAtt->name, attStr, "motion_frequency", xform.m_MotionFreq, success)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "motion_frequency", xform.m_MotionFreq, success)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "motion_offset", xform.m_MotionOffset, success)) { }
//Parse more complicated reads that have multiple possible values.
else if (!Compare(curAtt->name, "name"))
@ -1080,6 +1212,8 @@ private:
xform.m_MotionFunc = MOTION_TRIANGLE;
else if (!_stricmp("hill", attStr))
xform.m_MotionFunc = MOTION_HILL;
else if (!_stricmp("saw", attStr))
xform.m_MotionFunc = MOTION_SAW;
else
{
xform.m_MotionFunc = MOTION_SIN;

View File

@ -76,7 +76,6 @@ enum eOptionIDs
OPT_SYMMETRY,
OPT_SHEEP_GEN,
OPT_SHEEP_ID,
OPT_LOOPS,
OPT_REPEAT,
OPT_TRIES,
OPT_MAX_XFORMS,
@ -93,6 +92,7 @@ enum eOptionIDs
OPT_OFFSETX,
OPT_OFFSETY,
OPT_USEMEM,
OPT_LOOPS,
OPT_ISAAC_SEED,//String value args.
OPT_IN,
@ -357,7 +357,6 @@ public:
INITUINTOPTION(Frame, Eou(OPT_ANIM_GENOME, OPT_FRAME, _T("--frame"), 0, SO_REQ_SEP, "\t--frame=<val> Synonym for \"time\".\n"));
INITUINTOPTION(Dtime, Eou(OPT_USE_ANIMATE, OPT_DTIME, _T("--dtime"), 1, SO_REQ_SEP, "\t--dtime=<val> Time between frames [default: 1].\n"));
INITUINTOPTION(Frames, Eou(OPT_USE_GENOME, OPT_NFRAMES, _T("--nframes"), 20, SO_REQ_SEP, "\t--nframes=<val> Number of frames for each stage of the animation [default: 20].\n"));
INITUINTOPTION(Loops, Eou(OPT_USE_GENOME, OPT_LOOPS, _T("--loops"), 1, SO_REQ_SEP, "\t--loops=<val> Number of times to rotate each control point in sequence [default: 1].\n"));
INITUINTOPTION(Repeat, Eou(OPT_USE_GENOME, OPT_REPEAT, _T("--repeat"), 1, SO_REQ_SEP, "\t--repeat=<val> Number of new flames to create. Ignored if sequence, inter or rotate were specified [default: 1].\n"));
INITUINTOPTION(Tries, Eou(OPT_USE_GENOME, OPT_TRIES, _T("--tries"), 10, SO_REQ_SEP, "\t--tries=<val> Number times to try creating a flame that meets the specified constraints. Ignored if sequence, inter or rotate were specified [default: 10].\n"));
INITUINTOPTION(MaxXforms, Eou(OPT_USE_GENOME, OPT_MAX_XFORMS, _T("--maxxforms"), UINT_MAX, SO_REQ_SEP, "\t--maxxforms=<val> The maximum number of xforms allowed in the final output.\n"));
@ -376,6 +375,7 @@ public:
INITDOUBLEOPTION(OffsetX, Eod(OPT_USE_GENOME, OPT_OFFSETX, _T("--offsetx"), 0.0, SO_REQ_SEP, "\t--offsetx=<val> Amount to jitter each flame horizontally when applying genome tools [default: 0].\n"));
INITDOUBLEOPTION(OffsetY, Eod(OPT_USE_GENOME, OPT_OFFSETY, _T("--offsety"), 0.0, SO_REQ_SEP, "\t--offsety=<val> Amount to jitter each flame vertically when applying genome tools [default: 0].\n"));
INITDOUBLEOPTION(UseMem, Eod(OPT_USE_RENDER, OPT_USEMEM, _T("--use_mem"), 0.0, SO_REQ_SEP, "\t--use_mem=<val> Number of bytes of memory to use [default: max system memory].\n"));
INITDOUBLEOPTION(Loops, Eod(OPT_USE_GENOME, OPT_LOOPS, _T("--loops"), 1, SO_REQ_SEP, "\t--loops=<val> Number of times to rotate each control point in sequence [default: 1].\n"));
//String.
INITSTRINGOPTION(IsaacSeed, Eos(OPT_USE_ALL, OPT_ISAAC_SEED, _T("--isaac_seed"), "", SO_REQ_SEP, "\t--isaac_seed=<val> Character-based seed for the random number generator [default: random].\n"));
@ -487,7 +487,6 @@ public:
PARSEUINTOPTION(OPT_TIME, Time);
PARSEUINTOPTION(OPT_DTIME, Dtime);
PARSEUINTOPTION(OPT_NFRAMES, Frames);
PARSEUINTOPTION(OPT_LOOPS, Loops);
PARSEUINTOPTION(OPT_REPEAT, Repeat);
PARSEUINTOPTION(OPT_TRIES, Tries);
PARSEUINTOPTION(OPT_MAX_XFORMS, MaxXforms);
@ -503,6 +502,7 @@ public:
PARSEDOUBLEOPTION(OPT_OFFSETX, OffsetX);
PARSEDOUBLEOPTION(OPT_OFFSETY, OffsetY);
PARSEDOUBLEOPTION(OPT_USEMEM, UseMem);
PARSEDOUBLEOPTION(OPT_LOOPS, Loops);
PARSESTRINGOPTION(OPT_ISAAC_SEED, IsaacSeed);//String args.
PARSESTRINGOPTION(OPT_IN, Input);
@ -703,7 +703,6 @@ public:
EmberOptionEntry<uint> Time;
EmberOptionEntry<uint> Dtime;
EmberOptionEntry<uint> Frames;
EmberOptionEntry<uint> Loops;
EmberOptionEntry<uint> Repeat;
EmberOptionEntry<uint> Tries;
EmberOptionEntry<uint> MaxXforms;
@ -719,6 +718,7 @@ public:
EmberOptionEntry<double> OffsetX;
EmberOptionEntry<double> OffsetY;
EmberOptionEntry<double> UseMem;
EmberOptionEntry<double> Loops;
EmberOptionEntry<string> IsaacSeed;//Value string.
EmberOptionEntry<string> Input;

View File

@ -382,9 +382,9 @@ bool EmberGenome(EmberOptions& opt)
for (i = 0; i < embers.size(); i++)
{
if (opt.Loops())
if (opt.Loops() > 0)
{
for (frame = 0; frame < opt.Frames(); frame++)
for (frame = 0; frame < round(T(opt.Frames()) * opt.Loops()); frame++)
{
blend = T(frame) / T(opt.Frames());
tools.Spin(embers[i], pTemplate, result, frameCount++, blend);//Result is cleared and reassigned each time inside of Spin().
@ -394,12 +394,23 @@ bool EmberGenome(EmberOptions& opt)
if (i < embers.size() - 1)
{
vector<Ember<T>> interpEmbers;
interpEmbers.push_back(embers[i]);
interpEmbers.push_back(embers[i + 1]);
if (opt.Loops() > 0)
{
// we might have looped a non-integral number of times, so store the last result as our flame to interpolate from
interpEmbers[i] = result;
}
for (frame = 0; frame < opt.Frames(); frame++)
{
seqFlag = (frame == 0 || frame == opt.Frames() - 1);
blend = frame / T(opt.Frames());
result.Clear();
tools.SpinInter(&embers[i], pTemplate, result, frameCount++, seqFlag, blend);
tools.SpinInter(&interpEmbers[i], pTemplate, result, frameCount++, seqFlag, blend);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), false, opt.HexPalette());
}
}