X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fparticles.h;h=3061deb83ecf5a5d9d6868f143366b17122794a8;hb=e84d259ec7fcc00466bd947070cb4ed77cd83c52;hp=dda84385c1bf49df9571b62823153b26506adf71;hpb=fefa148d694364cb4c60fbb4ffdc0f8ae6388df6;p=minetest.git diff --git a/src/particles.h b/src/particles.h index dda84385c..3061deb83 100644 --- a/src/particles.h +++ b/src/particles.h @@ -17,181 +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 - -#define DIGGING_PARTICLES_AMOUNT 10 +#pragma once + +#include +#include +#include +#include +#include +#include "irrlichttypes_bloated.h" +#include "tileanimation.h" +#include "mapnode.h" +#include "util/serialize.h" +#include "util/numeric.h" + +// This file defines the particle-related structures that both the server and +// client need. The ParticleManager and rendering is in client/particles.h + +namespace ParticleParamTypes +{ + template + using enableIf = typename std::enable_if::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 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 ::value, bool> = true> + void serializeParameterValue(std::ostream& os, E k) { + serializeParameterValue(os, (std::underlying_type_t)k); + } -#include -#include "irrlichttypes_extrabloated.h" -#include "client/tile.h" -#include "localplayer.h" -#include "environment.h" + template ::value, bool> = true> + void deSerializeParameterValue(std::istream& is, E& k) { + std::underlying_type_t v; + deSerializeParameterValue(is, v); + k = (E)v; + } -struct ClientEvent; -class ParticleManager; + /* this is your brain on C++. */ -class Particle : public scene::ISceneNode -{ - public: - Particle( - IGameDef* gamedef, - scene::ISceneManager* mgr, - LocalPlayer *player, - ClientEnvironment *env, - v3f pos, - v3f velocity, - v3f acceleration, - float expirationtime, - float size, - bool collisiondetection, - bool vertical, - video::ITexture *texture, - v2f texpos, - v2f texsize - ); - ~Particle(); - - virtual const aabb3f &getBoundingBox() const + template + struct Parameter { - return m_box; - } + using ValType = T; + using pickFactors = float[PN]; + + T val = T(); + using This = Parameter; + + Parameter() = default; + + template + 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 u32 getMaterialCount() const + 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 T numericalBlend(float fac, T min, T max) + { return min + ((max - min) * fac); } + + template + struct VectorParameter : public Parameter { + using This = VectorParameter; + template + VectorParameter(Args... args) : Parameter(args...) {} + }; + + template + inline std::string dump(const Parameter& p) { - return 1; + return std::to_string(p.val); } - virtual video::SMaterial& getMaterial(u32 i) + template + inline std::string dump(const VectorParameter& 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; - 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; - u8 m_light; - bool m_collisiondetection; - bool m_vertical; - v3s16 m_camera_offset; -}; + using u8Parameter = Parameter; using s8Parameter = Parameter; + using u16Parameter = Parameter; using s16Parameter = Parameter; + using u32Parameter = Parameter; using s32Parameter = Parameter; -class ParticleSpawner -{ - public: - ParticleSpawner(IGameDef* gamedef, - scene::ISceneManager *smgr, - 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 vertical, - video::ITexture *texture, - u32 id, - 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; - scene::ISceneManager *m_smgr; - 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 m_spawntimes; - bool m_collisiondetection; - bool m_vertical; + using f32Parameter = Parameter; -}; + using v2fParameter = VectorParameter; + using v3fParameter = VectorParameter; -/** - * Class doing particle as well as their spawners handling - */ -class ParticleManager -{ -friend class ParticleSpawner; -public: - ParticleManager(ClientEnvironment* env); - ~ParticleManager(); + template + struct RangedParameter + { + using ValType = T; + using This = RangedParameter; + + T min, max; + f32 bias = 0; + + RangedParameter() = default; + 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 + { + 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 + inline std::string dump(const RangedParameter& r) + { + std::ostringstream s; + s << "range<" << dump(r.min) << " ~ " << dump(r.max); + if (r.bias != 0) + s << " :: " << r.bias; + s << ">"; + return s.str(); + } - void step (float dtime); + enum class TweenStyle : u8 { fwd, rev, pulse, flicker }; - void handleParticleEvent(ClientEvent *event,IGameDef *gamedef, - scene::ISceneManager* smgr, LocalPlayer *player); + template + struct TweenedParameter + { + using ValType = T; + using This = TweenedParameter; + + 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 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(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); + } + }; + + template + inline std::string dump(const TweenedParameter& 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) << " "<"; + return s.str(); + } - void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + enum class AttractorKind : u8 { none, point, line, plane }; + enum class BlendMode : u8 { alpha, add, sub, screen }; - void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + // these are consistently-named convenience aliases to make code more readable without `using ParticleParamTypes` declarations + using v3fRange = RangedParameter; + using f32Range = RangedParameter; - void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + using v2fTween = TweenedParameter; + using v3fTween = TweenedParameter; + using f32Tween = TweenedParameter; + using v3fRangeTween = TweenedParameter; + using f32RangeTween = TweenedParameter; -protected: - void addParticle(Particle* toadd); + #undef DECL_PARAM_SRZRS + #undef DECL_PARAM_OVERLOADS +} -private: +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 stepParticles (float dtime); - void stepSpawners (float dtime); +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); +}; - void clearAll (); +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); + } - std::vector m_particles; - std::map m_particle_spawners; + /* 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; + } +}; - ClientEnvironment* m_env; - Mutex m_particle_list_lock; - Mutex m_spawner_list_lock; +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); }; -#endif +struct ParticleSpawnerParameters : CommonParticleParams +{ + u16 amount = 1; + f32 time = 1; + + std::vector 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 +};