From 1a37504b0956ee428516c67383ebb21ce504d32d Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Mon, 22 Jun 2015 14:50:43 +0100 Subject: [PATCH 1/7] Add 'saw' motion function --- Source/Ember/EmberDefines.h | 2 +- Source/Ember/EmberToXml.h | 2 ++ Source/Ember/Interpolate.h | 6 +++++- Source/Ember/XmlToEmber.h | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/Ember/EmberDefines.h b/Source/Ember/EmberDefines.h index 534f3a6..cd619be 100644 --- a/Source/Ember/EmberDefines.h +++ b/Source/Ember/EmberDefines.h @@ -114,7 +114,7 @@ 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 }; diff --git a/Source/Ember/EmberToXml.h b/Source/Ember/EmberToXml.h index 9d6cbf1..b1febdd 100644 --- a/Source/Ember/EmberToXml.h +++ b/Source/Ember/EmberToXml.h @@ -488,6 +488,8 @@ 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\" "; } else { diff --git a/Source/Ember/Interpolate.h b/Source/Ember/Interpolate.h index 23b16d2..950ca86 100644 --- a/Source/Ember/Interpolate.h +++ b/Source/Ember/Interpolate.h @@ -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)); + } } /* diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 07ea62f..0a92ab4 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -1073,6 +1073,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; From d75a15136d6214d698c8c00deca47250c2537431 Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Wed, 24 Jun 2015 10:52:21 +0100 Subject: [PATCH 2/7] Convert motion_frequency to floating point --- Source/Ember/Xform.h | 4 ++-- Source/Ember/XmlToEmber.h | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Ember/Xform.h b/Source/Ember/Xform.h index 4e94319..cffc862 100644 --- a/Source/Ember/Xform.h +++ b/Source/Ember/Xform.h @@ -731,7 +731,7 @@ public: { //Original only pulls these from the first motion xform which is a bug. Want to pull it from each one. Xform& currentMot = xform.m_Motion[i]; - intmax_t freq = currentMot.m_MotionFreq; + T freq = currentMot.m_MotionFreq; eMotion func = currentMot.m_MotionFunc; //Clamp these to the appropriate range after all are applied. @@ -1232,7 +1232,7 @@ 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; vector> m_Motion; string m_Name; diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 0a92ab4..8293598 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -971,7 +971,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"); } @@ -1047,9 +1047,7 @@ 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)) { } //Parse more complicated reads that have multiple possible values. else if (!Compare(curAtt->name, "name")) From 9d5f3e85784dd804ad371708e1a4dd25124cfefc Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Wed, 24 Jun 2015 11:23:17 +0100 Subject: [PATCH 3/7] Add new class to store flame motion parameters --- Builds/QtCreator/Ember/Ember.pro | 3 +- Source/Ember/Ember.h | 9 +++++ Source/Ember/EmberDefines.h | 6 +++ Source/Ember/FlameMotion.h | 63 ++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 Source/Ember/FlameMotion.h diff --git a/Builds/QtCreator/Ember/Ember.pro b/Builds/QtCreator/Ember/Ember.pro index 58cf2c0..b0e57e1 100644 --- a/Builds/QtCreator/Ember/Ember.pro +++ b/Builds/QtCreator/Ember/Ember.pro @@ -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 diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index da0726e..06426ce 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -5,6 +5,7 @@ #include "PaletteList.h" #include "SpatialFilter.h" #include "TemporalFilter.h" +#include "FlameMotion.h" /// /// 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; } @@ -1683,6 +1689,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> m_FlameMotionElements; + private: /// /// The type of scaling used when resizing. diff --git a/Source/Ember/EmberDefines.h b/Source/Ember/EmberDefines.h index cd619be..92e69ae 100644 --- a/Source/Ember/EmberDefines.h +++ b/Source/Ember/EmberDefines.h @@ -120,4 +120,10 @@ enum eProcessState : uint { NONE = 0, ITER_STARTED = 1, ITER_DONE = 2, FILTER_DO 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 +}; } diff --git a/Source/Ember/FlameMotion.h b/Source/Ember/FlameMotion.h new file mode 100644 index 0000000..6700f42 --- /dev/null +++ b/Source/Ember/FlameMotion.h @@ -0,0 +1,63 @@ +#pragma once + +#include "EmberDefines.h" + +namespace EmberNs +{ + +/// +/// 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. +/// +template +class EMBER_API FlameMotion +{ +public: + + FlameMotion() + { + m_MotionFreq = 0; + m_MotionFunc = MOTION_SIN; + } + + FlameMotion(const FlameMotion &other) + { + operator=(other); + } + + template + FlameMotion(const FlameMotion &other) + { + operator=(other); + } + + FlameMotion& operator = (const FlameMotion& other) + { + if (this != &other) + FlameMotion::operator=(other); + + return *this; + } + + template + FlameMotion &operator = (const FlameMotion &other) + { + m_MotionParams.clear(); + + for (int i = 0; i < other.m_MotionParams.size(); ++i) + m_MotionParams.push_back(pair(other.m_MotionParams[i].first, T(other.m_MotionParams[i].second))); + + m_MotionFunc = other.m_MotionFunc; + m_MotionFreq = T(other.m_MotionFreq); + return *this; + } + + T m_MotionFreq; + eMotion m_MotionFunc; + vector> m_MotionParams; + +}; + +} From 679a8b4129041171d76096d947043cdc654777b4 Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Fri, 26 Jun 2015 21:23:27 +0100 Subject: [PATCH 4/7] Serialise FlameMotion elements to/from XML --- Source/Ember/EmberToXml.h | 107 ++++++++++++++++++++++++++++++ Source/Ember/XmlToEmber.h | 132 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) diff --git a/Source/Ember/EmberToXml.h b/Source/Ember/EmberToXml.h index b1febdd..45a2810 100644 --- a/Source/Ember/EmberToXml.h +++ b/Source/Ember/EmberToXml.h @@ -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 << " \n"; @@ -719,6 +722,110 @@ private: return os.str(); } + /// + /// Convert a FlameMotion element to an xml string + /// + /// The FlameMotion object to convert to XML + string ToString(const FlameMotion &motion) + { + ostringstream os; + os << "\n"; + + return os.str(); + } + void AddFilenameWithoutAmpersand(xmlNodePtr node, string& filename) { if (filename.find_first_of('&') != std::string::npos) diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 8293598..290b43e 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -998,6 +998,112 @@ private: editNode = xmlCopyNode(childNode, 1); xmlDocSetRootElement(currentEmber.m_Edits, editNode); } + else if (!Compare(childNode->name, "flame_motion")) + { + FlameMotion motion; + + att = childNode->properties; + + if (att == nullptr) + { + m_ErrorReport.push_back(string(loc) + " : element has no attributes"); + return false; + } + + for (curAtt = att; curAtt; curAtt = curAtt->next) + { + attStr = reinterpret_cast(xmlGetProp(childNode, curAtt->name)); + + if (ParseAndAssignFloat(curAtt->name, attStr, "motion_frequency", motion.m_MotionFreq, 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(FLAME_MOTION_BACKGROUND_R, T(r))); + if (g != 0) + motion.m_MotionParams.push_back(pair(FLAME_MOTION_BACKGROUND_G, T(g))); + if (b != 0) + motion.m_MotionParams.push_back(pair(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(FLAME_MOTION_CENTER_X, T(cx))); + if (cy != 0) + motion.m_MotionParams.push_back(pair(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) @@ -1010,6 +1116,32 @@ private: return m_ErrorReport.empty(); } + /// + /// Parse a floating point value from an xml attribute and add the value to a FlameMotion object + /// + /// The current attribute + /// The attribute value to parse + /// The flame motion parameter type + /// The flame motion element to add the parameter to + /// True if there were no errors, else false. + bool AttToFlameMotionFloat(xmlAttrPtr att, const char *attStr, eFlameMotionParam param, FlameMotion &motion) + { + const char* loc = __FUNCTION__; + + bool r = false; + T val = 0.0; + + if (Atof(attStr, val)) + { + motion.m_MotionParams.push_back(pair(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; + } + /// /// Parse an xform element. /// From f606986b7d4e3396343899412d944214973d4caf Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Thu, 2 Jul 2015 18:01:37 +0100 Subject: [PATCH 5/7] Apply flame motion elements when generating flame sequence for animation --- Source/Ember/Ember.h | 78 +++++++++++++++++++++++++++++++++++++++ Source/Ember/SheepTools.h | 6 ++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index 06426ce..de18c0f 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -478,6 +478,8 @@ public: { for (size_t i = 0; i < TotalXformCount(); i++) GetTotalXform(i)->DeleteMotionElements(); + + m_FlameMotionElements.clear(); } /// @@ -1291,6 +1293,82 @@ public: point.m_Z -= m_CamZPos; } +#define APP_FMP(x) x += param.second * Interpolater::MotionFuncs(motion.m_MotionFunc, motion.m_MotionFreq * blend) + /// + /// Update ember parameters based on stored motion elements + /// + /// 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 + void ApplyFlameMotion(T blend) + { + for (size_t i = 0; i < m_FlameMotionElements.size(); ++i) + { + const FlameMotion &motion = m_FlameMotionElements[i]; + + for (size_t j = 0; j < motion.m_MotionParams.size(); ++j) + { + const pair ¶m = 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; + } + } + } + } + /// /// Clear this ember and set to either reasonable or unreasonable default values. /// diff --git a/Source/Ember/SheepTools.h b/Source/Ember/SheepTools.h index d1d8c58..b01ac14 100644 --- a/Source/Ember/SheepTools.h +++ b/Source/Ember/SheepTools.h @@ -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 } /// From efb39f81601230883c499288ed139298f4167b7b Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Mon, 6 Jul 2015 12:06:20 +0100 Subject: [PATCH 6/7] Make 'loops' param to EmberGenome work correcly, and convert to double --- Source/EmberCommon/EmberOptions.h | 8 ++++---- Source/EmberGenome/EmberGenome.cpp | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Source/EmberCommon/EmberOptions.h b/Source/EmberCommon/EmberOptions.h index 047af08..fc7e453 100644 --- a/Source/EmberCommon/EmberOptions.h +++ b/Source/EmberCommon/EmberOptions.h @@ -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= Synonym for \"time\".\n")); INITUINTOPTION(Dtime, Eou(OPT_USE_ANIMATE, OPT_DTIME, _T("--dtime"), 1, SO_REQ_SEP, "\t--dtime= Time between frames [default: 1].\n")); INITUINTOPTION(Frames, Eou(OPT_USE_GENOME, OPT_NFRAMES, _T("--nframes"), 20, SO_REQ_SEP, "\t--nframes= 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= 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= 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= 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= 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= 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= 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= 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= 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= 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 Time; EmberOptionEntry Dtime; EmberOptionEntry Frames; - EmberOptionEntry Loops; EmberOptionEntry Repeat; EmberOptionEntry Tries; EmberOptionEntry MaxXforms; @@ -719,6 +718,7 @@ public: EmberOptionEntry OffsetX; EmberOptionEntry OffsetY; EmberOptionEntry UseMem; + EmberOptionEntry Loops; EmberOptionEntry IsaacSeed;//Value string. EmberOptionEntry Input; diff --git a/Source/EmberGenome/EmberGenome.cpp b/Source/EmberGenome/EmberGenome.cpp index 4292f82..a761393 100644 --- a/Source/EmberGenome/EmberGenome.cpp +++ b/Source/EmberGenome/EmberGenome.cpp @@ -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> 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()); } } From 3fa59be990bff27ea6e2d6d0b14eda99da6c04ca Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Mon, 6 Jul 2015 15:05:43 +0100 Subject: [PATCH 7/7] Add new 'motion_offset' parameter to motion elements --- Source/Ember/Ember.h | 2 +- Source/Ember/EmberToXml.h | 6 ++++++ Source/Ember/FlameMotion.h | 3 +++ Source/Ember/Xform.h | 14 ++++++++++---- Source/Ember/XmlToEmber.h | 2 ++ 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index de18c0f..b02cd76 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -1293,7 +1293,7 @@ public: point.m_Z -= m_CamZPos; } -#define APP_FMP(x) x += param.second * Interpolater::MotionFuncs(motion.m_MotionFunc, motion.m_MotionFreq * blend) +#define APP_FMP(x) x += param.second * Interpolater::MotionFuncs(motion.m_MotionFunc, motion.m_MotionFreq * (blend + motion.m_MotionOffset)) /// /// Update ember parameters based on stored motion elements /// diff --git a/Source/Ember/EmberToXml.h b/Source/Ember/EmberToXml.h index 45a2810..f79ba8a 100644 --- a/Source/Ember/EmberToXml.h +++ b/Source/Ember/EmberToXml.h @@ -493,6 +493,9 @@ private: 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 { @@ -731,6 +734,9 @@ private: ostringstream os; os << " 0) + os << "motion_offset=\"" << motion.m_MotionOffset << "\" "; + os << "motion_func="; switch(motion.m_MotionFunc) { diff --git a/Source/Ember/FlameMotion.h b/Source/Ember/FlameMotion.h index 6700f42..7f47d90 100644 --- a/Source/Ember/FlameMotion.h +++ b/Source/Ember/FlameMotion.h @@ -20,6 +20,7 @@ public: { m_MotionFreq = 0; m_MotionFunc = MOTION_SIN; + m_MotionOffset = 0; } FlameMotion(const FlameMotion &other) @@ -51,10 +52,12 @@ public: 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> m_MotionParams; diff --git a/Source/Ember/Xform.h b/Source/Ember/Xform.h index cffc862..f36edec 100644 --- a/Source/Ember/Xform.h +++ b/Source/Ember/Xform.h @@ -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::MotionFuncs(func, freq * blend); \ + x += currentMot.x * Interpolater::MotionFuncs(func, freq * (blend + offset)); \ } while (0) /// @@ -733,6 +736,7 @@ public: Xform& currentMot = xform.m_Motion[i]; 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* newVar = motVar->Copy(); - newVar->m_Weight = motVar->m_Weight * Interpolater::MotionFuncs(func, freq * blend); + newVar->m_Weight = motVar->m_Weight * Interpolater::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::MotionFuncs(func, freq * blend); + var->m_Weight += motVar->m_Weight * Interpolater::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::MotionFuncs(func, freq * blend); + *(params[k].Param()) += motParams[k].ParamVal() * Interpolater::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*>(this)->AllVarsFunc([&] (vector*>& variations, bool& keepGoing) { @@ -1233,6 +1238,7 @@ public: T m_Wind[2]; eMotion m_MotionFunc; T m_MotionFreq; + T m_MotionOffset; vector> m_Motion; string m_Name; diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 290b43e..b823ed0 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -1015,6 +1015,7 @@ private: attStr = reinterpret_cast(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); @@ -1180,6 +1181,7 @@ private: else if (ParseAndAssignFloat(curAtt->name, attStr, "opacity", xform.m_Opacity, success)) { } else if (ParseAndAssignFloat(curAtt->name, attStr, "var_color", xform.m_DirectColor, 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"))