particles.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. Minetest
  3. Copyright (C) 2020 sfan5 <sfan5@live.de>
  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. #include "particles.h"
  17. #include <type_traits>
  18. using namespace ParticleParamTypes;
  19. template<typename T>
  20. void RangedParameter<T>::serialize(std::ostream &os) const
  21. {
  22. min.serialize(os);
  23. max.serialize(os);
  24. writeF32(os, bias);
  25. }
  26. template<typename T>
  27. void RangedParameter<T>::deSerialize(std::istream &is)
  28. {
  29. min.deSerialize(is);
  30. max.deSerialize(is);
  31. bias = readF32(is);
  32. }
  33. template<typename T>
  34. T RangedParameter<T>::pickWithin() const
  35. {
  36. typename T::pickFactors values;
  37. auto p = numericAbsolute(bias) + 1;
  38. for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) {
  39. if (bias < 0)
  40. values[i] = 1.0f - std::pow(myrand_float(), p);
  41. else
  42. values[i] = std::pow(myrand_float(), p);
  43. }
  44. return T::pick(values, min, max);
  45. }
  46. template<typename T>
  47. T TweenedParameter<T>::blend(float fac) const
  48. {
  49. // warp time coordinates in accordance w/ settings
  50. if (fac > beginning) {
  51. // remap for beginning offset
  52. auto len = 1 - beginning;
  53. fac -= beginning;
  54. fac /= len;
  55. // remap for repetitions
  56. fac *= reps;
  57. if (fac > 1) // poor man's modulo
  58. fac -= (decltype(reps))fac;
  59. // remap for style
  60. switch (style) {
  61. case TweenStyle::fwd: /* do nothing */ break;
  62. case TweenStyle::rev: fac = 1.0f - fac; break;
  63. case TweenStyle::pulse:
  64. case TweenStyle::flicker: {
  65. if (fac > 0.5f) {
  66. fac = 1.f - (fac*2.f - 1.f);
  67. } else {
  68. fac = fac * 2;
  69. }
  70. if (style == TweenStyle::flicker) {
  71. fac *= myrand_range(0.7f, 1.0f);
  72. }
  73. }
  74. case TweenStyle::TweenStyle_END:
  75. break;
  76. }
  77. if (fac>1.f)
  78. fac = 1.f;
  79. else if (fac<0.f)
  80. fac = 0.f;
  81. } else {
  82. fac = (style == TweenStyle::rev) ? 1.f : 0.f;
  83. }
  84. return start.interpolate(fac, end);
  85. }
  86. template<typename T>
  87. void TweenedParameter<T>::serialize(std::ostream &os) const
  88. {
  89. writeU8(os, static_cast<u8>(style));
  90. writeU16(os, reps);
  91. writeF32(os, beginning);
  92. start.serialize(os);
  93. end.serialize(os);
  94. }
  95. template<typename T>
  96. void TweenedParameter<T>::deSerialize(std::istream &is)
  97. {
  98. style = static_cast<TweenStyle>(readU8(is));
  99. if (style >= TweenStyle::TweenStyle_END)
  100. style = TweenStyle::fwd;
  101. reps = readU16(is);
  102. beginning = readF32(is);
  103. start.deSerialize(is);
  104. end.deSerialize(is);
  105. }
  106. namespace ParticleParamTypes {
  107. // For function definitions
  108. template struct RangedParameter<v3fParameter>;
  109. template struct RangedParameter<f32Parameter>;
  110. template struct TweenedParameter<v2fParameter>;
  111. template struct TweenedParameter<v3fParameter>;
  112. template struct TweenedParameter<f32Parameter>;
  113. template struct TweenedParameter<v3fRange>;
  114. template struct TweenedParameter<f32Range>;
  115. }
  116. // Linear interpolation
  117. template <typename T>
  118. static T numericalBlend(float fac, T min, T max)
  119. {
  120. return min + ((max - min) * fac);
  121. }
  122. #define PARAM_PVFN(n) ParticleParamTypes::n##ParameterValue
  123. v2f PARAM_PVFN(pick) (float* f, const v2f a, const v2f b) {
  124. return v2f(
  125. numericalBlend(f[0], a.X, b.X),
  126. numericalBlend(f[1], a.Y, b.Y)
  127. );
  128. }
  129. v3f PARAM_PVFN(pick) (float* f, const v3f a, const v3f b) {
  130. return v3f(
  131. numericalBlend(f[0], a.X, b.X),
  132. numericalBlend(f[1], a.Y, b.Y),
  133. numericalBlend(f[2], a.Z, b.Z)
  134. );
  135. }
  136. v2f PARAM_PVFN(interpolate) (float fac, const v2f a, const v2f b)
  137. { return b.getInterpolated(a, fac); }
  138. v3f PARAM_PVFN(interpolate) (float fac, const v3f a, const v3f b)
  139. { return b.getInterpolated(a, fac); }
  140. #define PARAM_DEF_SRZR(T, wr, rd) \
  141. void PARAM_PVFN(serialize) (std::ostream& os, T v) {wr(os,v); } \
  142. void PARAM_PVFN(deSerialize)(std::istream& is, T& v) {v = rd(is);}
  143. #define PARAM_DEF_NUM(T, wr, rd) PARAM_DEF_SRZR(T, wr, rd) \
  144. T PARAM_PVFN(interpolate)(float fac, const T a, const T b) \
  145. { return numericalBlend<T>(fac,a,b); } \
  146. T PARAM_PVFN(pick) (float* f, const T a, const T b) \
  147. { return numericalBlend<T>(f[0],a,b); }
  148. PARAM_DEF_NUM(u8, writeU8, readU8); PARAM_DEF_NUM(s8, writeS8, readS8);
  149. PARAM_DEF_NUM(u16, writeU16, readU16); PARAM_DEF_NUM(s16, writeS16, readS16);
  150. PARAM_DEF_NUM(u32, writeU32, readU32); PARAM_DEF_NUM(s32, writeS32, readS32);
  151. PARAM_DEF_NUM(f32, writeF32, readF32);
  152. PARAM_DEF_SRZR(v2f, writeV2F32, readV2F32);
  153. PARAM_DEF_SRZR(v3f, writeV3F32, readV3F32);
  154. enum class ParticleTextureFlags : u8 {
  155. /* each value specifies a bit in a bitmask; if the maximum value
  156. * goes above 1<<7 the type of the flags field must be changed
  157. * from u8, which will necessitate a protocol change! */
  158. // the first bit indicates whether the texture is animated
  159. animated = 1,
  160. /* the next three bits indicate the blending mode of the texture
  161. * blendmode is encoded by (flags |= (u8)blend << 1); retrieve with
  162. * (flags & ParticleTextureFlags::blend) >> 1. note that the third
  163. * bit is currently reserved for adding more blend modes in the future */
  164. blend = 0x7 << 1,
  165. };
  166. /* define some shorthand so we don't have to repeat ourselves or use
  167. * decltype everywhere */
  168. using FlagT = std::underlying_type_t<ParticleTextureFlags>;
  169. void ServerParticleTexture::serialize(std::ostream &os, u16 protocol_ver, bool newPropertiesOnly) const
  170. {
  171. /* newPropertiesOnly is used to de/serialize parameters of the legacy texture
  172. * field, which are encoded separately from the texspec string */
  173. FlagT flags = 0;
  174. if (animated)
  175. flags |= FlagT(ParticleTextureFlags::animated);
  176. if (blendmode != BlendMode::alpha)
  177. flags |= FlagT(blendmode) << 1;
  178. serializeParameterValue(os, flags);
  179. alpha.serialize(os);
  180. scale.serialize(os);
  181. if (!newPropertiesOnly)
  182. os << serializeString32(string);
  183. if (animated)
  184. animation.serialize(os, protocol_ver);
  185. }
  186. void ServerParticleTexture::deSerialize(std::istream &is, u16 protocol_ver, bool newPropertiesOnly)
  187. {
  188. FlagT flags = 0;
  189. deSerializeParameterValue(is, flags);
  190. animated = !!(flags & FlagT(ParticleTextureFlags::animated));
  191. blendmode = BlendMode((flags & FlagT(ParticleTextureFlags::blend)) >> 1);
  192. alpha.deSerialize(is);
  193. scale.deSerialize(is);
  194. if (!newPropertiesOnly)
  195. string = deSerializeString32(is);
  196. if (animated)
  197. animation.deSerialize(is, protocol_ver);
  198. }
  199. void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const
  200. {
  201. writeV3F32(os, pos);
  202. writeV3F32(os, vel);
  203. writeV3F32(os, acc);
  204. writeF32(os, expirationtime);
  205. writeF32(os, size);
  206. writeU8(os, collisiondetection);
  207. os << serializeString32(texture.string);
  208. writeU8(os, vertical);
  209. writeU8(os, collision_removal);
  210. animation.serialize(os, protocol_ver);
  211. writeU8(os, glow);
  212. writeU8(os, object_collision);
  213. writeU16(os, node.param0);
  214. writeU8(os, node.param2);
  215. writeU8(os, node_tile);
  216. writeV3F32(os, drag);
  217. jitter.serialize(os);
  218. bounce.serialize(os);
  219. }
  220. template <typename T, T (reader)(std::istream& is)>
  221. inline bool streamEndsBeforeParam(T& val, std::istream& is)
  222. {
  223. // This is kinda awful
  224. T tmp = reader(is);
  225. if (is.eof())
  226. return true;
  227. val = tmp;
  228. return false;
  229. }
  230. void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver)
  231. {
  232. pos = readV3F32(is);
  233. vel = readV3F32(is);
  234. acc = readV3F32(is);
  235. expirationtime = readF32(is);
  236. size = readF32(is);
  237. collisiondetection = readU8(is);
  238. texture.string = deSerializeString32(is);
  239. vertical = readU8(is);
  240. collision_removal = readU8(is);
  241. animation.deSerialize(is, protocol_ver);
  242. glow = readU8(is);
  243. object_collision = readU8(is);
  244. if (streamEndsBeforeParam<u16, readU16>(node.param0, is))
  245. return;
  246. node.param2 = readU8(is);
  247. node_tile = readU8(is);
  248. if (streamEndsBeforeParam<v3f, readV3F32>(drag, is))
  249. return;
  250. jitter.deSerialize(is);
  251. bounce.deSerialize(is);
  252. }