]> git.lizzy.rs Git - minetest.git/blobdiff - src/particles.h
Remove shader caching hack. (#12991)
[minetest.git] / src / particles.h
index 13e73e861aaa94ed75dc820d47100cf72bcb57ce..3061deb83ecf5a5d9d6868f143366b17122794a8 100644 (file)
@@ -17,198 +17,414 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
-#ifndef PARTICLES_HEADER
-#define PARTICLES_HEADER
-
-#include <iostream>
-#include "irrlichttypes_extrabloated.h"
-#include "client/tile.h"
-#include "localplayer.h"
-#include "environment.h"
+#pragma once
+
+#include <string>
+#include <sstream>
+#include <vector>
+#include <ctgmath>
+#include <type_traits>
+#include "irrlichttypes_bloated.h"
 #include "tileanimation.h"
+#include "mapnode.h"
+#include "util/serialize.h"
+#include "util/numeric.h"
 
-struct ClientEvent;
-class ParticleManager;
-class ClientEnvironment;
-struct MapNode;
-struct ContentFeatures;
+// This file defines the particle-related structures that both the server and
+// client need. The ParticleManager and rendering is in client/particles.h
 
-class Particle : public scene::ISceneNode
+namespace ParticleParamTypes
 {
-       public:
-       Particle(
-               IGameDef* gamedef,
-               LocalPlayer *player,
-               ClientEnvironment *env,
-               v3f pos,
-               v3f velocity,
-               v3f acceleration,
-               float expirationtime,
-               float size,
-               bool collisiondetection,
-               bool collision_removal,
-               bool vertical,
-               video::ITexture *texture,
-               v2f texpos,
-               v2f texsize,
-               const struct TileAnimationParams &anim,
-               u8 glow,
-               video::SColor color = video::SColor(0xFFFFFFFF)
-       );
-       ~Particle();
-
-       virtual const aabb3f &getBoundingBox() const
-       {
-               return m_box;
+       template <bool cond, typename T>
+       using enableIf = typename std::enable_if<cond, T>::type;
+       // std::enable_if_t does not appear to be present in GCC????
+       // std::is_enum_v also missing. wtf. these are supposed to be
+       // present as of c++14
+
+       template<typename T> using BlendFunction = T(float,T,T);
+       #define DECL_PARAM_SRZRS(type) \
+               void serializeParameterValue  (std::ostream& os, type   v); \
+               void deSerializeParameterValue(std::istream& is, type&  r);
+       #define DECL_PARAM_OVERLOADS(type) DECL_PARAM_SRZRS(type) \
+               type interpolateParameterValue(float  fac,  const type a, const type b); \
+               type pickParameterValue       (float* facs, const type a, const type b);
+
+       DECL_PARAM_OVERLOADS(u8);  DECL_PARAM_OVERLOADS(s8);
+       DECL_PARAM_OVERLOADS(u16); DECL_PARAM_OVERLOADS(s16);
+       DECL_PARAM_OVERLOADS(u32); DECL_PARAM_OVERLOADS(s32);
+       DECL_PARAM_OVERLOADS(f32);
+       DECL_PARAM_OVERLOADS(v2f);
+       DECL_PARAM_OVERLOADS(v3f);
+
+       /* C++ is a strongly typed language. this means that enums cannot be implicitly
+        * cast to integers, as they can be in C. while this may sound good in principle,
+        * it means that our normal serialization functions cannot be called on
+        * enumerations unless they are explicitly cast to a particular type first. this
+        * is problematic, because in C++ enums can have any integral type as an underlying
+        * type, and that type would need to be named everywhere an enumeration is
+        * de/serialized.
+        *
+        * this is obviously not cool, both in terms of writing legible, succinct code,
+        * and in terms of robustness: the underlying type might be changed at some point,
+        * e.g. if a bitmask gets too big for its britches. we could use an equivalent of
+        * `std::to_underlying(value)` everywhere we need to deal with enumerations, but
+        * that's hideous and unintuitive. instead, we supply the following functions to
+        * transparently map enumeration types to their underlying values. */
+
+       template <typename E, enableIf<std::is_enum<E>::value, bool> = true>
+       void serializeParameterValue(std::ostream& os, E k) {
+               serializeParameterValue(os, (std::underlying_type_t<E>)k);
+       }
+
+       template <typename E, enableIf<std::is_enum<E>::value, bool> = true>
+       void deSerializeParameterValue(std::istream& is, E& k) {
+               std::underlying_type_t<E> v;
+               deSerializeParameterValue(is, v);
+               k = (E)v;
        }
 
-       virtual u32 getMaterialCount() const
+       /* this is your brain on C++. */
+
+       template <typename T, size_t PN>
+       struct Parameter
+       {
+               using ValType = T;
+               using pickFactors = float[PN];
+
+               T val = T();
+               using This = Parameter<T, PN>;
+
+               Parameter() = default;
+
+               template <typename... Args>
+               Parameter(Args... args) : val(args...) {}
+
+               virtual void serialize(std::ostream &os) const
+                       { serializeParameterValue  (os, this->val); }
+               virtual void deSerialize(std::istream &is)
+                       { deSerializeParameterValue(is, this->val); }
+
+               virtual T interpolate(float fac, const This& against) const
+               {
+                       return interpolateParameterValue(fac, this->val, against.val);
+               }
+
+               static T pick(float* f, const This& a, const This& b)
+               {
+                       return pickParameterValue(f, a.val, b.val);
+               }
+
+               operator T() const { return val; }
+               T operator=(T b) { return val = b; }
+
+       };
+
+       template <typename T> T numericalBlend(float fac, T min, T max)
+               { return min + ((max - min) * fac); }
+
+       template <typename T, size_t N>
+       struct VectorParameter : public Parameter<T,N> {
+               using This = VectorParameter<T,N>;
+               template <typename... Args>
+               VectorParameter(Args... args) : Parameter<T,N>(args...) {}
+       };
+
+       template <typename T, size_t PN>
+       inline std::string dump(const Parameter<T,PN>& p)
        {
-               return 1;
+               return std::to_string(p.val);
        }
 
-       virtual video::SMaterial& getMaterial(u32 i)
+       template <typename T, size_t N>
+       inline std::string dump(const VectorParameter<T,N>& v)
        {
-               return m_material;
+               std::ostringstream oss;
+               if (N == 3)
+                       oss << PP(v.val);
+               else
+                       oss << PP2(v.val);
+               return oss.str();
        }
 
-       virtual void OnRegisterSceneNode();
-       virtual void render();
-
-       void step(float dtime);
-
-       bool get_expired ()
-       { return m_expiration < m_time; }
-
-private:
-       void updateLight();
-       void updateVertices();
-
-       video::S3DVertex m_vertices[4];
-       float m_time = 0.0f;
-       float m_expiration;
-
-       ClientEnvironment *m_env;
-       IGameDef *m_gamedef;
-       aabb3f m_box;
-       aabb3f m_collisionbox;
-       video::SMaterial m_material;
-       v2f m_texpos;
-       v2f m_texsize;
-       v3f m_pos;
-       v3f m_velocity;
-       v3f m_acceleration;
-       LocalPlayer *m_player;
-       float m_size;
-       //! Color without lighting
-       video::SColor m_base_color;
-       //! Final rendered color
-       video::SColor m_color;
-       bool m_collisiondetection;
-       bool m_collision_removal;
-       bool m_vertical;
-       v3s16 m_camera_offset;
-       struct TileAnimationParams m_animation;
-       float m_animation_time = 0.0f;
-       int m_animation_frame = 0;
-       u8 m_glow;
-};
+       using u8Parameter  = Parameter<u8,  1>; using s8Parameter  = Parameter<s8,  1>;
+       using u16Parameter = Parameter<u16, 1>; using s16Parameter = Parameter<s16, 1>;
+       using u32Parameter = Parameter<u32, 1>; using s32Parameter = Parameter<s32, 1>;
 
-class ParticleSpawner
-{
-       public:
-       ParticleSpawner(IGameDef* gamedef,
-               LocalPlayer *player,
-               u16 amount,
-               float time,
-               v3f minp, v3f maxp,
-               v3f minvel, v3f maxvel,
-               v3f minacc, v3f maxacc,
-               float minexptime, float maxexptime,
-               float minsize, float maxsize,
-               bool collisiondetection,
-               bool collision_removal,
-               u16 attached_id,
-               bool vertical,
-               video::ITexture *texture,
-               u32 id,
-               const struct TileAnimationParams &anim, u8 glow,
-               ParticleManager* p_manager);
-
-       ~ParticleSpawner();
-
-       void step(float dtime, ClientEnvironment *env);
-
-       bool get_expired ()
-       { return (m_amount <= 0) && m_spawntime != 0; }
-
-       private:
-       ParticleManager* m_particlemanager;
-       float m_time;
-       IGameDef *m_gamedef;
-       LocalPlayer *m_player;
-       u16 m_amount;
-       float m_spawntime;
-       v3f m_minpos;
-       v3f m_maxpos;
-       v3f m_minvel;
-       v3f m_maxvel;
-       v3f m_minacc;
-       v3f m_maxacc;
-       float m_minexptime;
-       float m_maxexptime;
-       float m_minsize;
-       float m_maxsize;
-       video::ITexture *m_texture;
-       std::vector<float> m_spawntimes;
-       bool m_collisiondetection;
-       bool m_collision_removal;
-       bool m_vertical;
-       u16 m_attached_id;
-       struct TileAnimationParams m_animation;
-       u8 m_glow;
-};
+       using f32Parameter = Parameter<f32, 1>;
 
-/**
- * Class doing particle as well as their spawners handling
- */
-class ParticleManager
-{
-friend class ParticleSpawner;
-public:
-       ParticleManager(ClientEnvironment* env);
-       ~ParticleManager();
+       using v2fParameter = VectorParameter<v2f, 2>;
+       using v3fParameter = VectorParameter<v3f, 3>;
 
-       void step (float dtime);
+       template <typename T>
+       struct RangedParameter
+       {
+               using ValType = T;
+               using This = RangedParameter<T>;
+
+               T min, max;
+               f32 bias = 0;
+
+               RangedParameter() = default;
+               RangedParameter(T _min, T _max)            : min(_min),  max(_max)  {}
+               template <typename M> 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
+               {
+                       min.serialize(os);
+                       max.serialize(os);
+               }
+               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);
+               }
+
+               This interpolate(float fac, const This against) const
+               {
+                       This r;
+                       r.min = min.interpolate(fac, against.min);
+                       r.max = max.interpolate(fac, against.max);
+                       r.bias = bias;
+                       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);
+               }
+
+       };
+
+       template <typename T>
+       inline std::string dump(const RangedParameter<T>& r)
+       {
+               std::ostringstream s;
+               s << "range<" << dump(r.min) << " ~ " << dump(r.max);
+               if (r.bias != 0)
+                       s << " :: " << r.bias;
+               s << ">";
+               return s.str();
+       }
 
-       void handleParticleEvent(ClientEvent *event, Client *client,
-                       LocalPlayer *player);
+       enum class TweenStyle : u8 { fwd, rev, pulse, flicker };
 
-       void addDiggingParticles(IGameDef *gamedef, LocalPlayer *player, v3s16 pos,
-               const MapNode &n, const ContentFeatures &f);
+       template <typename T>
+       struct TweenedParameter
+       {
+               using ValType = T;
+               using This = TweenedParameter<T>;
+
+               TweenStyle style = TweenStyle::fwd;
+               u16 reps = 1;
+               f32 beginning = 0.0f;
+
+               T start, end;
+
+               TweenedParameter() = default;
+               TweenedParameter(T _start, T _end)          : start(_start),  end(_end) {}
+               template <typename M> 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;
+
+                               // 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<u8>(style));
+                       writeU16(os, reps);
+                       writeF32(os, beginning);
+                       start.serialize(os);
+                       end.serialize(os);
+               }
+               void deSerialize(std::istream &is)
+               {
+                       style = static_cast<TweenStyle>(readU8(is));
+                       reps = readU16(is);
+                       beginning = readF32(is);
+                       start.deSerialize(is);
+                       end.deSerialize(is);
+               }
+       };
+
+       template <typename T>
+       inline std::string dump(const TweenedParameter<T>& t)
+       {
+               std::ostringstream s;
+               const char* icon;
+               switch (t.style) {
+                       case TweenStyle::fwd: icon = "→"; break;
+                       case TweenStyle::rev: icon = "←"; break;
+                       case TweenStyle::pulse: icon = "↔"; break;
+                       case TweenStyle::flicker: icon = "↯"; break;
+               }
+               s << "tween<";
+               if (t.reps != 1)
+                       s << t.reps << "x ";
+               s << dump(t.start) << " "<<icon<<" " << dump(t.end) << ">";
+               return s.str();
+       }
 
-       void addPunchingParticles(IGameDef *gamedef, LocalPlayer *player, v3s16 pos,
-               const MapNode &n, const ContentFeatures &f);
+       enum class AttractorKind : u8 { none, point, line, plane };
+       enum class BlendMode     : u8 { alpha, add, sub, screen  };
 
-       void addNodeParticle(IGameDef *gamedef, LocalPlayer *player, v3s16 pos,
-               const MapNode &n, const ContentFeatures &f);
+       // these are consistently-named convenience aliases to make code more readable without `using ParticleParamTypes` declarations
+       using v3fRange = RangedParameter<v3fParameter>;
+       using f32Range = RangedParameter<f32Parameter>;
 
-protected:
-       void addParticle(Particle* toadd);
+       using v2fTween      = TweenedParameter<v2fParameter>;
+       using v3fTween      = TweenedParameter<v3fParameter>;
+       using f32Tween      = TweenedParameter<f32Parameter>;
+       using v3fRangeTween = TweenedParameter<v3fRange>;
+       using f32RangeTween = TweenedParameter<f32Range>;
 
-private:
+       #undef DECL_PARAM_SRZRS
+       #undef DECL_PARAM_OVERLOADS
+}
 
-       void stepParticles (float dtime);
-       void stepSpawners (float dtime);
+struct ParticleTexture
+{
+       bool animated = false;
+       ParticleParamTypes::BlendMode blendmode = ParticleParamTypes::BlendMode::alpha;
+       TileAnimationParams animation;
+       ParticleParamTypes::f32Tween alpha{1.0f};
+       ParticleParamTypes::v2fTween scale{v2f(1.0f)};
+};
 
-       void clearAll ();
+struct ServerParticleTexture : public ParticleTexture
+{
+       std::string string;
+       void serialize(std::ostream &os, u16 protocol_ver, bool newPropertiesOnly = false) const;
+       void deSerialize(std::istream &is, u16 protocol_ver, bool newPropertiesOnly = false);
+};
 
-       std::vector<Particle*> m_particles;
-       std::map<u32, ParticleSpawner*> m_particle_spawners;
+struct CommonParticleParams
+{
+       bool collisiondetection = false;
+       bool collision_removal = false;
+       bool object_collision = false;
+       bool vertical = false;
+       ServerParticleTexture texture;
+       struct TileAnimationParams animation;
+       u8 glow = 0;
+       MapNode node;
+       u8 node_tile = 0;
+
+       CommonParticleParams() {
+               animation.type = TAT_NONE;
+               node.setContent(CONTENT_IGNORE);
+       }
 
-       ClientEnvironment* m_env;
-       std::mutex m_particle_list_lock;
-       std::mutex m_spawner_list_lock;
+       /* This helper is useful for copying params from
+        * ParticleSpawnerParameters to ParticleParameters */
+       inline void copyCommon(CommonParticleParams &to) const {
+               to.collisiondetection = collisiondetection;
+               to.collision_removal = collision_removal;
+               to.object_collision = object_collision;
+               to.vertical = vertical;
+               to.texture = texture;
+               to.animation = animation;
+               to.glow = glow;
+               to.node = node;
+               to.node_tile = node_tile;
+       }
 };
 
-#endif
+struct ParticleParameters : CommonParticleParams
+{
+       v3f pos, vel, acc, drag;
+       f32 size = 1, expirationtime = 1;
+       ParticleParamTypes::f32Range bounce;
+       ParticleParamTypes::v3fRange jitter;
+
+       void serialize(std::ostream &os, u16 protocol_ver) const;
+       void deSerialize(std::istream &is, u16 protocol_ver);
+};
+
+struct ParticleSpawnerParameters : CommonParticleParams
+{
+       u16 amount = 1;
+       f32 time = 1;
+
+       std::vector<ServerParticleTexture> texpool;
+
+       ParticleParamTypes::v3fRangeTween
+               pos, vel, acc, drag, radius, jitter;
+
+       ParticleParamTypes::AttractorKind
+               attractor_kind;
+       ParticleParamTypes::v3fTween
+               attractor_origin, attractor_direction;
+       // object IDs
+       u16 attractor_attachment = 0,
+           attractor_direction_attachment = 0;
+       // do particles disappear when they cross the attractor threshold?
+       bool attractor_kill = true;
+
+       ParticleParamTypes::f32RangeTween
+               exptime{1.0f},
+               size   {1.0f},
+               attract{0.0f},
+               bounce {0.0f};
+
+       // For historical reasons no (de-)serialization methods here
+};