3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <type_traits>
27 #include "irrlichttypes_bloated.h"
28 #include "tileanimation.h"
30 #include "util/serialize.h"
31 #include "util/numeric.h"
33 // This file defines the particle-related structures that both the server and
34 // client need. The ParticleManager and rendering is in client/particles.h
36 namespace ParticleParamTypes
38 template <bool cond, typename T>
39 using enableIf = typename std::enable_if<cond, T>::type;
40 // std::enable_if_t does not appear to be present in GCC????
41 // std::is_enum_v also missing. wtf. these are supposed to be
42 // present as of c++14
44 template<typename T> using BlendFunction = T(float,T,T);
45 #define DECL_PARAM_SRZRS(type) \
46 void serializeParameterValue (std::ostream& os, type v); \
47 void deSerializeParameterValue(std::istream& is, type& r);
48 #define DECL_PARAM_OVERLOADS(type) DECL_PARAM_SRZRS(type) \
49 type interpolateParameterValue(float fac, const type a, const type b); \
50 type pickParameterValue (float* facs, const type a, const type b);
52 DECL_PARAM_OVERLOADS(u8); DECL_PARAM_OVERLOADS(s8);
53 DECL_PARAM_OVERLOADS(u16); DECL_PARAM_OVERLOADS(s16);
54 DECL_PARAM_OVERLOADS(u32); DECL_PARAM_OVERLOADS(s32);
55 DECL_PARAM_OVERLOADS(f32);
56 DECL_PARAM_OVERLOADS(v2f);
57 DECL_PARAM_OVERLOADS(v3f);
59 /* C++ is a strongly typed language. this means that enums cannot be implicitly
60 * cast to integers, as they can be in C. while this may sound good in principle,
61 * it means that our normal serialization functions cannot be called on
62 * enumerations unless they are explicitly cast to a particular type first. this
63 * is problematic, because in C++ enums can have any integral type as an underlying
64 * type, and that type would need to be named everywhere an enumeration is
67 * this is obviously not cool, both in terms of writing legible, succinct code,
68 * and in terms of robustness: the underlying type might be changed at some point,
69 * e.g. if a bitmask gets too big for its britches. we could use an equivalent of
70 * `std::to_underlying(value)` everywhere we need to deal with enumerations, but
71 * that's hideous and unintuitive. instead, we supply the following functions to
72 * transparently map enumeration types to their underlying values. */
74 template <typename E, enableIf<std::is_enum<E>::value, bool> = true>
75 void serializeParameterValue(std::ostream& os, E k) {
76 serializeParameterValue(os, (std::underlying_type_t<E>)k);
79 template <typename E, enableIf<std::is_enum<E>::value, bool> = true>
80 void deSerializeParameterValue(std::istream& is, E& k) {
81 std::underlying_type_t<E> v;
82 deSerializeParameterValue(is, v);
86 /* this is your brain on C++. */
88 template <typename T, size_t PN>
92 using pickFactors = float[PN];
95 using This = Parameter<T, PN>;
97 Parameter() = default;
99 template <typename... Args>
100 Parameter(Args... args) : val(args...) {}
102 virtual void serialize(std::ostream &os) const
103 { serializeParameterValue (os, this->val); }
104 virtual void deSerialize(std::istream &is)
105 { deSerializeParameterValue(is, this->val); }
107 virtual T interpolate(float fac, const This& against) const
109 return interpolateParameterValue(fac, this->val, against.val);
112 static T pick(float* f, const This& a, const This& b)
114 return pickParameterValue(f, a.val, b.val);
117 operator T() const { return val; }
118 T operator=(T b) { return val = b; }
122 template <typename T> T numericalBlend(float fac, T min, T max)
123 { return min + ((max - min) * fac); }
125 template <typename T, size_t N>
126 struct VectorParameter : public Parameter<T,N> {
127 using This = VectorParameter<T,N>;
128 template <typename... Args>
129 VectorParameter(Args... args) : Parameter<T,N>(args...) {}
132 template <typename T, size_t PN>
133 inline std::string dump(const Parameter<T,PN>& p)
135 return std::to_string(p.val);
138 template <typename T, size_t N>
139 inline std::string dump(const VectorParameter<T,N>& v)
141 std::ostringstream oss;
149 using u8Parameter = Parameter<u8, 1>; using s8Parameter = Parameter<s8, 1>;
150 using u16Parameter = Parameter<u16, 1>; using s16Parameter = Parameter<s16, 1>;
151 using u32Parameter = Parameter<u32, 1>; using s32Parameter = Parameter<s32, 1>;
153 using f32Parameter = Parameter<f32, 1>;
155 using v2fParameter = VectorParameter<v2f, 2>;
156 using v3fParameter = VectorParameter<v3f, 3>;
158 template <typename T>
159 struct RangedParameter
162 using This = RangedParameter<T>;
167 RangedParameter() = default;
168 RangedParameter(T _min, T _max) : min(_min), max(_max) {}
169 template <typename M> RangedParameter(M b) : min(b), max(b) {}
171 // these functions handle the old range serialization "format"; bias must
172 // be manually encoded in a separate part of the stream. NEVER ADD FIELDS
173 // TO THESE FUNCTIONS
174 void legacySerialize(std::ostream& os) const
179 void legacyDeSerialize(std::istream& is)
185 // these functions handle the format used by new fields. new fields go here
186 void serialize(std::ostream &os) const
191 void deSerialize(std::istream &is)
193 legacyDeSerialize(is);
197 This interpolate(float fac, const This against) const
200 r.min = min.interpolate(fac, against.min);
201 r.max = max.interpolate(fac, against.max);
208 typename T::pickFactors values;
209 auto p = numericAbsolute(bias) + 1;
210 for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) {
212 values[i] = 1.0f - pow(myrand_float(), p);
214 values[i] = pow(myrand_float(), p);
216 return T::pick(values, min, max);
221 template <typename T>
222 inline std::string dump(const RangedParameter<T>& r)
224 std::ostringstream s;
225 s << "range<" << dump(r.min) << " ~ " << dump(r.max);
227 s << " :: " << r.bias;
232 enum class TweenStyle : u8 { fwd, rev, pulse, flicker };
234 template <typename T>
235 struct TweenedParameter
238 using This = TweenedParameter<T>;
240 TweenStyle style = TweenStyle::fwd;
242 f32 beginning = 0.0f;
246 TweenedParameter() = default;
247 TweenedParameter(T _start, T _end) : start(_start), end(_end) {}
248 template <typename M> TweenedParameter(M b) : start(b), end(b) {}
250 T blend(float fac) const
252 // warp time coordinates in accordance w/ settings
253 if (fac > beginning) {
254 // remap for beginning offset
255 auto len = 1 - beginning;
259 // remap for repetitions
261 if (fac > 1) // poor man's modulo
262 fac -= (decltype(reps))fac;
266 case TweenStyle::fwd: /* do nothing */ break;
267 case TweenStyle::rev: fac = 1.0f - fac; break;
268 case TweenStyle::pulse:
269 case TweenStyle::flicker: {
271 fac = 1.f - (fac*2.f - 1.f);
275 if (style == TweenStyle::flicker) {
276 fac *= myrand_range(0.7f, 1.0f);
285 fac = (style == TweenStyle::rev) ? 1.f : 0.f;
288 return start.interpolate(fac, end);
291 void serialize(std::ostream &os) const
293 writeU8(os, static_cast<u8>(style));
295 writeF32(os, beginning);
299 void deSerialize(std::istream &is)
301 style = static_cast<TweenStyle>(readU8(is));
303 beginning = readF32(is);
304 start.deSerialize(is);
309 template <typename T>
310 inline std::string dump(const TweenedParameter<T>& t)
312 std::ostringstream s;
315 case TweenStyle::fwd: icon = "→"; break;
316 case TweenStyle::rev: icon = "←"; break;
317 case TweenStyle::pulse: icon = "↔"; break;
318 case TweenStyle::flicker: icon = "↯"; break;
323 s << dump(t.start) << " "<<icon<<" " << dump(t.end) << ">";
327 enum class AttractorKind : u8 { none, point, line, plane };
328 enum class BlendMode : u8 { alpha, add, sub, screen };
330 // these are consistently-named convenience aliases to make code more readable without `using ParticleParamTypes` declarations
331 using v3fRange = RangedParameter<v3fParameter>;
332 using f32Range = RangedParameter<f32Parameter>;
334 using v2fTween = TweenedParameter<v2fParameter>;
335 using v3fTween = TweenedParameter<v3fParameter>;
336 using f32Tween = TweenedParameter<f32Parameter>;
337 using v3fRangeTween = TweenedParameter<v3fRange>;
338 using f32RangeTween = TweenedParameter<f32Range>;
340 #undef DECL_PARAM_SRZRS
341 #undef DECL_PARAM_OVERLOADS
344 struct ParticleTexture
346 bool animated = false;
347 ParticleParamTypes::BlendMode blendmode = ParticleParamTypes::BlendMode::alpha;
348 TileAnimationParams animation;
349 ParticleParamTypes::f32Tween alpha{1.0f};
350 ParticleParamTypes::v2fTween scale{v2f(1.0f)};
353 struct ServerParticleTexture : public ParticleTexture
356 void serialize(std::ostream &os, u16 protocol_ver, bool newPropertiesOnly = false) const;
357 void deSerialize(std::istream &is, u16 protocol_ver, bool newPropertiesOnly = false);
360 struct CommonParticleParams
362 bool collisiondetection = false;
363 bool collision_removal = false;
364 bool object_collision = false;
365 bool vertical = false;
366 ServerParticleTexture texture;
367 struct TileAnimationParams animation;
372 CommonParticleParams() {
373 animation.type = TAT_NONE;
374 node.setContent(CONTENT_IGNORE);
377 /* This helper is useful for copying params from
378 * ParticleSpawnerParameters to ParticleParameters */
379 inline void copyCommon(CommonParticleParams &to) const {
380 to.collisiondetection = collisiondetection;
381 to.collision_removal = collision_removal;
382 to.object_collision = object_collision;
383 to.vertical = vertical;
384 to.texture = texture;
385 to.animation = animation;
388 to.node_tile = node_tile;
392 struct ParticleParameters : CommonParticleParams
394 v3f pos, vel, acc, drag;
395 f32 size = 1, expirationtime = 1;
396 ParticleParamTypes::f32Range bounce;
397 ParticleParamTypes::v3fRange jitter;
399 void serialize(std::ostream &os, u16 protocol_ver) const;
400 void deSerialize(std::istream &is, u16 protocol_ver);
403 struct ParticleSpawnerParameters : CommonParticleParams
408 std::vector<ServerParticleTexture> texpool;
410 ParticleParamTypes::v3fRangeTween
411 pos, vel, acc, drag, radius, jitter;
413 ParticleParamTypes::AttractorKind
415 ParticleParamTypes::v3fTween
416 attractor_origin, attractor_direction;
418 u16 attractor_attachment = 0,
419 attractor_direction_attachment = 0;
420 // do particles disappear when they cross the attractor threshold?
421 bool attractor_kill = true;
423 ParticleParamTypes::f32RangeTween
429 // For historical reasons no (de-)serialization methods here