diff --git a/src/particles.cpp b/src/particles.cpp index e495ecd03..74ccc2aae 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -19,8 +19,122 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "particles.h" #include + using namespace ParticleParamTypes; +template +void RangedParameter::serialize(std::ostream &os) const +{ + min.serialize(os); + max.serialize(os); + writeF32(os, bias); +} + +template +void RangedParameter::deSerialize(std::istream &is) +{ + min.deSerialize(is); + max.deSerialize(is); + bias = readF32(is); +} + + +template +T RangedParameter::pickWithin() const +{ + typename T::pickFactors values; + auto p = numericAbsolute(bias) + 1; + for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) { + if (bias < 0) + values[i] = 1.0f - pow(myrand_float(), p); + else + values[i] = pow(myrand_float(), p); + } + return T::pick(values, min, max); +} + + +template +T TweenedParameter::blend(float fac) const +{ + // warp time coordinates in accordance w/ settings + if (fac > beginning) { + // remap for beginning offset + auto len = 1 - beginning; + fac -= beginning; + fac /= len; + + // remap for repetitions + fac *= reps; + if (fac > 1) // poor man's modulo + fac -= (decltype(reps))fac; + + // remap for style + switch (style) { + case TweenStyle::fwd: /* do nothing */ break; + case TweenStyle::rev: fac = 1.0f - fac; break; + case TweenStyle::pulse: + case TweenStyle::flicker: { + if (fac > 0.5f) { + fac = 1.f - (fac*2.f - 1.f); + } else { + fac = fac * 2; + } + if (style == TweenStyle::flicker) { + fac *= myrand_range(0.7f, 1.0f); + } + } + } + if (fac>1.f) + fac = 1.f; + else if (fac<0.f) + fac = 0.f; + } else { + fac = (style == TweenStyle::rev) ? 1.f : 0.f; + } + + return start.interpolate(fac, end); +} + +template +void TweenedParameter::serialize(std::ostream &os) const +{ + writeU8(os, static_cast(style)); + writeU16(os, reps); + writeF32(os, beginning); + start.serialize(os); + end.serialize(os); +} + +template +void TweenedParameter::deSerialize(std::istream &is) +{ + style = static_cast(readU8(is)); + reps = readU16(is); + beginning = readF32(is); + start.deSerialize(is); + end.deSerialize(is); +} + +namespace ParticleParamTypes { + // For function definitions + template struct RangedParameter; + template struct RangedParameter; + + template struct TweenedParameter; + template struct TweenedParameter; + template struct TweenedParameter; + template struct TweenedParameter; + template struct TweenedParameter; +} + +// Linear interpolation +template +static T numericalBlend(float fac, T min, T max) +{ + return min + ((max - min) * fac); +} + #define PARAM_PVFN(n) ParticleParamTypes::n##ParameterValue v2f PARAM_PVFN(pick) (float* f, const v2f a, const v2f b) { return v2f( diff --git a/src/particles.h b/src/particles.h index 3061deb83..179d5ac5b 100644 --- a/src/particles.h +++ b/src/particles.h @@ -49,6 +49,7 @@ namespace ParticleParamTypes type interpolateParameterValue(float fac, const type a, const type b); \ type pickParameterValue (float* facs, const type a, const type b); + // Function definition: see "particles.cpp" DECL_PARAM_OVERLOADS(u8); DECL_PARAM_OVERLOADS(s8); DECL_PARAM_OVERLOADS(u16); DECL_PARAM_OVERLOADS(s16); DECL_PARAM_OVERLOADS(u32); DECL_PARAM_OVERLOADS(s32); @@ -83,8 +84,7 @@ namespace ParticleParamTypes k = (E)v; } - /* this is your brain on C++. */ - + // Describes a single value template struct Parameter { @@ -119,9 +119,8 @@ namespace ParticleParamTypes }; - template T numericalBlend(float fac, T min, T max) - { return min + ((max - min) * fac); } - + // New struct required to differentiate between core::vectorNd-compatible + // structs for proper value dumping (debugging) template struct VectorParameter : public Parameter { using This = VectorParameter; @@ -146,15 +145,12 @@ namespace ParticleParamTypes return oss.str(); } - using u8Parameter = Parameter; using s8Parameter = Parameter; - using u16Parameter = Parameter; using s16Parameter = Parameter; - using u32Parameter = Parameter; using s32Parameter = Parameter; - using f32Parameter = Parameter; - using v2fParameter = VectorParameter; using v3fParameter = VectorParameter; + // Add more parameter types here if you need them ... + // Bound limits information based on "Parameter" types template struct RangedParameter { @@ -168,31 +164,20 @@ namespace ParticleParamTypes RangedParameter(T _min, T _max) : min(_min), max(_max) {} template RangedParameter(M b) : min(b), max(b) {} - // these functions handle the old range serialization "format"; bias must - // be manually encoded in a separate part of the stream. NEVER ADD FIELDS - // TO THESE FUNCTIONS - void legacySerialize(std::ostream& os) const + // Binary format must not be changed. Function is to be deprecated. + void legacySerialize(std::ostream &os) const { min.serialize(os); max.serialize(os); } - void legacyDeSerialize(std::istream& is) + void legacyDeSerialize(std::istream &is) { min.deSerialize(is); max.deSerialize(is); } - // these functions handle the format used by new fields. new fields go here - void serialize(std::ostream &os) const - { - legacySerialize(os); - writeF32(os, bias); - } - void deSerialize(std::istream &is) - { - legacyDeSerialize(is); - bias = readF32(is); - } + void serialize(std::ostream &os) const; + void deSerialize(std::istream &is); This interpolate(float fac, const This against) const { @@ -203,19 +188,8 @@ namespace ParticleParamTypes return r; } - T pickWithin() const - { - typename T::pickFactors values; - auto p = numericAbsolute(bias) + 1; - for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) { - if (bias < 0) - values[i] = 1.0f - pow(myrand_float(), p); - else - values[i] = pow(myrand_float(), p); - } - return T::pick(values, min, max); - } - + // Pick a random value (e.g. position) within bounds + T pickWithin() const; }; template @@ -229,8 +203,10 @@ namespace ParticleParamTypes return s.str(); } + // Animation styles (fwd is normal, linear interpolation) enum class TweenStyle : u8 { fwd, rev, pulse, flicker }; + // "Tweened" pretty much means "animated" in this context template struct TweenedParameter { @@ -238,72 +214,21 @@ namespace ParticleParamTypes using This = TweenedParameter; TweenStyle style = TweenStyle::fwd; - u16 reps = 1; - f32 beginning = 0.0f; + u16 reps = 1; // Blending repetitions (same pattern) + f32 beginning = 0.0f; // Blending start offset T start, end; TweenedParameter() = default; TweenedParameter(T _start, T _end) : start(_start), end(_end) {} + // For initializer lists and assignment template TweenedParameter(M b) : start(b), end(b) {} - T blend(float fac) const - { - // warp time coordinates in accordance w/ settings - if (fac > beginning) { - // remap for beginning offset - auto len = 1 - beginning; - fac -= beginning; - fac /= len; + // Blend (or animate) the current value + T blend(float fac) const; - // remap for repetitions - fac *= reps; - if (fac > 1) // poor man's modulo - fac -= (decltype(reps))fac; - - // remap for style - switch (style) { - case TweenStyle::fwd: /* do nothing */ break; - case TweenStyle::rev: fac = 1.0f - fac; break; - case TweenStyle::pulse: - case TweenStyle::flicker: { - if (fac > 0.5f) { - fac = 1.f - (fac*2.f - 1.f); - } else { - fac = fac * 2; - } - if (style == TweenStyle::flicker) { - fac *= myrand_range(0.7f, 1.0f); - } - } - } - if (fac>1.f) - fac = 1.f; - else if (fac<0.f) - fac = 0.f; - } else { - fac = (style == TweenStyle::rev) ? 1.f : 0.f; - } - - return start.interpolate(fac, end); - } - - void serialize(std::ostream &os) const - { - writeU8(os, static_cast(style)); - writeU16(os, reps); - writeF32(os, beginning); - start.serialize(os); - end.serialize(os); - } - void deSerialize(std::istream &is) - { - style = static_cast(readU8(is)); - reps = readU16(is); - beginning = readF32(is); - start.deSerialize(is); - end.deSerialize(is); - } + void serialize(std::ostream &os) const; + void deSerialize(std::istream &is); }; template