CParticle.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. #pragma once
  2. #include "CRenderable.h"
  3. #include "WallpaperEngine/Data/Model/Object.h"
  4. #include "WallpaperEngine/Render/Objects/Effects/CPass.h"
  5. #include "WallpaperEngine/Render/Wallpapers/CScene.h"
  6. #include "WallpaperEngine/Scripting/ScriptableObject.h"
  7. #include <functional>
  8. #include <glm/mat4x4.hpp>
  9. #include <glm/vec3.hpp>
  10. #include <glm/vec4.hpp>
  11. #include <memory>
  12. #include <optional>
  13. #include <random>
  14. #include <vector>
  15. using namespace WallpaperEngine;
  16. using namespace WallpaperEngine::Render;
  17. using namespace WallpaperEngine::Data::Model;
  18. namespace WallpaperEngine::Render::Objects {
  19. constexpr uint32_t DEFAULT_MAX_PARTICLES = 1000;
  20. struct ParticleInstance {
  21. glm::vec3 position { 0.0f };
  22. /** Where the particle was when this frame's operators started, collisionquad tests the step in between */
  23. glm::vec3 previousPosition { 0.0f };
  24. glm::vec3 velocity { 0.0f };
  25. glm::vec3 acceleration { 0.0f };
  26. glm::vec3 rotation { 0.0f };
  27. glm::vec3 angularVelocity { 0.0f };
  28. glm::vec3 angularAcceleration { 0.0f };
  29. glm::vec3 color { 1.0f };
  30. float alpha { 1.0f };
  31. float size { 20.0f };
  32. float frame { 0.0f }; // Current animation frame
  33. float lifetime { 1.0f }; // Total lifetime in seconds
  34. float age { 0.0f }; // Current age in seconds
  35. // Oscillator state (per-particle random values)
  36. // base is updated by alphafade/sizechange operators so oscillation combines properly
  37. struct {
  38. float frequency { 0.0f };
  39. float scale { 1.0f };
  40. float phase { 0.0f };
  41. float base { 1.0f };
  42. bool initialized { false };
  43. } oscillateAlpha, oscillateSize;
  44. struct {
  45. glm::vec3 frequency { 0.0f };
  46. glm::vec3 scale { 1.0f };
  47. glm::vec3 phase { 0.0f };
  48. bool initialized { false };
  49. } oscillatePosition;
  50. // Initial values for resets/multipliers
  51. struct {
  52. glm::vec3 color { 1.0f };
  53. float alpha { 1.0f };
  54. float size { 20.0f };
  55. float lifetime { 1.0f };
  56. } initial;
  57. /** Random 0..1 picked at spawn, WE's turbulence operator uses it for the particle's phase and speed */
  58. float seed { 0.0f };
  59. /** Unique per system, eventfollow children find the particle they follow by it (indices shift on compaction) */
  60. uint32_t id { 0 };
  61. /** WE's pool slot: the lowest one free at spawn, what orders particles handed to child control points */
  62. uint32_t slot { 0 };
  63. bool alive { false };
  64. float getLifetimePos () const { return lifetime > 0.0f ? (age / lifetime) : 1.0f; }
  65. // sub_140236CD0 kills a particle once lifetime < age, so it is still drawn on the frame it reaches its lifetime
  66. bool isAlive () const { return alive && lifetime != 0.0f && !(lifetime < age); }
  67. };
  68. struct ControlPointData {
  69. glm::vec3 position { 0.0f };
  70. /** Rotation and scale of the control point's matrix, emitters orient their shape and velocities with it */
  71. glm::mat3 orientation { 1.0f };
  72. glm::vec3 offset { 0.0f };
  73. bool linkMouse { false };
  74. bool worldSpace { false };
  75. /** Follows the parent system's control point parentIndex (child systems only) */
  76. bool followParent { false };
  77. /** Flag 8: takes the parent's control point as it is, without moving it into this system's space */
  78. bool copyUntransformed { false };
  79. /** Written by a remap component, the engine leaves it alone (WE's loader flag 0x10000) */
  80. bool remapOutput { false };
  81. int parentIndex { 0 };
  82. /** Movement per second over the last update, for inheritcontrolpointvelocity */
  83. glm::vec3 velocity { 0.0f };
  84. /** How far it moved since the last update (wallpaper64.exe keeps last frame's matrix at +64) */
  85. glm::vec3 movement { 0.0f };
  86. glm::vec3 previousPosition { 0.0f };
  87. bool hasPreviousPosition { false };
  88. };
  89. using EmitterFunc = std::function<void (std::vector<ParticleInstance>&, uint32_t&, float)>;
  90. using InitializerFunc = std::function<void (ParticleInstance&)>;
  91. using OperatorFunc = std::function<
  92. void (std::vector<ParticleInstance>&, uint32_t, const std::vector<ControlPointData>&, float, float)>;
  93. class CParticle final : public CRenderable, public Scripting::ScriptableObject {
  94. friend CObject;
  95. public:
  96. CParticle (Wallpapers::CScene& scene, const Particle& particle);
  97. ~CParticle ();
  98. void setup () override;
  99. void render () override;
  100. void update (float dt);
  101. [[nodiscard]] const Particle& getParticle () const;
  102. [[nodiscard]] const float& getBrightness () const override;
  103. [[nodiscard]] const float& getUserAlpha () const override;
  104. [[nodiscard]] const float& getAlpha () const override;
  105. [[nodiscard]] const glm::vec3& getColor () const override;
  106. [[nodiscard]] const glm::vec4& getColor4 () const override;
  107. [[nodiscard]] const glm::vec3& getCompositeColor () const override;
  108. [[nodiscard]] bool isPlaying () const override;
  109. protected:
  110. void setupEmitters ();
  111. void setupInitializers ();
  112. void setupOperators ();
  113. EmitterFunc createBoxEmitter (const ParticleEmitter& emitter);
  114. EmitterFunc createSphereEmitter (const ParticleEmitter& emitter);
  115. /** Spawn position from the emitter's shape offset and control point, sets m_emitOrientation */
  116. void placeSpawn (
  117. ParticleInstance& p, const ControlPointData* cp, int controlPointIndex, const glm::vec3& origin,
  118. glm::vec3& offset
  119. );
  120. [[nodiscard]] float
  121. sampleAudio (int mode, const glm::vec2& bounds, float exponent, int frequencyStart, int frequencyEnd) const;
  122. InitializerFunc createColorRandomInitializer (const ColorRandomInitializer& init);
  123. InitializerFunc createSizeRandomInitializer (const SizeRandomInitializer& init);
  124. InitializerFunc createAlphaRandomInitializer (const AlphaRandomInitializer& init);
  125. InitializerFunc createLifetimeRandomInitializer (const LifetimeRandomInitializer& init);
  126. InitializerFunc createVelocityRandomInitializer (const VelocityRandomInitializer& init);
  127. InitializerFunc createRotationRandomInitializer (const RotationRandomInitializer& init);
  128. InitializerFunc createAngularVelocityRandomInitializer (const AngularVelocityRandomInitializer& init);
  129. InitializerFunc createTurbulentVelocityRandomInitializer (const TurbulentVelocityRandomInitializer& init);
  130. InitializerFunc
  131. createMapSequenceAroundControlPointInitializer (const MapSequenceAroundControlPointInitializer& init);
  132. InitializerFunc createInheritInitialValueFromEventInitializer (const InheritInitialValueFromEventInitializer& init);
  133. InitializerFunc createInheritControlPointVelocityInitializer (const InheritControlPointVelocityInitializer& init);
  134. InitializerFunc createHsvColorRandomInitializer (const HsvColorRandomInitializer& init);
  135. InitializerFunc createColorListInitializer (const ColorListInitializer& init);
  136. InitializerFunc createPositionOffsetRandomInitializer (const PositionOffsetRandomInitializer& init);
  137. InitializerFunc
  138. createMapSequenceBetweenControlPointsInitializer (const MapSequenceBetweenControlPointsInitializer& init);
  139. InitializerFunc createRemapInitialValueInitializer (const RemapInitialValueInitializer& init);
  140. OperatorFunc createMovementOperator (const MovementOperator& op);
  141. OperatorFunc createAngularMovementOperator (const AngularMovementOperator& op);
  142. OperatorFunc createAlphaFadeOperator (const AlphaFadeOperator& op);
  143. OperatorFunc createSizeChangeOperator (const SizeChangeOperator& op);
  144. OperatorFunc createAlphaChangeOperator (const AlphaChangeOperator& op);
  145. OperatorFunc createColorChangeOperator (const ColorChangeOperator& op);
  146. OperatorFunc createTurbulenceOperator (const TurbulenceOperator& op);
  147. OperatorFunc createVortexOperator (const VortexOperator& op);
  148. OperatorFunc createControlPointAttractOperator (const ControlPointAttractOperator& op);
  149. OperatorFunc createOscillateAlphaOperator (const OscillateAlphaOperator& op);
  150. OperatorFunc createOscillateSizeOperator (const OscillateSizeOperator& op);
  151. OperatorFunc createOscillatePositionOperator (const OscillatePositionOperator& op);
  152. OperatorFunc createInheritValueFromEventOperator (const InheritValueFromEventOperator& op);
  153. OperatorFunc createCapVelocityOperator (const CapVelocityOperator& op);
  154. OperatorFunc createBoidsOperator (const BoidsOperator& op);
  155. OperatorFunc createRemapValueOperator (const RemapValueOperator& op);
  156. OperatorFunc createMaintainDistanceToControlPointOperator (const MaintainDistanceToControlPointOperator& op);
  157. OperatorFunc
  158. createMaintainDistanceBetweenControlPointsOperator (const MaintainDistanceBetweenControlPointsOperator& op);
  159. OperatorFunc createReduceMovementNearControlPointOperator (const ReduceMovementNearControlPointOperator& op);
  160. OperatorFunc createCollisionOperator (const CollisionOperator& op);
  161. /** A remap input as wallpaper64.exe reads it during the simulation (sub_14023FBC0 case 19), in its y-up space */
  162. [[nodiscard]] glm::vec3 remapOperatorInput (const ParticleRemap& remap, const ParticleInstance& p) const;
  163. /** The same for remapinitialvalue (sub_14023B340 case 15), which reads the particle's base values */
  164. [[nodiscard]] glm::vec3 remapInitialInput (const ParticleRemap& remap, ParticleInstance& p);
  165. /** Layer transform translation in wallpaper64.exe's scene coordinates (remap input layerorigin) */
  166. [[nodiscard]] glm::vec3 remapLayerOrigin () const;
  167. /** Control point position in wallpaper64.exe's y-up particle space */
  168. [[nodiscard]] glm::vec3 controlPointWE (int index) const;
  169. /** A position or direction as the vertex buffer takes it, see m_drawFlipY */
  170. [[nodiscard]] glm::vec3 drawVector (const glm::vec3& value) const;
  171. /** A control point's matrix before the system's transform: its offset, or the instance override's point/angles */
  172. [[nodiscard]] glm::mat4 localControlPointMatrix (size_t index) const;
  173. /** Operators scale some forces by how long frames take (sub_140236CD0): dt * min(1, 0.025 / frame time)^0.7 */
  174. [[nodiscard]] float frameScaledDelta (float dt) const;
  175. /** wallpaper64.exe's time since the layer became visible (+1904), the remap input layertime */
  176. [[nodiscard]] float layerTime () const;
  177. void renderSprites ();
  178. void renderRope ();
  179. void buildRopeTrail (uint32_t& vertexIndex, uint32_t& indexOffset);
  180. [[nodiscard]] float ropeUVScale () const;
  181. void setupPass ();
  182. void setupGeometryCallbacks ();
  183. void setupParticleUniforms ();
  184. void updateMatrices ();
  185. void updateParticleViewProjection ();
  186. void updateParticleRenderVars ();
  187. void prewarm (double now);
  188. /** base is the matrix the parent leaves for its children, identity for top level systems */
  189. void draw (const glm::mat4& base);
  190. /** The object's transform (origin, parallax, angles, scale), what WE keeps at +928 for top level systems */
  191. [[nodiscard]] glm::mat4 objectMatrix () const;
  192. /** The matrix stack top WE simulates this system with (sub_140229760 / sub_140229810) */
  193. void updateFrame ();
  194. /** wallpaper64.exe sub_14022E3E0 */
  195. void updateControlPoints ();
  196. [[nodiscard]] DynamicValue* emitterCountOverride () const;
  197. /** wallpaper64.exe sub_14022BD40 / sub_14022F890: whether the instanceoverride color applies, and how */
  198. void refreshColorOverride ();
  199. /** wallpaper64.exe sub_14022A360: where an event child sits for one of this system's particles */
  200. [[nodiscard]] glm::mat4 eventPlacement (const ParticleChild& child, const glm::vec3& position) const;
  201. /** A child system: owned and driven by its parent, placed in the parent's particle space */
  202. CParticle (CParticle& parent, const ParticleChild& child);
  203. void setupChildren ();
  204. void updateChildren (float dt);
  205. void spawnEventChildren (ParticleChildType type, const ParticleInstance& particle, bool alive);
  206. void clearEventChildren ();
  207. void placeChild (const glm::mat4& placement);
  208. [[nodiscard]] const ParticleInstance* findParticle (uint32_t id) const;
  209. void passControlPoints (CParticle& child, const ParticleChild& definition) const;
  210. /** Starts over as a freshly spawned system, for pooled event children */
  211. void restart ();
  212. [[nodiscard]] bool isFinished () const;
  213. [[nodiscard]] bool emittersExhausted () const;
  214. private:
  215. const Particle& m_particle;
  216. std::vector<ParticleInstance> m_particles;
  217. uint32_t m_particleCount { 0 };
  218. uint32_t m_maxParticles { DEFAULT_MAX_PARTICLES };
  219. std::vector<EmitterFunc> m_emitters;
  220. std::vector<InitializerFunc> m_initializers;
  221. std::vector<OperatorFunc> m_operators;
  222. std::vector<ControlPointData> m_controlPoints;
  223. std::vector<float> m_vertices;
  224. std::vector<uint32_t> m_indices;
  225. // Reused across frames (resized, not reallocated) so renderRope() doesn't heap-allocate every frame
  226. std::vector<glm::vec3> m_splinePositions;
  227. std::vector<float> m_splineSizes;
  228. std::vector<glm::vec4> m_splineColors;
  229. std::vector<float> m_cumulativeArcLength;
  230. double m_time { 0.0 };
  231. bool m_prewarmed { false };
  232. // Mouse-linked systems run on unscaled real time so cursor trails track 1:1 regardless of --speed
  233. bool m_hasMouseControlPoint { false };
  234. Effects::CPass* m_pass { nullptr };
  235. std::unique_ptr<ImageEffectPassOverride> m_passOverride;
  236. std::shared_ptr<FBOProvider> m_passFBOProvider;
  237. TextureMap m_passBinds;
  238. GLsizei m_activeIndexCount { 0 };
  239. // REFRACT: copy of scene FBO to avoid read/write conflict
  240. bool m_hasRefract { false };
  241. std::shared_ptr<CFBO> m_refractFBO;
  242. GLuint m_vao { 0 };
  243. GLuint m_vbo { 0 };
  244. GLuint m_ebo { 0 };
  245. GLint m_prevVAO { 0 };
  246. // Particle-specific uniform data (stored here, pointed to by CPass)
  247. glm::mat4 m_modelMatrix { 1.0f };
  248. /** 2D scenes: vertices are uploaded in WE's y-up particle frame, m_modelMatrix carries the flip */
  249. bool m_drawFlipY = false;
  250. glm::mat4 m_modelMatrixInverse { 1.0f };
  251. glm::mat4 m_mvpMatrix { 1.0f };
  252. glm::mat4 m_mvpMatrixInverse { 1.0f };
  253. glm::mat4 m_viewProjectionMatrix { 1.0f };
  254. glm::vec3 m_orientationUp { 0.0f, 1.0f, 0.0f };
  255. glm::vec3 m_orientationRight { 1.0f, 0.0f, 0.0f };
  256. glm::vec3 m_orientationForward { 0.0f, 0.0f, 1.0f };
  257. glm::vec3 m_viewUp { 0.0f, 1.0f, 0.0f };
  258. glm::vec3 m_viewRight { 1.0f, 0.0f, 0.0f };
  259. glm::vec3 m_eyePosition { 0.0f, 0.0f, 1000.0f };
  260. glm::vec4 m_renderVar0 { 0.0f };
  261. glm::vec4 m_renderVar1 { 0.0f };
  262. int m_spritesheetCols { 0 };
  263. int m_spritesheetRows { 0 };
  264. int m_spritesheetFrames { 0 };
  265. float m_spritesheetDuration { 1.0f };
  266. float m_overbright { 1.0f };
  267. float m_refractAmount { 0.05f }; // Default from shader annotation
  268. bool m_useTrailRenderer { false };
  269. float m_trailLength { 0.05f };
  270. float m_trailMaxLength { 10.0f };
  271. float m_trailMinLength { 0.0f };
  272. // Rope renderer (rope + ropetrail both use genericropeparticle shader)
  273. bool m_useRopeRenderer { false };
  274. int m_ropeSubdivision { 4 }; // Catmull-Rom subdivisions between points (smoothing)
  275. int m_ropeSegments { 4 }; // ropetrail: historical position snapshots per particle
  276. float m_ropeUVScale { 1.0f };
  277. bool m_ropeUVScrolling { false };
  278. bool m_ropeUVSmoothing { true }; // rope only
  279. bool m_uniformLifetimes { false }; // true when lifetime min==max (enables UV smoothing)
  280. bool m_trailFadeAlpha { false };
  281. bool m_trailFadeSize { false };
  282. /** ropetrail: m_ropeSegments past positions per particle slot, newest first, compacted with the particles */
  283. std::vector<glm::vec3> m_trailHistory;
  284. /** History entries filled so far and pushes since spawn (uvscrolling) per particle slot */
  285. std::vector<uint16_t> m_trailCount;
  286. std::vector<uint16_t> m_trailScroll;
  287. /** Counts down to the next history push, WE starts it at 0 so the first update pushes */
  288. float m_trailTimer { 0.0f };
  289. float m_trailInterval { 1.0f };
  290. static constexpr int SPRITE_FLOATS_PER_VERTEX = 17;
  291. static constexpr int ROPE_FLOATS_PER_VERTEX = 26;
  292. std::mt19937 m_rng;
  293. /** Only drawn when an operator needs it, so systems without one keep the same random sequence */
  294. bool m_usesParticleSeed = false;
  295. bool m_initialized { false };
  296. Playback m_lastPlayback { Playback::Playing };
  297. struct EventChildSlot {
  298. const ParticleChild* child;
  299. std::vector<std::unique_ptr<CParticle>> active;
  300. std::vector<std::unique_ptr<CParticle>> pool;
  301. };
  302. CParticle* m_parent { nullptr };
  303. const ParticleChild* m_childDefinition { nullptr };
  304. /**
  305. * WE's +928 matrix. Top level: the object's transform. Static children: the child transform. Event children:
  306. * the spawning particle's position and the child transform, in the parent's space or the world (eventPlacement)
  307. */
  308. glm::mat4 m_placement { 1.0f };
  309. /** The full transform particles are simulated in, the world for world space systems */
  310. glm::mat4 m_frame { 1.0f };
  311. /** Particle flags bit 0: particles are kept in world space, moving the system leaves them behind */
  312. bool m_worldSpace { false };
  313. /** Orientation of the control point the current emitter spawns from, the velocity initializers apply it */
  314. glm::mat3 m_emitOrientation { 1.0f };
  315. /** Event children: the parent's particle that spawned it, while it lives (eventfollow ones move with it) */
  316. std::optional<uint32_t> m_eventId;
  317. /** That particle as last seen, what the inherit components read */
  318. ParticleInstance m_eventParticle;
  319. bool m_hasEventParticle { false };
  320. bool m_following { false };
  321. bool m_emissionStopped { false };
  322. /** Emission time since (re)start, for telling when every emitter is done */
  323. float m_emitterTime { 0.0f };
  324. std::vector<std::unique_ptr<CParticle>> m_staticChildren;
  325. std::vector<EventChildSlot> m_eventChildren;
  326. bool m_hasBirthEvents { false };
  327. bool m_hasDeathEvents { false };
  328. /** No events while prewarming, like WE */
  329. bool m_prewarming { false };
  330. uint32_t m_nextParticleId { 0 };
  331. /** Pool slots taken by live particles, see ParticleInstance::slot */
  332. std::vector<uint8_t> m_slotUsed;
  333. std::vector<uint32_t> m_births;
  334. std::vector<ParticleInstance> m_deaths;
  335. /** Slots handed out since the last restart, wallpaper64.exe's particle count (+832), the boids sample stride */
  336. uint32_t m_slotExtent { 0 };
  337. /**
  338. * wallpaper64.exe never clears a dead pool slot: movement keeps integrating it and boids still reads it as a
  339. * neighbor (see createBoidsOperator). Kept per slot for systems with boids, the only operator that reads others
  340. */
  341. std::vector<ParticleInstance> m_ghosts;
  342. std::vector<uint8_t> m_ghostUsed;
  343. bool m_hasBoids { false };
  344. /** wallpaper64.exe +1008: simulated time since the system started, the remapinitialvalue particlesystemtime */
  345. float m_systemTime { 0.0f };
  346. /** Top level systems only, see layerTime () */
  347. float m_layerTime { 0.0f };
  348. /** Real time between the last two rendered frames and the frame count, for frameScaledDelta and boids */
  349. float m_frameDelta { 0.0f };
  350. float m_lastRealTime { 0.0f };
  351. uint32_t m_frameCounter { 0 };
  352. struct ColorOverride {
  353. /** override color given and further than 0.9/255 from the particle file's reference color */
  354. bool active = false;
  355. /** spawn color every particle starts from (sub_1401D15A0), the override color itself only in tint mode */
  356. glm::vec3 tint = glm::vec3 (1.0f);
  357. glm::vec3 hsv = glm::vec3 (0.0f);
  358. /** override HSV minus the reference HSV, what colorrandom and colorchange shift their colors by */
  359. glm::vec3 shift = glm::vec3 (0.0f);
  360. };
  361. ColorOverride m_colorOverride {};
  362. /** System flag 2 in wallpaper64.exe: angularvelocityrandom or angularmovement, angular speed means something */
  363. bool m_hasAngularVelocity { false };
  364. /** sub_14023FBC0 restores these from the spawn values before the operators when a remapvalue writes them */
  365. bool m_resetSizeFromBase { false };
  366. bool m_resetAlphaFromBase { false };
  367. bool m_resetColorFromBase { false };
  368. /** collisionquad needs where particles were before this frame's operators */
  369. bool m_tracksPreviousPosition { false };
  370. };
  371. } // namespace WallpaperEngine::Render::Objects