| 1 | // Copyright (C) 2016 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #ifndef PARTICLESYSTEM_H |
| 5 | #define PARTICLESYSTEM_H |
| 6 | |
| 7 | // |
| 8 | // W A R N I N G |
| 9 | // ------------- |
| 10 | // |
| 11 | // This file is not part of the Qt API. It exists purely as an |
| 12 | // implementation detail. This header file may change from version to |
| 13 | // version without notice, or even be removed. |
| 14 | // |
| 15 | // We mean it. |
| 16 | // |
| 17 | |
| 18 | #include <QtQuick/QQuickItem> |
| 19 | #include <QElapsedTimer> |
| 20 | #include <QVector> |
| 21 | #include <QVarLengthArray> |
| 22 | #include <QHash> |
| 23 | #include <QSet> |
| 24 | #include <QPointer> |
| 25 | #include <private/qquicksprite_p.h> |
| 26 | #include <QAbstractAnimation> |
| 27 | #include <QtQml/qqml.h> |
| 28 | #include <private/qv4util_p.h> |
| 29 | #include <private/qv4global_p.h> |
| 30 | #include <private/qv4staticvalue_p.h> |
| 31 | #include <private/qtquickparticlesglobal_p.h> |
| 32 | |
| 33 | QT_BEGIN_NAMESPACE |
| 34 | |
| 35 | template<class T, int Prealloc> |
| 36 | class QQuickParticleVarLengthArray: public QVarLengthArray<T, Prealloc> |
| 37 | { |
| 38 | public: |
| 39 | void insert(const T &element) |
| 40 | { |
| 41 | if (!this->contains(element)) { |
| 42 | this->append(element); |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | bool removeOne(const T &element) |
| 47 | { |
| 48 | for (int i = 0; i < this->size(); ++i) { |
| 49 | if (this->at(i) == element) { |
| 50 | this->remove(i); |
| 51 | return true; |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | return false; |
| 56 | } |
| 57 | }; |
| 58 | |
| 59 | class QQuickParticleSystem; |
| 60 | class QQuickParticleAffector; |
| 61 | class QQuickParticleEmitter; |
| 62 | class QQuickParticlePainter; |
| 63 | class QQuickParticleData; |
| 64 | class QQuickParticleSystemAnimation; |
| 65 | class QQuickStochasticEngine; |
| 66 | class QQuickSprite; |
| 67 | class QQuickV4ParticleData; |
| 68 | class QQuickParticleGroup; |
| 69 | class QQuickImageParticle; |
| 70 | |
| 71 | struct QQuickParticleDataHeapNode{ |
| 72 | int time;//in ms |
| 73 | QSet<QQuickParticleData*> data;//Set ptrs instead? |
| 74 | }; |
| 75 | |
| 76 | class Q_QUICKPARTICLES_EXPORT QQuickParticleDataHeap { |
| 77 | //Idea is to do a binary heap, but which also stores a set of int,Node* so that if the int already exists, you can |
| 78 | //add it to the data* list. Pops return the whole list at once. |
| 79 | public: |
| 80 | QQuickParticleDataHeap(); |
| 81 | void insert(QQuickParticleData* data); |
| 82 | void insertTimed(QQuickParticleData* data, int time); |
| 83 | |
| 84 | int top(); |
| 85 | |
| 86 | bool isEmpty() const { return m_end == 0; } |
| 87 | |
| 88 | QSet<QQuickParticleData*> pop(); |
| 89 | |
| 90 | void clear(); |
| 91 | |
| 92 | bool contains(QQuickParticleData*);//O(n), for debugging purposes only |
| 93 | private: |
| 94 | void grow(); |
| 95 | void swap(int, int); |
| 96 | void bubbleUp(int); |
| 97 | void bubbleDown(int); |
| 98 | int m_size; |
| 99 | int m_end; |
| 100 | QQuickParticleDataHeapNode m_tmp; |
| 101 | QVector<QQuickParticleDataHeapNode> m_data; |
| 102 | QHash<int,int> m_lookups; |
| 103 | }; |
| 104 | |
| 105 | class Q_QUICKPARTICLES_EXPORT QQuickParticleGroupData { |
| 106 | class FreeList |
| 107 | { |
| 108 | public: |
| 109 | FreeList() {} |
| 110 | |
| 111 | void resize(int newSize) |
| 112 | { |
| 113 | Q_ASSERT(newSize >= 0); |
| 114 | int oldSize = isUnused.size(); |
| 115 | isUnused.resize(newSize, newValue: true); |
| 116 | if (newSize > oldSize) { |
| 117 | if (firstUnused == UINT_MAX) { |
| 118 | firstUnused = oldSize; |
| 119 | } else { |
| 120 | firstUnused = std::min(a: firstUnused, b: unsigned(oldSize)); |
| 121 | } |
| 122 | } else if (firstUnused >= unsigned(newSize)) { |
| 123 | firstUnused = UINT_MAX; |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | void free(int index) |
| 128 | { |
| 129 | isUnused.setBit(index); |
| 130 | firstUnused = std::min(a: firstUnused, b: unsigned(index)); |
| 131 | --allocated; |
| 132 | } |
| 133 | |
| 134 | int count() const |
| 135 | { return allocated; } |
| 136 | |
| 137 | bool hasUnusedEntries() const |
| 138 | { return firstUnused != UINT_MAX; } |
| 139 | |
| 140 | int alloc() |
| 141 | { |
| 142 | if (hasUnusedEntries()) { |
| 143 | int nextFree = firstUnused; |
| 144 | isUnused.clearBit(idx: firstUnused); |
| 145 | firstUnused = isUnused.findNext(start: firstUnused, value: true, wrapAround: false); |
| 146 | if (firstUnused >= unsigned(isUnused.size())) { |
| 147 | firstUnused = UINT_MAX; |
| 148 | } |
| 149 | ++allocated; |
| 150 | return nextFree; |
| 151 | } else { |
| 152 | return -1; |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | private: |
| 157 | QV4::BitVector isUnused; |
| 158 | unsigned firstUnused = UINT_MAX; |
| 159 | int allocated = 0; |
| 160 | }; |
| 161 | |
| 162 | public: // types |
| 163 | typedef int ID; |
| 164 | enum { InvalidID = -1, DefaultGroupID = 0 }; |
| 165 | |
| 166 | public: |
| 167 | QQuickParticleGroupData(const QString &name, QQuickParticleSystem* sys); |
| 168 | ~QQuickParticleGroupData(); |
| 169 | |
| 170 | int size() const |
| 171 | { |
| 172 | return m_size; |
| 173 | } |
| 174 | |
| 175 | bool isActive() { return freeList.count() > 0; } |
| 176 | |
| 177 | QString name() const; |
| 178 | |
| 179 | void setSize(int newSize); |
| 180 | |
| 181 | const ID index; |
| 182 | QQuickParticleVarLengthArray<QQuickParticlePainter*, 4> painters;//TODO: What if they are dynamically removed? |
| 183 | |
| 184 | //TODO: Refactor particle data list out into a separate class |
| 185 | QVector<QQuickParticleData*> data; |
| 186 | FreeList freeList; |
| 187 | QQuickParticleDataHeap dataHeap; |
| 188 | bool recycle(); //Force recycling round, returns true if all indexes are now reusable |
| 189 | |
| 190 | void initList(); |
| 191 | void kill(QQuickParticleData* d); |
| 192 | |
| 193 | //After calling this, initialize, then call prepareRecycler(d) |
| 194 | QQuickParticleData* newDatum(bool respectsLimits); |
| 195 | |
| 196 | //TODO: Find and clean up those that don't get added to the recycler (currently they get lost) |
| 197 | void prepareRecycler(QQuickParticleData* d); |
| 198 | |
| 199 | private: |
| 200 | int m_size; |
| 201 | QQuickParticleSystem* m_system; |
| 202 | // Only used in recycle() for tracking of alive particles after latest recycling round |
| 203 | QVector<QQuickParticleData*> m_latestAliveParticles; |
| 204 | }; |
| 205 | |
| 206 | struct Color4ub { |
| 207 | uchar r; |
| 208 | uchar g; |
| 209 | uchar b; |
| 210 | uchar a; |
| 211 | }; |
| 212 | |
| 213 | class Q_QUICKPARTICLES_EXPORT QQuickParticleData |
| 214 | { |
| 215 | public: |
| 216 | //Convenience functions for working backwards, because parameters are from the start of particle life |
| 217 | //If setting multiple parameters at once, doing the conversion yourself will be faster. |
| 218 | |
| 219 | //sets the x accleration without affecting the instantaneous x velocity or position |
| 220 | void setInstantaneousAX(float ax, QQuickParticleSystem *particleSystem); |
| 221 | //sets the x velocity without affecting the instantaneous x postion |
| 222 | void setInstantaneousVX(float vx, QQuickParticleSystem *particleSystem); |
| 223 | //sets the instantaneous x postion |
| 224 | void setInstantaneousX(float x, QQuickParticleSystem *particleSystem); |
| 225 | //sets the y accleration without affecting the instantaneous y velocity or position |
| 226 | void setInstantaneousAY(float ay, QQuickParticleSystem *particleSystem); |
| 227 | //sets the y velocity without affecting the instantaneous y postion |
| 228 | void setInstantaneousVY(float vy, QQuickParticleSystem *particleSystem); |
| 229 | //sets the instantaneous Y postion |
| 230 | void setInstantaneousY(float y, QQuickParticleSystem *particleSystem); |
| 231 | |
| 232 | //TODO: Slight caching? |
| 233 | float curX(QQuickParticleSystem *particleSystem) const; |
| 234 | float curVX(QQuickParticleSystem *particleSystem) const; |
| 235 | float curAX() const { return ax; } |
| 236 | float curAX(QQuickParticleSystem *) const { return ax; } // used by the macros in qquickv4particledata.cpp |
| 237 | float curY(QQuickParticleSystem *particleSystem) const; |
| 238 | float curVY(QQuickParticleSystem *particleSystem) const; |
| 239 | float curAY() const { return ay; } |
| 240 | float curAY(QQuickParticleSystem *) const { return ay; } // used by the macros in qquickv4particledata.cpp |
| 241 | |
| 242 | int index = 0; |
| 243 | int systemIndex = -1; |
| 244 | |
| 245 | //General Position Stuff |
| 246 | float x = 0; |
| 247 | float y = 0; |
| 248 | float t = -1; |
| 249 | float lifeSpan = 0; |
| 250 | float size = 0; |
| 251 | float endSize = 0; |
| 252 | float vx = 0; |
| 253 | float vy = 0; |
| 254 | float ax = 0; |
| 255 | float ay = 0; |
| 256 | |
| 257 | //Painter-specific stuff, now universally shared |
| 258 | //Used by ImageParticle color mode |
| 259 | Color4ub color = { .r: 255, .g: 255, .b: 255, .a: 255}; |
| 260 | //Used by ImageParticle deform mode |
| 261 | float xx = 1; |
| 262 | float xy = 0; |
| 263 | float yx = 0; |
| 264 | float yy = 1; |
| 265 | float rotation = 0; |
| 266 | float rotationVelocity = 0; |
| 267 | uchar autoRotate = 0; // Basically a bool |
| 268 | //Used by ImageParticle Sprite mode |
| 269 | float animIdx = 0; |
| 270 | float frameDuration = 1; |
| 271 | float frameAt = -1;//Used for duration -1 |
| 272 | float frameCount = 1; |
| 273 | float animT = -1; |
| 274 | float animX = 0; |
| 275 | float animY = 0; |
| 276 | float animWidth = 1; |
| 277 | float animHeight = 1; |
| 278 | |
| 279 | QQuickParticleGroupData::ID groupId = 0; |
| 280 | |
| 281 | //Used by ImageParticle data shadowing |
| 282 | QQuickImageParticle* colorOwner = nullptr; |
| 283 | QQuickImageParticle* rotationOwner = nullptr; |
| 284 | QQuickImageParticle* deformationOwner = nullptr; |
| 285 | QQuickImageParticle* animationOwner = nullptr; |
| 286 | |
| 287 | //Used by ItemParticle |
| 288 | QQuickItem* delegate = nullptr; |
| 289 | //Used by custom affectors |
| 290 | float update = 0; |
| 291 | |
| 292 | void debugDump(QQuickParticleSystem *particleSystem) const; |
| 293 | bool stillAlive(QQuickParticleSystem *particleSystem) const; //Only checks end, because usually that's all you need and it's a little faster. |
| 294 | bool alive(QQuickParticleSystem *particleSystem) const; |
| 295 | float lifeLeft(QQuickParticleSystem *particleSystem) const; |
| 296 | |
| 297 | float curSize(QQuickParticleSystem *particleSystem) const; |
| 298 | |
| 299 | QQuickV4ParticleData v4Value(QQuickParticleSystem *particleSystem); |
| 300 | void extendLife(float time, QQuickParticleSystem *particleSystem); |
| 301 | |
| 302 | static inline constexpr float EPSILON() noexcept { return 0.001f; } |
| 303 | }; |
| 304 | |
| 305 | static_assert(std::is_trivially_copyable_v<QQuickParticleData>); |
| 306 | static_assert(std::is_trivially_destructible_v<QQuickParticleData>); |
| 307 | |
| 308 | class Q_QUICKPARTICLES_EXPORT QQuickParticleSystem : public QQuickItem |
| 309 | { |
| 310 | Q_OBJECT |
| 311 | Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) |
| 312 | Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) |
| 313 | Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged) |
| 314 | QML_NAMED_ELEMENT(ParticleSystem) |
| 315 | QML_ADDED_IN_VERSION(2, 0) |
| 316 | |
| 317 | public: |
| 318 | explicit QQuickParticleSystem(QQuickItem *parent = nullptr); |
| 319 | ~QQuickParticleSystem(); |
| 320 | |
| 321 | bool isRunning() const |
| 322 | { |
| 323 | return m_running; |
| 324 | } |
| 325 | |
| 326 | int count() const |
| 327 | { |
| 328 | return particleCount; |
| 329 | } |
| 330 | |
| 331 | static const int maxLife = 600000; |
| 332 | |
| 333 | Q_SIGNALS: |
| 334 | |
| 335 | void systemInitialized(); |
| 336 | void runningChanged(bool arg); |
| 337 | void pausedChanged(bool arg); |
| 338 | void emptyChanged(bool arg); |
| 339 | |
| 340 | public Q_SLOTS: |
| 341 | void start(){setRunning(true);} |
| 342 | void stop(){setRunning(false);} |
| 343 | void restart(){setRunning(false);setRunning(true);} |
| 344 | void pause(){setPaused(true);} |
| 345 | void resume(){setPaused(false);} |
| 346 | |
| 347 | void reset(); |
| 348 | void setRunning(bool arg); |
| 349 | void setPaused(bool arg); |
| 350 | |
| 351 | virtual int duration() const { return -1; } |
| 352 | |
| 353 | |
| 354 | protected: |
| 355 | //This one only once per frame (effectively) |
| 356 | void componentComplete() override; |
| 357 | |
| 358 | private Q_SLOTS: |
| 359 | void emittersChanged(); |
| 360 | void loadPainter(QQuickParticlePainter *p); |
| 361 | void createEngine(); //Not invoked by sprite engine, unlike Sprite uses |
| 362 | void particleStateChange(int idx); |
| 363 | |
| 364 | public: |
| 365 | //These can be called multiple times per frame, performance critical |
| 366 | void emitParticle(QQuickParticleData* p, QQuickParticleEmitter *particleEmitter); |
| 367 | QQuickParticleData *newDatum( |
| 368 | int groupId, bool respectLimits = true, int sysIdx = -1, |
| 369 | const QQuickParticleData *cloneFrom = nullptr); |
| 370 | void finishNewDatum(QQuickParticleData*); |
| 371 | void moveGroups(QQuickParticleData *d, int newGIdx); |
| 372 | int nextSystemIndex(); |
| 373 | |
| 374 | //This one only once per painter per frame |
| 375 | int systemSync(QQuickParticlePainter* p); |
| 376 | |
| 377 | //Data members here for ease of related class and auto-test usage. Not "public" API. TODO: d_ptrize |
| 378 | QSet<QQuickParticleData*> needsReset; |
| 379 | QVector<QQuickParticleData*> bySysIdx; //Another reference to the data (data owned by group), but by sysIdx |
| 380 | QQuickStochasticEngine* stateEngine; |
| 381 | |
| 382 | QHash<QString, int> groupIds; |
| 383 | QVarLengthArray<QQuickParticleGroupData*, 32> groupData; |
| 384 | int nextFreeGroupId; |
| 385 | int registerParticleGroupData(const QString &name, QQuickParticleGroupData *pgd); |
| 386 | |
| 387 | //Also only here for auto-test usage |
| 388 | void updateCurrentTime( int currentTime ); |
| 389 | QQuickParticleSystemAnimation* m_animation; |
| 390 | bool m_running; |
| 391 | bool m_debugMode; |
| 392 | |
| 393 | int timeInt; |
| 394 | bool initialized; |
| 395 | int particleCount; |
| 396 | |
| 397 | void registerParticlePainter(QQuickParticlePainter* p); |
| 398 | void registerParticleEmitter(QQuickParticleEmitter* e); |
| 399 | void finishRegisteringParticleEmitter(QQuickParticleEmitter *e); |
| 400 | void registerParticleAffector(QQuickParticleAffector* a); |
| 401 | void registerParticleGroup(QQuickParticleGroup* g); |
| 402 | |
| 403 | static void statePropertyRedirect(QQmlListProperty<QObject> *prop, QObject *value); |
| 404 | static void stateRedirect(QQuickParticleGroup* group, QQuickParticleSystem* sys, QObject *value); |
| 405 | bool isPaused() const |
| 406 | { |
| 407 | return m_paused; |
| 408 | } |
| 409 | |
| 410 | bool isEmpty() const |
| 411 | { |
| 412 | return m_empty; |
| 413 | } |
| 414 | |
| 415 | private: |
| 416 | void searchNextFreeGroupId(); |
| 417 | |
| 418 | private: |
| 419 | void emitterAdded(QQuickParticleEmitter *e); |
| 420 | void postProcessEmitters(); |
| 421 | void initializeSystem(); |
| 422 | void initGroups(); |
| 423 | QList<QPointer<QQuickParticleEmitter> > m_emitters; |
| 424 | QList<QPointer<QQuickParticleAffector> > m_affectors; |
| 425 | QList<QPointer<QQuickParticlePainter> > m_painters; |
| 426 | QList<QPointer<QQuickParticlePainter> > m_syncList; |
| 427 | QList<QQuickParticleGroup*> m_groups; |
| 428 | int m_nextIndex; |
| 429 | QSet<int> m_reusableIndexes; |
| 430 | bool m_componentComplete; |
| 431 | |
| 432 | bool m_paused; |
| 433 | bool m_allDead; |
| 434 | bool m_empty; |
| 435 | }; |
| 436 | |
| 437 | // Internally, this animation drives all the timing. Painters sync up in their updatePaintNode |
| 438 | class QQuickParticleSystemAnimation : public QAbstractAnimation |
| 439 | { |
| 440 | Q_OBJECT |
| 441 | public: |
| 442 | QQuickParticleSystemAnimation(QQuickParticleSystem* system) |
| 443 | : QAbstractAnimation(static_cast<QObject*>(system)), m_system(system) |
| 444 | { } |
| 445 | protected: |
| 446 | void updateCurrentTime(int t) override |
| 447 | { |
| 448 | m_system->updateCurrentTime(currentTime: t); |
| 449 | } |
| 450 | |
| 451 | int duration() const override |
| 452 | { |
| 453 | return -1; |
| 454 | } |
| 455 | |
| 456 | private: |
| 457 | QQuickParticleSystem* m_system; |
| 458 | }; |
| 459 | |
| 460 | inline void QQuickParticleData::setInstantaneousAX(float ax, QQuickParticleSystem* particleSystem) |
| 461 | { |
| 462 | float t = (particleSystem->timeInt / 1000.0f) - this->t; |
| 463 | float t_sq = t * t; |
| 464 | float vx = (this->vx + t * this->ax) - t * ax; |
| 465 | float ex = this->x + this->vx * t + 0.5f * this->ax * t_sq; |
| 466 | float x = ex - t * vx - 0.5f * t_sq * ax; |
| 467 | |
| 468 | this->ax = ax; |
| 469 | this->vx = vx; |
| 470 | this->x = x; |
| 471 | } |
| 472 | |
| 473 | inline void QQuickParticleData::setInstantaneousVX(float vx, QQuickParticleSystem* particleSystem) |
| 474 | { |
| 475 | float t = (particleSystem->timeInt / 1000.0f) - this->t; |
| 476 | float t_sq = t * t; |
| 477 | float evx = vx - t * this->ax; |
| 478 | float ex = this->x + this->vx * t + 0.5f * this->ax * t_sq; |
| 479 | float x = ex - t * evx - 0.5f * t_sq * this->ax; |
| 480 | |
| 481 | this->vx = evx; |
| 482 | this->x = x; |
| 483 | } |
| 484 | |
| 485 | inline void QQuickParticleData::setInstantaneousX(float x, QQuickParticleSystem* particleSystem) |
| 486 | { |
| 487 | float t = (particleSystem->timeInt / 1000.0f) - this->t; |
| 488 | float t_sq = t * t; |
| 489 | this->x = x - t * this->vx - 0.5f * t_sq * this->ax; |
| 490 | } |
| 491 | |
| 492 | inline void QQuickParticleData::setInstantaneousAY(float ay, QQuickParticleSystem* particleSystem) |
| 493 | { |
| 494 | float t = (particleSystem->timeInt / 1000.0f) - this->t; |
| 495 | float t_sq = t * t; |
| 496 | float vy = (this->vy + t * this->ay) - t * ay; |
| 497 | float ey = this->y + this->vy * t + 0.5f * this->ay * t_sq; |
| 498 | float y = ey - t * vy - 0.5f * t_sq * ay; |
| 499 | |
| 500 | this->ay = ay; |
| 501 | this->vy = vy; |
| 502 | this->y = y; |
| 503 | } |
| 504 | |
| 505 | inline void QQuickParticleData::setInstantaneousVY(float vy, QQuickParticleSystem* particleSystem) |
| 506 | { |
| 507 | float t = (particleSystem->timeInt / 1000.0f) - this->t; |
| 508 | float t_sq = t * t; |
| 509 | float evy = vy - t * this->ay; |
| 510 | float ey = this->y + this->vy * t + 0.5f * this->ay * t_sq; |
| 511 | float y = ey - t*evy - 0.5f * t_sq * this->ay; |
| 512 | |
| 513 | this->vy = evy; |
| 514 | this->y = y; |
| 515 | } |
| 516 | |
| 517 | inline void QQuickParticleData::setInstantaneousY(float y, QQuickParticleSystem *particleSystem) |
| 518 | { |
| 519 | float t = (particleSystem->timeInt / 1000.0f) - this->t; |
| 520 | float t_sq = t * t; |
| 521 | this->y = y - t * this->vy - 0.5f * t_sq * this->ay; |
| 522 | } |
| 523 | |
| 524 | inline float QQuickParticleData::curX(QQuickParticleSystem *particleSystem) const |
| 525 | { |
| 526 | float t = (particleSystem->timeInt / 1000.0f) - this->t; |
| 527 | float t_sq = t * t; |
| 528 | return this->x + this->vx * t + 0.5f * this->ax * t_sq; |
| 529 | } |
| 530 | |
| 531 | inline float QQuickParticleData::curVX(QQuickParticleSystem *particleSystem) const |
| 532 | { |
| 533 | float t = (particleSystem->timeInt / 1000.0f) - this->t; |
| 534 | return this->vx + t * this->ax; |
| 535 | } |
| 536 | |
| 537 | inline float QQuickParticleData::curY(QQuickParticleSystem *particleSystem) const |
| 538 | { |
| 539 | float t = (particleSystem->timeInt / 1000.0f) - this->t; |
| 540 | float t_sq = t * t; |
| 541 | return y + vy * t + 0.5f * ay * t_sq; |
| 542 | } |
| 543 | |
| 544 | inline float QQuickParticleData::curVY(QQuickParticleSystem *particleSystem) const |
| 545 | { |
| 546 | float t = (particleSystem->timeInt / 1000.0f) - this->t; |
| 547 | return vy + t*ay; |
| 548 | } |
| 549 | |
| 550 | inline bool QQuickParticleData::stillAlive(QQuickParticleSystem* system) const |
| 551 | { |
| 552 | if (!system) |
| 553 | return false; |
| 554 | return (t + lifeSpan - EPSILON()) > (system->timeInt / 1000.0f); |
| 555 | } |
| 556 | |
| 557 | inline bool QQuickParticleData::alive(QQuickParticleSystem* system) const |
| 558 | { |
| 559 | if (!system) |
| 560 | return false; |
| 561 | float st = (system->timeInt / 1000.0f); |
| 562 | return (t + EPSILON()) < st && (t + lifeSpan - EPSILON()) > st; |
| 563 | } |
| 564 | |
| 565 | inline float QQuickParticleData::lifeLeft(QQuickParticleSystem *particleSystem) const |
| 566 | { |
| 567 | if (!particleSystem) |
| 568 | return 0.0f; |
| 569 | return (t + lifeSpan) - (particleSystem->timeInt / 1000.0f); |
| 570 | } |
| 571 | |
| 572 | inline float QQuickParticleData::curSize(QQuickParticleSystem *particleSystem) const |
| 573 | { |
| 574 | if (!particleSystem || lifeSpan == 0.0f) |
| 575 | return 0.0f; |
| 576 | return size + (endSize - size) * (1 - (lifeLeft(particleSystem) / lifeSpan)); |
| 577 | } |
| 578 | |
| 579 | QT_END_NAMESPACE |
| 580 | |
| 581 | #endif // PARTICLESYSTEM_H |
| 582 | |
| 583 | |
| 584 | |