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