particles.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #pragma once
  17. #include <string>
  18. #include <sstream>
  19. #include <vector>
  20. #include <ctgmath>
  21. #include <type_traits>
  22. #include "irrlichttypes_bloated.h"
  23. #include "tileanimation.h"
  24. #include "mapnode.h"
  25. #include "util/serialize.h"
  26. #include "util/numeric.h"
  27. // This file defines the particle-related structures that both the server and
  28. // client need. The ParticleManager and rendering is in client/particles.h
  29. namespace ParticleParamTypes
  30. {
  31. template <bool cond, typename T>
  32. using enableIf = typename std::enable_if<cond, T>::type;
  33. // std::enable_if_t does not appear to be present in GCC????
  34. // std::is_enum_v also missing. wtf. these are supposed to be
  35. // present as of c++14
  36. template<typename T> using BlendFunction = T(float,T,T);
  37. #define DECL_PARAM_SRZRS(type) \
  38. void serializeParameterValue (std::ostream& os, type v); \
  39. void deSerializeParameterValue(std::istream& is, type& r);
  40. #define DECL_PARAM_OVERLOADS(type) DECL_PARAM_SRZRS(type) \
  41. type interpolateParameterValue(float fac, const type a, const type b); \
  42. type pickParameterValue (float* facs, const type a, const type b);
  43. DECL_PARAM_OVERLOADS(u8); DECL_PARAM_OVERLOADS(s8);
  44. DECL_PARAM_OVERLOADS(u16); DECL_PARAM_OVERLOADS(s16);
  45. DECL_PARAM_OVERLOADS(u32); DECL_PARAM_OVERLOADS(s32);
  46. DECL_PARAM_OVERLOADS(f32);
  47. DECL_PARAM_OVERLOADS(v2f);
  48. DECL_PARAM_OVERLOADS(v3f);
  49. /* C++ is a strongly typed language. this means that enums cannot be implicitly
  50. * cast to integers, as they can be in C. while this may sound good in principle,
  51. * it means that our normal serialization functions cannot be called on
  52. * enumerations unless they are explicitly cast to a particular type first. this
  53. * is problematic, because in C++ enums can have any integral type as an underlying
  54. * type, and that type would need to be named everywhere an enumeration is
  55. * de/serialized.
  56. *
  57. * this is obviously not cool, both in terms of writing legible, succinct code,
  58. * and in terms of robustness: the underlying type might be changed at some point,
  59. * e.g. if a bitmask gets too big for its britches. we could use an equivalent of
  60. * `std::to_underlying(value)` everywhere we need to deal with enumerations, but
  61. * that's hideous and unintuitive. instead, we supply the following functions to
  62. * transparently map enumeration types to their underlying values. */
  63. template <typename E, enableIf<std::is_enum<E>::value, bool> = true>
  64. void serializeParameterValue(std::ostream& os, E k) {
  65. serializeParameterValue(os, (std::underlying_type_t<E>)k);
  66. }
  67. template <typename E, enableIf<std::is_enum<E>::value, bool> = true>
  68. void deSerializeParameterValue(std::istream& is, E& k) {
  69. std::underlying_type_t<E> v;
  70. deSerializeParameterValue(is, v);
  71. k = (E)v;
  72. }
  73. /* this is your brain on C++. */
  74. template <typename T, size_t PN>
  75. struct Parameter
  76. {
  77. using ValType = T;
  78. using pickFactors = float[PN];
  79. T val = T();
  80. using This = Parameter<T, PN>;
  81. Parameter() = default;
  82. template <typename... Args>
  83. Parameter(Args... args) : val(args...) {}
  84. virtual void serialize(std::ostream &os) const
  85. { serializeParameterValue (os, this->val); }
  86. virtual void deSerialize(std::istream &is)
  87. { deSerializeParameterValue(is, this->val); }
  88. virtual T interpolate(float fac, const This& against) const
  89. {
  90. return interpolateParameterValue(fac, this->val, against.val);
  91. }
  92. static T pick(float* f, const This& a, const This& b)
  93. {
  94. return pickParameterValue(f, a.val, b.val);
  95. }
  96. operator T() const { return val; }
  97. T operator=(T b) { return val = b; }
  98. };
  99. template <typename T> T numericalBlend(float fac, T min, T max)
  100. { return min + ((max - min) * fac); }
  101. template <typename T, size_t N>
  102. struct VectorParameter : public Parameter<T,N> {
  103. using This = VectorParameter<T,N>;
  104. template <typename... Args>
  105. VectorParameter(Args... args) : Parameter<T,N>(args...) {}
  106. };
  107. template <typename T, size_t PN>
  108. inline std::string dump(const Parameter<T,PN>& p)
  109. {
  110. return std::to_string(p.val);
  111. }
  112. template <typename T, size_t N>
  113. inline std::string dump(const VectorParameter<T,N>& v)
  114. {
  115. std::ostringstream oss;
  116. if (N == 3)
  117. oss << PP(v.val);
  118. else
  119. oss << PP2(v.val);
  120. return oss.str();
  121. }
  122. using u8Parameter = Parameter<u8, 1>; using s8Parameter = Parameter<s8, 1>;
  123. using u16Parameter = Parameter<u16, 1>; using s16Parameter = Parameter<s16, 1>;
  124. using u32Parameter = Parameter<u32, 1>; using s32Parameter = Parameter<s32, 1>;
  125. using f32Parameter = Parameter<f32, 1>;
  126. using v2fParameter = VectorParameter<v2f, 2>;
  127. using v3fParameter = VectorParameter<v3f, 3>;
  128. template <typename T>
  129. struct RangedParameter
  130. {
  131. using ValType = T;
  132. using This = RangedParameter<T>;
  133. T min, max;
  134. f32 bias = 0;
  135. RangedParameter() = default;
  136. RangedParameter(T _min, T _max) : min(_min), max(_max) {}
  137. template <typename M> RangedParameter(M b) : min(b), max(b) {}
  138. // these functions handle the old range serialization "format"; bias must
  139. // be manually encoded in a separate part of the stream. NEVER ADD FIELDS
  140. // TO THESE FUNCTIONS
  141. void legacySerialize(std::ostream& os) const
  142. {
  143. min.serialize(os);
  144. max.serialize(os);
  145. }
  146. void legacyDeSerialize(std::istream& is)
  147. {
  148. min.deSerialize(is);
  149. max.deSerialize(is);
  150. }
  151. // these functions handle the format used by new fields. new fields go here
  152. void serialize(std::ostream &os) const
  153. {
  154. legacySerialize(os);
  155. writeF32(os, bias);
  156. }
  157. void deSerialize(std::istream &is)
  158. {
  159. legacyDeSerialize(is);
  160. bias = readF32(is);
  161. }
  162. This interpolate(float fac, const This against) const
  163. {
  164. This r;
  165. r.min = min.interpolate(fac, against.min);
  166. r.max = max.interpolate(fac, against.max);
  167. r.bias = bias;
  168. return r;
  169. }
  170. T pickWithin() const
  171. {
  172. typename T::pickFactors values;
  173. auto p = numericAbsolute(bias) + 1;
  174. for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) {
  175. if (bias < 0)
  176. values[i] = 1.0f - pow(myrand_float(), p);
  177. else
  178. values[i] = pow(myrand_float(), p);
  179. }
  180. return T::pick(values, min, max);
  181. }
  182. };
  183. template <typename T>
  184. inline std::string dump(const RangedParameter<T>& r)
  185. {
  186. std::ostringstream s;
  187. s << "range<" << dump(r.min) << " ~ " << dump(r.max);
  188. if (r.bias != 0)
  189. s << " :: " << r.bias;
  190. s << ">";
  191. return s.str();
  192. }
  193. enum class TweenStyle : u8 { fwd, rev, pulse, flicker };
  194. template <typename T>
  195. struct TweenedParameter
  196. {
  197. using ValType = T;
  198. using This = TweenedParameter<T>;
  199. TweenStyle style = TweenStyle::fwd;
  200. u16 reps = 1;
  201. f32 beginning = 0.0f;
  202. T start, end;
  203. TweenedParameter() = default;
  204. TweenedParameter(T _start, T _end) : start(_start), end(_end) {}
  205. template <typename M> TweenedParameter(M b) : start(b), end(b) {}
  206. T blend(float fac) const
  207. {
  208. // warp time coordinates in accordance w/ settings
  209. if (fac > beginning) {
  210. // remap for beginning offset
  211. auto len = 1 - beginning;
  212. fac -= beginning;
  213. fac /= len;
  214. // remap for repetitions
  215. fac *= reps;
  216. if (fac > 1) // poor man's modulo
  217. fac -= (decltype(reps))fac;
  218. // remap for style
  219. switch (style) {
  220. case TweenStyle::fwd: /* do nothing */ break;
  221. case TweenStyle::rev: fac = 1.0f - fac; break;
  222. case TweenStyle::pulse:
  223. case TweenStyle::flicker: {
  224. if (fac > 0.5f) {
  225. fac = 1.f - (fac*2.f - 1.f);
  226. } else {
  227. fac = fac * 2;
  228. }
  229. if (style == TweenStyle::flicker) {
  230. fac *= myrand_range(0.7f, 1.0f);
  231. }
  232. }
  233. }
  234. if (fac>1.f)
  235. fac = 1.f;
  236. else if (fac<0.f)
  237. fac = 0.f;
  238. } else {
  239. fac = (style == TweenStyle::rev) ? 1.f : 0.f;
  240. }
  241. return start.interpolate(fac, end);
  242. }
  243. void serialize(std::ostream &os) const
  244. {
  245. writeU8(os, static_cast<u8>(style));
  246. writeU16(os, reps);
  247. writeF32(os, beginning);
  248. start.serialize(os);
  249. end.serialize(os);
  250. }
  251. void deSerialize(std::istream &is)
  252. {
  253. style = static_cast<TweenStyle>(readU8(is));
  254. reps = readU16(is);
  255. beginning = readF32(is);
  256. start.deSerialize(is);
  257. end.deSerialize(is);
  258. }
  259. };
  260. template <typename T>
  261. inline std::string dump(const TweenedParameter<T>& t)
  262. {
  263. std::ostringstream s;
  264. const char* icon;
  265. switch (t.style) {
  266. case TweenStyle::fwd: icon = "→"; break;
  267. case TweenStyle::rev: icon = "←"; break;
  268. case TweenStyle::pulse: icon = "↔"; break;
  269. case TweenStyle::flicker: icon = "↯"; break;
  270. }
  271. s << "tween<";
  272. if (t.reps != 1)
  273. s << t.reps << "x ";
  274. s << dump(t.start) << " "<<icon<<" " << dump(t.end) << ">";
  275. return s.str();
  276. }
  277. enum class AttractorKind : u8 { none, point, line, plane };
  278. enum class BlendMode : u8 { alpha, add, sub, screen };
  279. // these are consistently-named convenience aliases to make code more readable without `using ParticleParamTypes` declarations
  280. using v3fRange = RangedParameter<v3fParameter>;
  281. using f32Range = RangedParameter<f32Parameter>;
  282. using v2fTween = TweenedParameter<v2fParameter>;
  283. using v3fTween = TweenedParameter<v3fParameter>;
  284. using f32Tween = TweenedParameter<f32Parameter>;
  285. using v3fRangeTween = TweenedParameter<v3fRange>;
  286. using f32RangeTween = TweenedParameter<f32Range>;
  287. #undef DECL_PARAM_SRZRS
  288. #undef DECL_PARAM_OVERLOADS
  289. }
  290. struct ParticleTexture
  291. {
  292. bool animated = false;
  293. ParticleParamTypes::BlendMode blendmode = ParticleParamTypes::BlendMode::alpha;
  294. TileAnimationParams animation;
  295. ParticleParamTypes::f32Tween alpha{1.0f};
  296. ParticleParamTypes::v2fTween scale{v2f(1.0f)};
  297. };
  298. struct ServerParticleTexture : public ParticleTexture
  299. {
  300. std::string string;
  301. void serialize(std::ostream &os, u16 protocol_ver, bool newPropertiesOnly = false) const;
  302. void deSerialize(std::istream &is, u16 protocol_ver, bool newPropertiesOnly = false);
  303. };
  304. struct CommonParticleParams
  305. {
  306. bool collisiondetection = false;
  307. bool collision_removal = false;
  308. bool object_collision = false;
  309. bool vertical = false;
  310. ServerParticleTexture texture;
  311. struct TileAnimationParams animation;
  312. u8 glow = 0;
  313. MapNode node;
  314. u8 node_tile = 0;
  315. CommonParticleParams() {
  316. animation.type = TAT_NONE;
  317. node.setContent(CONTENT_IGNORE);
  318. }
  319. /* This helper is useful for copying params from
  320. * ParticleSpawnerParameters to ParticleParameters */
  321. inline void copyCommon(CommonParticleParams &to) const {
  322. to.collisiondetection = collisiondetection;
  323. to.collision_removal = collision_removal;
  324. to.object_collision = object_collision;
  325. to.vertical = vertical;
  326. to.texture = texture;
  327. to.animation = animation;
  328. to.glow = glow;
  329. to.node = node;
  330. to.node_tile = node_tile;
  331. }
  332. };
  333. struct ParticleParameters : CommonParticleParams
  334. {
  335. v3f pos, vel, acc, drag;
  336. f32 size = 1, expirationtime = 1;
  337. ParticleParamTypes::f32Range bounce;
  338. ParticleParamTypes::v3fRange jitter;
  339. void serialize(std::ostream &os, u16 protocol_ver) const;
  340. void deSerialize(std::istream &is, u16 protocol_ver);
  341. };
  342. struct ParticleSpawnerParameters : CommonParticleParams
  343. {
  344. u16 amount = 1;
  345. f32 time = 1;
  346. std::vector<ServerParticleTexture> texpool;
  347. ParticleParamTypes::v3fRangeTween
  348. pos, vel, acc, drag, radius, jitter;
  349. ParticleParamTypes::AttractorKind
  350. attractor_kind;
  351. ParticleParamTypes::v3fTween
  352. attractor_origin, attractor_direction;
  353. // object IDs
  354. u16 attractor_attachment = 0,
  355. attractor_direction_attachment = 0;
  356. // do particles disappear when they cross the attractor threshold?
  357. bool attractor_kill = true;
  358. ParticleParamTypes::f32RangeTween
  359. exptime{1.0f},
  360. size {1.0f},
  361. attract{0.0f},
  362. bounce {0.0f};
  363. // For historical reasons no (de-)serialization methods here
  364. };