From 49df4d727d310897c9b475e4be6f0611e5e38bc6 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Fri, 20 May 2022 01:15:39 +0300 Subject: [PATCH 01/61] Update --- core/2d/CCParticleSystem.cpp | 82 ++++++++++++++++++++++---------- core/2d/CCParticleSystem.h | 75 ++++++++++++++++++++++++++++- core/2d/CCParticleSystemQuad.cpp | 62 +++++++++++++++++++----- 3 files changed, 181 insertions(+), 38 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index daaf7e9928..1f92f725a4 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -121,24 +121,26 @@ bool ParticleData::init(int count) { maxCount = count; - posx = (float*)malloc(count * sizeof(float)); - posy = (float*)malloc(count * sizeof(float)); - startPosX = (float*)malloc(count * sizeof(float)); - startPosY = (float*)malloc(count * sizeof(float)); - colorR = (float*)malloc(count * sizeof(float)); - colorG = (float*)malloc(count * sizeof(float)); - colorB = (float*)malloc(count * sizeof(float)); - colorA = (float*)malloc(count * sizeof(float)); - deltaColorR = (float*)malloc(count * sizeof(float)); - deltaColorG = (float*)malloc(count * sizeof(float)); - deltaColorB = (float*)malloc(count * sizeof(float)); - deltaColorA = (float*)malloc(count * sizeof(float)); - size = (float*)malloc(count * sizeof(float)); - deltaSize = (float*)malloc(count * sizeof(float)); - rotation = (float*)malloc(count * sizeof(float)); - deltaRotation = (float*)malloc(count * sizeof(float)); - timeToLive = (float*)malloc(count * sizeof(float)); - atlasIndex = (unsigned int*)malloc(count * sizeof(unsigned int)); + posx = (float*)malloc(count * sizeof(float)); + posy = (float*)malloc(count * sizeof(float)); + startPosX = (float*)malloc(count * sizeof(float)); + startPosY = (float*)malloc(count * sizeof(float)); + colorR = (float*)malloc(count * sizeof(float)); + colorG = (float*)malloc(count * sizeof(float)); + colorB = (float*)malloc(count * sizeof(float)); + colorA = (float*)malloc(count * sizeof(float)); + deltaColorR = (float*)malloc(count * sizeof(float)); + deltaColorG = (float*)malloc(count * sizeof(float)); + deltaColorB = (float*)malloc(count * sizeof(float)); + deltaColorA = (float*)malloc(count * sizeof(float)); + size = (float*)malloc(count * sizeof(float)); + deltaSize = (float*)malloc(count * sizeof(float)); + rotation = (float*)malloc(count * sizeof(float)); + deltaRotation = (float*)malloc(count * sizeof(float)); + totalTimeToLive = (float*)malloc(count * sizeof(float)); + timeToLive = (float*)malloc(count * sizeof(float)); + animCellIndex = (unsigned int*)malloc(count * sizeof(unsigned int)); + atlasIndex = (unsigned int*)malloc(count * sizeof(unsigned int)); modeA.dirX = (float*)malloc(count * sizeof(float)); modeA.dirY = (float*)malloc(count * sizeof(float)); @@ -151,9 +153,9 @@ bool ParticleData::init(int count) modeB.radius = (float*)malloc(count * sizeof(float)); return posx && posy && startPosY && startPosX && colorR && colorG && colorB && colorA && deltaColorR && - deltaColorG && deltaColorB && deltaColorA && size && deltaSize && rotation && deltaRotation && timeToLive && - atlasIndex && modeA.dirX && modeA.dirY && modeA.radialAccel && modeA.tangentialAccel && modeB.angle && - modeB.degreesPerSecond && modeB.deltaRadius && modeB.radius; + deltaColorG && deltaColorB && deltaColorA && size && deltaSize && rotation && deltaRotation && totalTimeToLive && + timeToLive && atlasIndex && modeA.dirX && modeA.dirY && modeA.radialAccel && modeA.tangentialAccel && + modeB.angle && modeB.degreesPerSecond && modeB.deltaRadius && modeB.radius; } void ParticleData::release() @@ -174,7 +176,9 @@ void ParticleData::release() CC_SAFE_FREE(deltaSize); CC_SAFE_FREE(rotation); CC_SAFE_FREE(deltaRotation); + CC_SAFE_FREE(totalTimeToLive); CC_SAFE_FREE(timeToLive); + CC_SAFE_FREE(animCellIndex); CC_SAFE_FREE(atlasIndex); CC_SAFE_FREE(modeA.dirX); @@ -223,6 +227,11 @@ ParticleSystem::ParticleSystem() , _texture(nullptr) , _blendFunc(BlendFunc::ALPHA_PREMULTIPLIED) , _opacityModifyRGB(false) + , _isLifeAnimated(false) + , _isEmitterAnimated(false) + , _animDir(TexAnimDir::VERTICAL) + , _animUnifiedSize(0) + , _isLifeAnimationReversed(false) , _yCoordFlipped(1) , _positionType(PositionType::FREE) , _paused(false) @@ -607,20 +616,23 @@ ParticleSystem::~ParticleSystem() CC_SAFE_RELEASE(_texture); } -void ParticleSystem::addParticles(int count) +void ParticleSystem::addParticles(int count, int animationCellIndex) { if (_paused) return; uint32_t RANDSEED = rand(); + animationCellIndex = MIN(animationCellIndex, getTotalAnimationCells() - 1); + int start = _particleCount; _particleCount += count; // life for (int i = start; i < _particleCount; ++i) { - float theLife = _life + _lifeVar * RANDOM_M11(&RANDSEED); - _particleData.timeToLive[i] = MAX(0, theLife); + float particleLife = _life + _lifeVar * RANDOM_M11(&RANDSEED); + _particleData.totalTimeToLive[i] = MAX(0, particleLife); + _particleData.timeToLive[i] = MAX(0, particleLife); } // position @@ -634,6 +646,22 @@ void ParticleSystem::addParticles(int count) _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_M11(&RANDSEED); } + if (animationCellIndex == -1 && _isEmitterAnimated) + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.animCellIndex[i] = (int)abs(RANDOM_M11(&RANDSEED) * getTotalAnimationCells()); + } + } + + if (animationCellIndex != -1) + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.animCellIndex[i] = animationCellIndex; + } + } + // color #define SET_COLOR(c, b, v) \ for (int i = start; i < _particleCount; ++i) \ @@ -880,6 +908,12 @@ void ParticleSystem::update(float dt) for (int i = 0; i < _particleCount; ++i) { _particleData.timeToLive[i] -= dt; + if (_isLifeAnimated) + { + float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; + percent = _isLifeAnimationReversed ? 1 - percent : percent; + _particleData.animCellIndex[i] = (unsigned int)MIN((percent * getTotalAnimationCells()), getTotalAnimationCells() - 1); + } } for (int i = 0; i < _particleCount; ++i) diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 4cd110e10f..f4747c621d 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -74,7 +74,9 @@ public: float* deltaSize; float* rotation; float* deltaRotation; + float* totalTimeToLive; float* timeToLive; + unsigned int* animCellIndex; unsigned int* atlasIndex; //! Mode A: gravity, direction, radial accel, tangential accel @@ -124,8 +126,10 @@ public: rotation[p1] = rotation[p2]; deltaRotation[p1] = deltaRotation[p2]; + totalTimeToLive[p1] = totalTimeToLive[p2]; timeToLive[p1] = timeToLive[p2]; + animCellIndex[p1] = animCellIndex[p2]; atlasIndex[p1] = atlasIndex[p2]; modeA.dirX[p1] = modeA.dirX[p2]; @@ -202,7 +206,7 @@ public: }; /** PositionType - Possible types of particle positions. + Types of particle positioning. * @js cc.ParticleSystem.TYPE_FREE */ enum class PositionType @@ -216,6 +220,17 @@ public: }; + /** TexAnimDir + Texture animation direction for the particles. + */ + enum class TexAnimDir + { + VERTICAL, /** texture coordinates are read top to bottom within the texture */ + + HORIZONTAL, /** texture coordinates are read left to right within the texture */ + + }; + //* @enum enum { @@ -252,7 +267,7 @@ public: static Vector& getAllParticleSystems(); public: - void addParticles(int count); + void addParticles(int count, int animationCellIndex = -1); void stopSystem(); /** Kill all living particles. @@ -728,6 +743,52 @@ public: void setOpacityModifyRGB(bool opacityModifyRGB) override { _opacityModifyRGB = opacityModifyRGB; } bool isOpacityModifyRGB() const override { return _opacityModifyRGB; } + /** Enables or disables tex coord animations that are set based on particle life. */ + void setLifeAnimation(bool enabled) { _isLifeAnimated = enabled; } + + /** Enables or disables tex coord animations that are set by the emitter randomly when a particle is emitted. + * WARNING: this won't matter if particle life animation is enabled ie. setLifeAnimation(true) */ + void setEmitterAnimation(bool enabled) { _isEmitterAnimated = enabled; } + + bool isLifeAnimated() { return _isLifeAnimated; } + bool isEmitterAnimated() { return _isEmitterAnimated; } + + /** Sets texture animation direction for the particles */ + void setAnimationTexDir(TexAnimDir dir = TexAnimDir::VERTICAL) { _animDir = dir; } + + /** Gets texture animation direction for the particles */ + TexAnimDir getAnimationTexDir() { return _animDir; } + + /** Sets the width and height of a single animated cell *unified* + * Example: if a cell's size in the texture is 32 pixels wide and 32 pixel high then the unified size is 32 */ + void setAnimationCellUnifiedSize(int unifiedSizeInPixels) { _animUnifiedSize = unifiedSizeInPixels; } + + /** Gets the width and height of a single animated cell *unified* + * Example: if a cell's size in the texture is 32 pixels wide and 32 pixel high then the unified size is 32 */ + int getAnimationCellUnifiedSize() { return _animUnifiedSize; } + + /** Gets the total pixels in a texture based on the direction set */ + int getAnimationPixels() + { + switch (_animDir) + { + case TexAnimDir::VERTICAL: + return _texture->getPixelsHigh(); + case TexAnimDir::HORIZONTAL: + return _texture->getPixelsWide(); + default: return 0; + } + } + + /** Gets the total cells viewable in a texture by dividing texture height or width into animation cell size + * animation cell size can be changed using setAnimationCellUnifiedSize(int) */ + int getTotalAnimationCells() { return getAnimationPixels() / _animUnifiedSize; } + + /** Sets wether to start from first cell and go forward (normal) + * or last cell and go backward (reversed) when using life animation */ + void setAnimationLifeReverse(bool reverse) { _isLifeAnimationReversed = reverse; } + bool isAnimationLifeReversed() { return _isLifeAnimationReversed; } + /** Gets the particles movement type: Free or Grouped. @since v0.8 * @@ -975,6 +1036,16 @@ protected: BlendFunc _blendFunc; /** does the alpha value modify color */ bool _opacityModifyRGB; + /** is the particle system animated */ + bool _isLifeAnimated; + /** is the emitter particle system animated */ + bool _isEmitterAnimated; + /** tex coord animation direction for the system */ + TexAnimDir _animDir; + /** the width and height of an animated cell unified */ + int _animUnifiedSize; + /** wether to start from first or last when using life animation */ + int _isLifeAnimationReversed; /** does FlippedY variance of each particle */ int _yCoordFlipped; diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index cab5a697f2..b891375c62 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -405,16 +405,47 @@ void ParticleSystemQuad::updateParticleQuads() } } + float texPixels = getAnimationPixels(); + float cellPixels = getAnimationCellUnifiedSize(); + + auto setTexCoords = [this, texPixels, cellPixels](V3F_C4B_T2F_Quad* quad, unsigned int* cellIndex) { + + float left = 0; + float right = 1; + float top = *cellIndex * cellPixels / texPixels; + float bottom = (*cellIndex * cellPixels + cellPixels) / texPixels; + + if (_animDir == TexAnimDir::HORIZONTAL) + { + std::swap(top, right); + std::swap(left, bottom); + } + + quad->bl.texCoords.u = left; + quad->bl.texCoords.v = bottom; + + quad->br.texCoords.u = right; + quad->br.texCoords.v = bottom; + + quad->tl.texCoords.u = left; + quad->tl.texCoords.v = top; + + quad->tr.texCoords.u = right; + quad->tr.texCoords.v = top; + + }; + // set color if (_opacityModifyRGB) { - V3F_C4B_T2F_Quad* quad = startQuad; - float* r = _particleData.colorR; - float* g = _particleData.colorG; - float* b = _particleData.colorB; - float* a = _particleData.colorA; + V3F_C4B_T2F_Quad* quad = startQuad; + float* r = _particleData.colorR; + float* g = _particleData.colorG; + float* b = _particleData.colorB; + float* a = _particleData.colorA; + unsigned int* cellIndex = _particleData.animCellIndex; - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++cellIndex) { uint8_t colorR = *r * *a * 255; uint8_t colorG = *g * *a * 255; @@ -424,17 +455,21 @@ void ParticleSystemQuad::updateParticleQuads() quad->br.colors.set(colorR, colorG, colorB, colorA); quad->tl.colors.set(colorR, colorG, colorB, colorA); quad->tr.colors.set(colorR, colorG, colorB, colorA); + + if (_isLifeAnimated || _isEmitterAnimated) + setTexCoords(quad, cellIndex); } } else { - V3F_C4B_T2F_Quad* quad = startQuad; - float* r = _particleData.colorR; - float* g = _particleData.colorG; - float* b = _particleData.colorB; - float* a = _particleData.colorA; + V3F_C4B_T2F_Quad* quad = startQuad; + float* r = _particleData.colorR; + float* g = _particleData.colorG; + float* b = _particleData.colorB; + float* a = _particleData.colorA; + unsigned int* cellIndex = _particleData.animCellIndex; - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++cellIndex) { uint8_t colorR = *r * 255; uint8_t colorG = *g * 255; @@ -444,6 +479,9 @@ void ParticleSystemQuad::updateParticleQuads() quad->br.colors.set(colorR, colorG, colorB, colorA); quad->tl.colors.set(colorR, colorG, colorB, colorA); quad->tr.colors.set(colorR, colorG, colorB, colorA); + + if (_isLifeAnimated || _isEmitterAnimated) + setTexCoords(quad, cellIndex); } } } From 6ca5481edf9140188a3370376f2d0e5cd67592b8 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Fri, 20 May 2022 01:45:55 +0300 Subject: [PATCH 02/61] Correct function naming --- core/2d/CCParticleSystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index f4747c621d..3be224dd17 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -786,7 +786,7 @@ public: /** Sets wether to start from first cell and go forward (normal) * or last cell and go backward (reversed) when using life animation */ - void setAnimationLifeReverse(bool reverse) { _isLifeAnimationReversed = reverse; } + void setLifeAnimationReverse(bool reverse) { _isLifeAnimationReversed = reverse; } bool isAnimationLifeReversed() { return _isLifeAnimationReversed; } /** Gets the particles movement type: Free or Grouped. From c9ff24e8304974159ceb4dba22e5274cd755c952 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sat, 21 May 2022 16:48:46 +0300 Subject: [PATCH 03/61] Add sprite frame and animation descriptors support --- core/2d/CCParticleSystem.cpp | 182 +++++++++++++++++++++++++++++-- core/2d/CCParticleSystem.h | 151 +++++++++++++++++++++++-- core/2d/CCParticleSystemQuad.cpp | 104 +++++++++++------- 3 files changed, 384 insertions(+), 53 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 1f92f725a4..e1339dc65e 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1,3 +1,4 @@ +#include "CCParticleSystem.h" /**************************************************************************** Copyright (c) 2008-2010 Ricardo Quesada Copyright (c) 2010-2012 cocos2d-x.org @@ -98,7 +99,7 @@ inline void normalize_point(float x, float y, particle_point* out) } /** - A more effect random number getter function, get from ejoy2d. + A more effective random number generator function, made by ejoy2d. */ inline static float RANDOM_M11(unsigned int* seed) { @@ -136,10 +137,14 @@ bool ParticleData::init(int count) size = (float*)malloc(count * sizeof(float)); deltaSize = (float*)malloc(count * sizeof(float)); rotation = (float*)malloc(count * sizeof(float)); + staticRotation = (float*)malloc(count * sizeof(float)); deltaRotation = (float*)malloc(count * sizeof(float)); totalTimeToLive = (float*)malloc(count * sizeof(float)); timeToLive = (float*)malloc(count * sizeof(float)); - animCellIndex = (unsigned int*)malloc(count * sizeof(unsigned int)); + animTimeDelta = (float*)malloc(count * sizeof(float)); + animTimeLength = (float*)malloc(count * sizeof(float)); + animIndex = (unsigned short*)malloc(count * sizeof(unsigned short)); + animCellIndex = (unsigned short*)malloc(count * sizeof(unsigned short)); atlasIndex = (unsigned int*)malloc(count * sizeof(unsigned int)); modeA.dirX = (float*)malloc(count * sizeof(float)); @@ -175,9 +180,13 @@ void ParticleData::release() CC_SAFE_FREE(size); CC_SAFE_FREE(deltaSize); CC_SAFE_FREE(rotation); + CC_SAFE_FREE(staticRotation); CC_SAFE_FREE(deltaRotation); CC_SAFE_FREE(totalTimeToLive); CC_SAFE_FREE(timeToLive); + CC_SAFE_FREE(animTimeDelta); + CC_SAFE_FREE(animTimeLength); + CC_SAFE_FREE(animIndex); CC_SAFE_FREE(animCellIndex); CC_SAFE_FREE(atlasIndex); @@ -229,9 +238,13 @@ ParticleSystem::ParticleSystem() , _opacityModifyRGB(false) , _isLifeAnimated(false) , _isEmitterAnimated(false) + , _isLoopAnimated(false) + , _isAnimationAtlas(false) , _animDir(TexAnimDir::VERTICAL) - , _animUnifiedSize(0) + , _animUnifiedSize(1) + , _animIndexCount(0) , _isLifeAnimationReversed(false) + , _isAnimationMulti(false) , _yCoordFlipped(1) , _positionType(PositionType::FREE) , _paused(false) @@ -613,16 +626,21 @@ ParticleSystem::~ParticleSystem() // it is not needed to call "unscheduleUpdate" here. In fact, it will be called in "cleanup" // unscheduleUpdate(); _particleData.release(); + _animations.clear(); CC_SAFE_RELEASE(_texture); } -void ParticleSystem::addParticles(int count, int animationCellIndex) +void ParticleSystem::addParticles(int count, int animationCellIndex, int animationIndex) { if (_paused) return; uint32_t RANDSEED = rand(); - animationCellIndex = MIN(animationCellIndex, getTotalAnimationCells() - 1); + if (_isAnimationAtlas) + { + animationCellIndex = MIN(animationCellIndex, getTotalAnimationCells() - 1); + animationIndex = MIN(animationIndex, _animIndexCount - 1); + } int start = _particleCount; _particleCount += count; @@ -662,6 +680,47 @@ void ParticleSystem::addParticles(int count, int animationCellIndex) } } + if (animationIndex == -1 && !_isAnimationMulti && _isLoopAnimated) + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.animIndex[i] = 0; + auto descriptor = _animations.at(_particleData.animIndex[i]); + _particleData.animTimeLength[i] = + descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_M11(&RANDSEED); + } + } + + if (animationIndex == -1 && _isAnimationMulti && _isLoopAnimated) + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.animIndex[i] = _randomAnimations[abs(RANDOM_M11(&RANDSEED) * _randomAnimations.size())]; + auto descriptor = _animations.at(_particleData.animIndex[i]); + _particleData.animTimeLength[i] = + descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_M11(&RANDSEED); + } + } + + if (_isLoopAnimated) + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.animTimeDelta[i] = 0; + } + } + + if (animationIndex != -1) + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.animIndex[i] = animationIndex; + auto descriptor = _animations.at(_particleData.animIndex[i]); + _particleData.animTimeLength[i] = + descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_M11(&RANDSEED); + } + } + // color #define SET_COLOR(c, b, v) \ for (int i = start; i < _particleCount; ++i) \ @@ -725,6 +784,12 @@ void ParticleSystem::addParticles(int count, int animationCellIndex) _particleData.deltaRotation[i] = (endA - _particleData.rotation[i]) / _particleData.timeToLive[i]; } + // static rotation + for (int i = start; i < _particleCount; ++i) + { + _particleData.staticRotation[i] = _staticRotation + _staticRotationVar * RANDOM_M11(&RANDSEED); + } + // position Vec2 pos; if (_positionType == PositionType::FREE) @@ -829,6 +894,84 @@ void ParticleSystem::addParticles(int count, int animationCellIndex) } } +void ParticleSystem::setAnimationDescriptor(unsigned short indexOfDescriptor, + float time, + float timeVariance, + std::vector indices, + bool reverse) +{ + ParticleAnimationDescriptor desc{}; + + desc.animationSpeed = time; + desc.animationSpeedVariance = timeVariance; + desc.animationIndices = indices; + desc.reverseIndices = reverse; + + if (_animations.find(indexOfDescriptor) == _animations.end()) + _animations.insert({indexOfDescriptor, desc}); + else + { + _animations.erase(indexOfDescriptor); + _animations.insert({indexOfDescriptor, desc}); + } +} + +void ParticleSystem::resetAnimationIndices() +{ + _animIndexCount = 0; + _animationIndices.clear(); +} + +void ParticleSystem::resetAnimationDescriptors() +{ + _animations.clear(); + _randomAnimations.clear(); +} + +void ParticleSystem::setMultiAnimationRandom() +{ + _randomAnimations.clear(); + for (auto& a : _animations) + _randomAnimations.push_back(a.first); +} + +void ParticleSystem::addAnimationIndex(std::string_view frameName) +{ + addAnimationIndex(_animIndexCount++, frameName); +} + +void ParticleSystem::addAnimationIndex(unsigned short index, std::string_view frameName) +{ + auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(frameName); + + if (frame) + addAnimationIndex(index, frame); + } + +void ParticleSystem::addAnimationIndex(cocos2d::SpriteFrame* frame) +{ + addAnimationIndex(_animIndexCount++, frame); +} + +void ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::SpriteFrame* frame) +{ + //Not sure how to check texture equality truly but it won't hurt to skip it + //CCASSERT(frame->getTexture() == _texture, "Sprite frame texture and particle system texture should match!"); + + ParticleFrameDescriptor desc{}; + + desc.rect = frame->getRect(); + desc.isRotated = frame->isRotated(); + + if (_animationIndices.find(index) == _animationIndices.end()) + _animationIndices.insert({index, desc}); + else + { + _animationIndices.erase(index); + _animationIndices.insert({index, desc}); + } +} + void ParticleSystem::onEnter() { Node::onEnter(); @@ -908,12 +1051,37 @@ void ParticleSystem::update(float dt) for (int i = 0; i < _particleCount; ++i) { _particleData.timeToLive[i] -= dt; - if (_isLifeAnimated) + if (_isLifeAnimated && _animations.empty()) { float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; - percent = _isLifeAnimationReversed ? 1 - percent : percent; + percent = _isLifeAnimationReversed ? 1.0F - percent : percent; _particleData.animCellIndex[i] = (unsigned int)MIN((percent * getTotalAnimationCells()), getTotalAnimationCells() - 1); } + if (_isLifeAnimated && !_animations.empty()) + { + auto& anim = _animations.begin()->second; + + float percent = + (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; + + percent = (!!_isLifeAnimationReversed != !!anim.reverseIndices) ? 1.0F - percent : percent; + _particleData.animCellIndex[i] = anim.animationIndices[MIN(abs(percent * anim.animationIndices.size()), + anim.animationIndices.size() - 1)]; + } + if (_isLoopAnimated) + { + auto& anim = _animations.at(_particleData.animIndex[i]); + + _particleData.animTimeDelta[i] += dt; + if (_particleData.animTimeDelta[i] >= _particleData.animTimeLength[i]) + _particleData.animTimeDelta[i] = 0; + + float percent = _particleData.animTimeDelta[i] / _particleData.animTimeLength[i]; + + percent = anim.reverseIndices ? 1.0F - percent : percent; + _particleData.animCellIndex[i] = anim.animationIndices[MIN(abs(percent * anim.animationIndices.size()), + anim.animationIndices.size() - 1)]; + } } for (int i = 0; i < _particleCount; ++i) diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 3be224dd17..d44ccd01fb 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -52,6 +52,20 @@ struct particle_point float y; }; +struct ParticleAnimationDescriptor +{ + float animationSpeed; + float animationSpeedVariance; + std::vector animationIndices; + bool reverseIndices; +}; + +struct ParticleFrameDescriptor +{ + cocos2d::Rect rect; + bool isRotated; +}; + class CC_DLL ParticleData { public: @@ -73,10 +87,14 @@ public: float* size; float* deltaSize; float* rotation; + float* staticRotation; float* deltaRotation; float* totalTimeToLive; float* timeToLive; - unsigned int* animCellIndex; + float* animTimeDelta; + float* animTimeLength; + unsigned short* animIndex; + unsigned short* animCellIndex; unsigned int* atlasIndex; //! Mode A: gravity, direction, radial accel, tangential accel @@ -267,7 +285,7 @@ public: static Vector& getAllParticleSystems(); public: - void addParticles(int count, int animationCellIndex = -1); + void addParticles(int count, int animationCellIndex = -1, int animationIndex = -1); void stopSystem(); /** Kill all living particles. @@ -717,6 +735,17 @@ public: */ void setEndSpinVar(float endSpinVar) { _endSpinVar = endSpinVar; } + /** Sets the static rotation of each particle + * + * @param angle The angle in degrees that the particle will exist with + */ + virtual void setStaticRotation(float angle) { _staticRotation = angle; }; + /** Sets the static rotation variance of each particle. + * + * @param angle The angle in degrees variance + */ + virtual void setStaticRotationVar(float angle) { _staticRotationVar = angle; }; + /** Gets the emission rate of the particles. * * @return The emission rate of the particles. @@ -744,11 +773,32 @@ public: bool isOpacityModifyRGB() const override { return _opacityModifyRGB; } /** Enables or disables tex coord animations that are set based on particle life. */ - void setLifeAnimation(bool enabled) { _isLifeAnimated = enabled; } + void setLifeAnimation(bool enabled) + { + _isLifeAnimated = enabled; + _isEmitterAnimated = false; + _isLoopAnimated = false; + } /** Enables or disables tex coord animations that are set by the emitter randomly when a particle is emitted. * WARNING: this won't matter if particle life animation is enabled ie. setLifeAnimation(true) */ - void setEmitterAnimation(bool enabled) { _isEmitterAnimated = enabled; } + void setEmitterAnimation(bool enabled) + { + _isEmitterAnimated = enabled; + _isLifeAnimated = false; + _isLoopAnimated = false; + } + + /** Enables or disables tex coord animations that are used to make particles play a sequence forever until they die + * This interduces a new concept of animation where you specify the indices and then specify animations descriptors that tell how these indices are used and what speed they're played at. + * Functions that effect this are: setMultiAnimationParticles(), resetAnimationDescriptors(), resetAnimationIndices(), addAnimationIndex(), setAnimationDescriptor(), setMultiAnimationRandom(), setMultiAnimationRandomSpecific(), + */ + void setLoopAnimation(bool enabled) + { + _isLoopAnimated = enabled; + _isEmitterAnimated = false; + _isLifeAnimated = false; + } bool isLifeAnimated() { return _isLifeAnimated; } bool isEmitterAnimated() { return _isEmitterAnimated; } @@ -781,14 +831,82 @@ public: } /** Gets the total cells viewable in a texture by dividing texture height or width into animation cell size - * animation cell size can be changed using setAnimationCellUnifiedSize(int) */ - int getTotalAnimationCells() { return getAnimationPixels() / _animUnifiedSize; } + * animation cell size can be changed using setAnimationCellUnifiedSize(int) + * incase atlas animation is set off it will return the indices added through addAnimationIndex() */ + int getTotalAnimationCells() { return _isAnimationAtlas ? getAnimationPixels() / _animUnifiedSize : _animIndexCount; } /** Sets wether to start from first cell and go forward (normal) * or last cell and go backward (reversed) when using life animation */ void setLifeAnimationReverse(bool reverse) { _isLifeAnimationReversed = reverse; } bool isAnimationLifeReversed() { return _isLifeAnimationReversed; } + /** Sets wether to use atlas rendering or sprite frame rendering */ + void setAnimationAtlas(bool atlas) { _isAnimationAtlas = atlas; } + bool isAnimationAtlas() { return _isAnimationAtlas; } + + /** Sets wether to use multiable different index animations that can be randomly choosen for particles */ + void setMultiAnimationParticles(bool multi) { _isAnimationMulti = multi; } + bool isMultiAnimationParticles() { return _isAnimationMulti; } + + /** Resets the count of indices to 0 and empties the index array */ + void resetAnimationIndices(); + + /** Resets the container of animation descriptors empties the random array */ + void resetAnimationDescriptors(); + + /** Choose what animation descriptors are to be selected at random for particles + * This function should be called after you've inserted/overwritten any animation descriptors. + * + * @param animations Array of specific animations to play at random + */ + void setMultiAnimationRandomSpecific(std::vector animations) { _randomAnimations = animations; }; + + /** Choose ALL animation descriptors to be selected at random for particles. + * This function should be called after you've inserted/overwritten any animation descriptors. + */ + void setMultiAnimationRandom(); + + /** Add a particle animation index based on tex coords spicified using a sprite frame if atlas mode is off. + * The index is automatically incremented on each addition. + * + * @param frameName SpriteFrame name to search for + */ + void addAnimationIndex(std::string_view frameName); + + /** Add a particle animation index based on tex coords spicified using a sprite frame if atlas mode is off. + * + * @param frameName SpriteFrame name to search for + */ + void addAnimationIndex(unsigned short index, std::string_view frameName); + + /** Add a particle animation index based on tex coords spicified using a sprite frame if atlas mode is off. + * The index is automatically incremented on each addition. + * + * @param frame SpriteFrame containting data about tex coords + */ + void addAnimationIndex(cocos2d::SpriteFrame* frame); + + /** Add a particle animation index based on tex coords spicified using a sprite frame if atlas mode is off. + * you can specify which index you want to override in this function + * @param index Index id to override the index with + * @param frame SpriteFrame containting data about tex coords + */ + void addAnimationIndex(unsigned short index, cocos2d::SpriteFrame* frame); + + /** Add a particle animation descriptor with an index. + * + * @param indexOfDescriptor Index of the animation to be added, adding to the same index will just override the pervious animation descriptor + * @param time length of the animation in seconds + * @param timeVariance Time randomly selected for each different particle added on the animation length + * @param indices An array of the indicies + * @param reverse Should the animation indicies be played backwards? (default: false) + */ + void setAnimationDescriptor(unsigned short indexOfDescriptor, + float time, + float timeVariance, + std::vector indices, + bool reverse = false); + /** Gets the particles movement type: Free or Grouped. @since v0.8 * @@ -1026,6 +1144,10 @@ protected: float _endSpin; //* initial angle of each particle float _endSpinVar; + //* initial rotation of each particle + float _staticRotation; + //* initial rotation of each particle + float _staticRotationVar; /** emission rate of the particles */ float _emissionRate; /** maximum particles of the system */ @@ -1040,12 +1162,27 @@ protected: bool _isLifeAnimated; /** is the emitter particle system animated */ bool _isEmitterAnimated; + /** is the emitter particle system animated */ + bool _isLoopAnimated; + /** True if you want to use an atlas with a fixed cell size + * False if you want to use SpriteFrames as your indexes using the function addAnimationIndex() */ + bool _isAnimationAtlas; /** tex coord animation direction for the system */ TexAnimDir _animDir; /** the width and height of an animated cell unified */ int _animUnifiedSize; + /** variable keeping count of sprite frames added for atlas mode off */ + int _animIndexCount; /** wether to start from first or last when using life animation */ - int _isLifeAnimationReversed; + bool _isLifeAnimationReversed; + /** A map that stores particle animation index coords */ + std::unordered_map _animationIndices; + /** wether to start from first or last when using life animation */ + int _isAnimationMulti; + /** A map that stores particle animation descriptors */ + std::unordered_map _animations; + /** A vector that stores ids of animation descriptors that are choosen at random */ + std::vector _randomAnimations; /** does FlippedY variance of each particle */ int _yCoordFlipped; diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index b891375c62..dadeb9d680 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -273,7 +273,11 @@ void ParticleSystemQuad::initIndices() } } -inline void updatePosWithParticle(V3F_C4B_T2F_Quad* quad, const Vec2& newPosition, float size, float rotation) +inline void updatePosWithParticle(V3F_C4B_T2F_Quad* quad, + const Vec2& newPosition, + float size, + float rotation, + float staticRotation) { // vertices float size_2 = size / 2; @@ -285,7 +289,7 @@ inline void updatePosWithParticle(V3F_C4B_T2F_Quad* quad, const Vec2& newPositio float x = newPosition.x; float y = newPosition.y; - float r = (float)-CC_DEGREES_TO_RADIANS(rotation); + float r = (float)-CC_DEGREES_TO_RADIANS(rotation + staticRotation); float cr = cosf(r); float sr = sinf(r); float ax = x1 * cr - y1 * sr + x; @@ -351,14 +355,15 @@ void ParticleSystemQuad::updateParticleQuads() worldToNodeTM.transformPoint(&p1); Vec3 p2; Vec2 newPos; - float* startX = _particleData.startPosX; - float* startY = _particleData.startPosY; - float* x = _particleData.posx; - float* y = _particleData.posy; - float* s = _particleData.size; - float* r = _particleData.rotation; + float* startX = _particleData.startPosX; + float* startY = _particleData.startPosY; + float* x = _particleData.posx; + float* y = _particleData.posy; + float* s = _particleData.size; + float* r = _particleData.rotation; + float* sr = _particleData.staticRotation; V3F_C4B_T2F_Quad* quadStart = startQuad; - for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r) + for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr) { p2.set(*startX, *startY, 0); worldToNodeTM.transformPoint(&p2); @@ -366,7 +371,7 @@ void ParticleSystemQuad::updateParticleQuads() p2 = p1 - p2; newPos.x -= p2.x - pos.x; newPos.y -= p2.y - pos.y; - updatePosWithParticle(quadStart, newPos, *s, *r); + updatePosWithParticle(quadStart, newPos, *s, *r, *sr); } } else if (_positionType == PositionType::RELATIVE) @@ -378,14 +383,15 @@ void ParticleSystemQuad::updateParticleQuads() float* y = _particleData.posy; float* s = _particleData.size; float* r = _particleData.rotation; + float* sr = _particleData.staticRotation; V3F_C4B_T2F_Quad* quadStart = startQuad; - for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r) + for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr) { newPos.set(*x, *y); newPos.x = *x - (currentPosition.x - *startX); newPos.y = *y - (currentPosition.y - *startY); newPos += pos; - updatePosWithParticle(quadStart, newPos, *s, *r); + updatePosWithParticle(quadStart, newPos, *s, *r, *sr); } } else @@ -397,28 +403,48 @@ void ParticleSystemQuad::updateParticleQuads() float* y = _particleData.posy; float* s = _particleData.size; float* r = _particleData.rotation; + float* sr = _particleData.staticRotation; V3F_C4B_T2F_Quad* quadStart = startQuad; - for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r) + for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr) { newPos.set(*x + pos.x, *y + pos.y); - updatePosWithParticle(quadStart, newPos, *s, *r); + updatePosWithParticle(quadStart, newPos, *s, *r, *sr); } } - float texPixels = getAnimationPixels(); - float cellPixels = getAnimationCellUnifiedSize(); + auto setTexCoords = [this](V3F_C4B_T2F_Quad* quad, unsigned short* cellIndex) { - auto setTexCoords = [this, texPixels, cellPixels](V3F_C4B_T2F_Quad* quad, unsigned int* cellIndex) { + float left = 0.0F, bottom = 0.0F, top = 1.0F, right = 1.0F; - float left = 0; - float right = 1; - float top = *cellIndex * cellPixels / texPixels; - float bottom = (*cellIndex * cellPixels + cellPixels) / texPixels; - - if (_animDir == TexAnimDir::HORIZONTAL) + if (_isAnimationAtlas) { - std::swap(top, right); - std::swap(left, bottom); + float texPixels = getAnimationPixels(); + float cellPixels = getAnimationCellUnifiedSize(); + + left = 0.0F; + right = 1.0F; + top = *cellIndex * cellPixels / texPixels; + bottom = (*cellIndex * cellPixels + cellPixels) / texPixels; + + // Flip texture coords if direction of texture is horizontal + if (_animDir == TexAnimDir::HORIZONTAL) + { + std::swap(top, right); + std::swap(left, bottom); + } + } + else + { + auto& index = _animationIndices.at(*cellIndex); + + auto texWidth = _texture->getPixelsWide(); + auto texHeight = _texture->getPixelsHigh(); + + left = index.rect.origin.x / texWidth; + right = (index.rect.origin.x + index.rect.size.x) / texWidth; + + top = index.rect.origin.y / texHeight; + bottom = (index.rect.origin.y + index.rect.size.y) / texHeight; } quad->bl.texCoords.u = left; @@ -438,12 +464,12 @@ void ParticleSystemQuad::updateParticleQuads() // set color if (_opacityModifyRGB) { - V3F_C4B_T2F_Quad* quad = startQuad; - float* r = _particleData.colorR; - float* g = _particleData.colorG; - float* b = _particleData.colorB; - float* a = _particleData.colorA; - unsigned int* cellIndex = _particleData.animCellIndex; + V3F_C4B_T2F_Quad* quad = startQuad; + float* r = _particleData.colorR; + float* g = _particleData.colorG; + float* b = _particleData.colorB; + float* a = _particleData.colorA; + unsigned short* cellIndex = _particleData.animCellIndex; for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++cellIndex) { @@ -456,18 +482,18 @@ void ParticleSystemQuad::updateParticleQuads() quad->tl.colors.set(colorR, colorG, colorB, colorA); quad->tr.colors.set(colorR, colorG, colorB, colorA); - if (_isLifeAnimated || _isEmitterAnimated) + if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) setTexCoords(quad, cellIndex); } } else { - V3F_C4B_T2F_Quad* quad = startQuad; - float* r = _particleData.colorR; - float* g = _particleData.colorG; - float* b = _particleData.colorB; - float* a = _particleData.colorA; - unsigned int* cellIndex = _particleData.animCellIndex; + V3F_C4B_T2F_Quad* quad = startQuad; + float* r = _particleData.colorR; + float* g = _particleData.colorG; + float* b = _particleData.colorB; + float* a = _particleData.colorA; + unsigned short* cellIndex = _particleData.animCellIndex; for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++cellIndex) { @@ -480,7 +506,7 @@ void ParticleSystemQuad::updateParticleQuads() quad->tl.colors.set(colorR, colorG, colorB, colorA); quad->tr.colors.set(colorR, colorG, colorB, colorA); - if (_isLifeAnimated || _isEmitterAnimated) + if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) setTexCoords(quad, cellIndex); } } From f18da9f6e4195747f44de924a607c3761f2ed058 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sat, 21 May 2022 16:51:56 +0300 Subject: [PATCH 04/61] Fix #include Visual studio thinks it's doing something useful throwing includes all around LOL --- core/2d/CCParticleSystem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index e1339dc65e..a97b7f4df0 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1,4 +1,3 @@ -#include "CCParticleSystem.h" /**************************************************************************** Copyright (c) 2008-2010 Ricardo Quesada Copyright (c) 2010-2012 cocos2d-x.org From ef404fed8c99805dd0b2dd057cd4e2afadf5e8c6 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sat, 21 May 2022 16:56:56 +0300 Subject: [PATCH 05/61] Add TODO --- core/2d/CCParticleSystemQuad.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index dadeb9d680..515e9e897f 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -435,6 +435,8 @@ void ParticleSystemQuad::updateParticleQuads() } else { + // TODO: index.isRotated should be treated accordingly + auto& index = _animationIndices.at(*cellIndex); auto texWidth = _texture->getPixelsWide(); From 1b45bba085769396d98c5225e5c6c1225f58ba1b Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sat, 21 May 2022 17:47:44 +0300 Subject: [PATCH 06/61] Fix Other Platforms Compilations Somehow not including `2d/CCSpriteFrame.h` makes the other platforms fail to compile but not windows :o --- core/2d/CCParticleSystem.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index d44ccd01fb..e8e9e5495e 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -32,6 +32,7 @@ THE SOFTWARE. #include "base/CCProtocols.h" #include "2d/CCNode.h" #include "base/CCValue.h" +#include "2d/CCSpriteFrame.h" NS_CC_BEGIN @@ -52,6 +53,9 @@ struct particle_point float y; }; +/** @struct ParticleAnimationDescriptor +Structure that contains animation description +*/ struct ParticleAnimationDescriptor { float animationSpeed; @@ -60,6 +64,9 @@ struct ParticleAnimationDescriptor bool reverseIndices; }; +/** @struct ParticleFrameDescriptor +Structure that contains frame description +*/ struct ParticleFrameDescriptor { cocos2d::Rect rect; From 30003499bd8566728293bc25a6caf355a2fbe221 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sat, 21 May 2022 17:57:21 +0300 Subject: [PATCH 07/61] Fix Other Platforms Compilations --- core/2d/CCParticleSystem.h | 1 + 1 file changed, 1 insertion(+) diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index e8e9e5495e..e75e993e1e 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -33,6 +33,7 @@ THE SOFTWARE. #include "2d/CCNode.h" #include "base/CCValue.h" #include "2d/CCSpriteFrame.h" +#include "2d/CCSpriteFrameCache.h" NS_CC_BEGIN From 4a0624a9ed863a4333e2d52782005ac4afd2e07d Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sat, 21 May 2022 20:23:19 +0300 Subject: [PATCH 08/61] Macros and random functions are pain... --- core/2d/CCParticleSystem.cpp | 23 +++++++++++++---------- core/2d/CCParticleSystem.h | 10 ++++++++++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index a97b7f4df0..3188517c52 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -667,7 +667,8 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati { for (int i = start; i < _particleCount; ++i) { - _particleData.animCellIndex[i] = (int)abs(RANDOM_M11(&RANDSEED) * getTotalAnimationCells()); + int rand0 = abs(RANDOM_M11(&RANDSEED) * getTotalAnimationCells()); + _particleData.animCellIndex[i] = MIN(rand0, getTotalAnimationCells() - 1); } } @@ -679,7 +680,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati } } - if (animationIndex == -1 && !_isAnimationMulti && _isLoopAnimated) + if (animationIndex == -1 && !_isAnimationMulti && !_animations.empty()) { for (int i = start; i < _particleCount; ++i) { @@ -690,11 +691,13 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati } } - if (animationIndex == -1 && _isAnimationMulti && _isLoopAnimated) + if (animationIndex == -1 && _isAnimationMulti && !_animations.empty()) { for (int i = start; i < _particleCount; ++i) { - _particleData.animIndex[i] = _randomAnimations[abs(RANDOM_M11(&RANDSEED) * _randomAnimations.size())]; + int rand0 = abs(RANDOM_M11(&RANDSEED) * _randomAnimations.size()); + int index = MIN(rand0, _randomAnimations.size() - 1); + _particleData.animIndex[i] = _randomAnimations[index]; auto descriptor = _animations.at(_particleData.animIndex[i]); _particleData.animTimeLength[i] = descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_M11(&RANDSEED); @@ -1054,17 +1057,17 @@ void ParticleSystem::update(float dt) { float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; percent = _isLifeAnimationReversed ? 1.0F - percent : percent; - _particleData.animCellIndex[i] = (unsigned int)MIN((percent * getTotalAnimationCells()), getTotalAnimationCells() - 1); + _particleData.animCellIndex[i] = (unsigned short)MIN(percent * getTotalAnimationCells(), getTotalAnimationCells() - 1); } if (_isLifeAnimated && !_animations.empty()) { - auto& anim = _animations.begin()->second; + auto& anim = _animations.at(_particleData.animIndex[i]); float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; - percent = (!!_isLifeAnimationReversed != !!anim.reverseIndices) ? 1.0F - percent : percent; - _particleData.animCellIndex[i] = anim.animationIndices[MIN(abs(percent * anim.animationIndices.size()), + + _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; } if (_isLoopAnimated) @@ -1076,9 +1079,9 @@ void ParticleSystem::update(float dt) _particleData.animTimeDelta[i] = 0; float percent = _particleData.animTimeDelta[i] / _particleData.animTimeLength[i]; - percent = anim.reverseIndices ? 1.0F - percent : percent; - _particleData.animCellIndex[i] = anim.animationIndices[MIN(abs(percent * anim.animationIndices.size()), + + _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; } } diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index e75e993e1e..98a79af202 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -915,6 +915,16 @@ public: std::vector indices, bool reverse = false); + /** Add a particle animation descriptor with the index 0. + * + * @param indices An array of the indicies + * @param reverse Should the animation indicies be played backwards? (default: false) + */ + void setAnimationDescriptor(std::vector indices, bool reverse = false) + { + setAnimationDescriptor(0, 0, 0, indices, reverse); + }; + /** Gets the particles movement type: Free or Grouped. @since v0.8 * From 3d666712cd18725c582087e798e5cc932811ff5e Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 22 May 2022 10:09:13 +0300 Subject: [PATCH 09/61] Update CCParticleSystem.cpp --- core/2d/CCParticleSystem.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 3188517c52..097b9e118e 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -693,6 +693,9 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati if (animationIndex == -1 && _isAnimationMulti && !_animations.empty()) { + if (_randomAnimations.empty()) + setMultiAnimationRandom(); + for (int i = start; i < _particleCount; ++i) { int rand0 = abs(RANDOM_M11(&RANDSEED) * _randomAnimations.size()); @@ -902,20 +905,15 @@ void ParticleSystem::setAnimationDescriptor(unsigned short indexOfDescriptor, std::vector indices, bool reverse) { - ParticleAnimationDescriptor desc{}; + auto iter = _animations.find(indexOfDescriptor); + if (iter == _animations.end()) + iter = _animations.emplace(indexOfDescriptor, ParticleAnimationDescriptor{}).first; + auto& desc = iter->second; desc.animationSpeed = time; desc.animationSpeedVariance = timeVariance; - desc.animationIndices = indices; + desc.animationIndices = std::move(indices); desc.reverseIndices = reverse; - - if (_animations.find(indexOfDescriptor) == _animations.end()) - _animations.insert({indexOfDescriptor, desc}); - else - { - _animations.erase(indexOfDescriptor); - _animations.insert({indexOfDescriptor, desc}); - } } void ParticleSystem::resetAnimationIndices() @@ -1023,6 +1021,15 @@ void ParticleSystem::update(float dt) { CC_PROFILER_START_CATEGORY(kProfilerCategoryParticles, "CCParticleSystem - update"); + if (_componentContainer && !_componentContainer->isEmpty()) + { + _componentContainer->visit(dt); + } + + // Considerably reduces particle bursts when the game freezes or stutters for a moment. + if (dt > 1.0F / 3.0F) + dt = 1.0F / 3.0F; + if (_isActive && _emissionRate) { float rate = 1.0f / _emissionRate; From a3b7217ec081547f38960aa0f31ce938e49e5802 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 22 May 2022 12:13:17 +0300 Subject: [PATCH 10/61] Add emitter animation random picking --- core/2d/CCParticleSystem.cpp | 31 +++++++++++++++++-------------- core/2d/CCParticleSystem.h | 6 ------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 097b9e118e..0305819c12 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -243,7 +243,6 @@ ParticleSystem::ParticleSystem() , _animUnifiedSize(1) , _animIndexCount(0) , _isLifeAnimationReversed(false) - , _isAnimationMulti(false) , _yCoordFlipped(1) , _positionType(PositionType::FREE) , _paused(false) @@ -680,18 +679,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati } } - if (animationIndex == -1 && !_isAnimationMulti && !_animations.empty()) - { - for (int i = start; i < _particleCount; ++i) - { - _particleData.animIndex[i] = 0; - auto descriptor = _animations.at(_particleData.animIndex[i]); - _particleData.animTimeLength[i] = - descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_M11(&RANDSEED); - } - } - - if (animationIndex == -1 && _isAnimationMulti && !_animations.empty()) + if (animationIndex == -1 && !_animations.empty()) { if (_randomAnimations.empty()) setMultiAnimationRandom(); @@ -707,7 +695,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati } } - if (_isLoopAnimated) + if (_isEmitterAnimated || _isLoopAnimated) { for (int i = start; i < _particleCount; ++i) { @@ -1060,6 +1048,21 @@ void ParticleSystem::update(float dt) for (int i = 0; i < _particleCount; ++i) { _particleData.timeToLive[i] -= dt; + if (_isEmitterAnimated && !_animations.empty()) + { + _particleData.animTimeDelta[i] += dt; + if (_particleData.animTimeDelta[i] > _particleData.animTimeLength[i]) + { + auto& anim = _animations.at(_particleData.animIndex[i]); + uint32_t RANDSEED = rand(); + float percent = abs(RANDOM_M11(&RANDSEED)); + percent = anim.reverseIndices ? 1.0F - percent : percent; + + _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), + anim.animationIndices.size() - 1)]; + _particleData.animTimeDelta[i] = 0; + } + } if (_isLifeAnimated && _animations.empty()) { float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 98a79af202..e6e7cbfb07 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -852,10 +852,6 @@ public: void setAnimationAtlas(bool atlas) { _isAnimationAtlas = atlas; } bool isAnimationAtlas() { return _isAnimationAtlas; } - /** Sets wether to use multiable different index animations that can be randomly choosen for particles */ - void setMultiAnimationParticles(bool multi) { _isAnimationMulti = multi; } - bool isMultiAnimationParticles() { return _isAnimationMulti; } - /** Resets the count of indices to 0 and empties the index array */ void resetAnimationIndices(); @@ -1195,8 +1191,6 @@ protected: bool _isLifeAnimationReversed; /** A map that stores particle animation index coords */ std::unordered_map _animationIndices; - /** wether to start from first or last when using life animation */ - int _isAnimationMulti; /** A map that stores particle animation descriptors */ std::unordered_map _animations; /** A vector that stores ids of animation descriptors that are choosen at random */ From 16fe6d796308266c14ea477539344ef5b69cc8e6 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 22 May 2022 15:58:36 +0300 Subject: [PATCH 11/61] Reimplement atlas animation system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reimplement atlas animations with an easier workflow and make it work with the new animation descriptor system 😍πŸ₯ΆπŸ₯³ --- core/2d/CCParticleSystem.cpp | 133 +++++++++++++++++++++++-------- core/2d/CCParticleSystem.h | 86 ++++++++------------ core/2d/CCParticleSystemQuad.cpp | 38 +++------ 3 files changed, 145 insertions(+), 112 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 0305819c12..55e69ba99a 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -238,11 +238,8 @@ ParticleSystem::ParticleSystem() , _isLifeAnimated(false) , _isEmitterAnimated(false) , _isLoopAnimated(false) - , _isAnimationAtlas(false) - , _animDir(TexAnimDir::VERTICAL) - , _animUnifiedSize(1) , _animIndexCount(0) - , _isLifeAnimationReversed(false) + , _isAnimationReversed(false) , _yCoordFlipped(1) , _positionType(PositionType::FREE) , _paused(false) @@ -634,11 +631,9 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati return; uint32_t RANDSEED = rand(); - if (_isAnimationAtlas) - { - animationCellIndex = MIN(animationCellIndex, getTotalAnimationCells() - 1); - animationIndex = MIN(animationIndex, _animIndexCount - 1); - } + + animationCellIndex = MIN(animationCellIndex, _animIndexCount - 1); + animationIndex = MIN(animationIndex, _animIndexCount - 1); int start = _particleCount; _particleCount += count; @@ -666,8 +661,8 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati { for (int i = start; i < _particleCount; ++i) { - int rand0 = abs(RANDOM_M11(&RANDSEED) * getTotalAnimationCells()); - _particleData.animCellIndex[i] = MIN(rand0, getTotalAnimationCells() - 1); + int rand0 = abs(RANDOM_M11(&RANDSEED) * _animIndexCount); + _particleData.animCellIndex[i] = MIN(rand0, _animIndexCount - 1); } } @@ -703,6 +698,14 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati } } + if (_isLoopAnimated && _animations.empty()) + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.animTimeDelta[i] = 0; + } + } + if (animationIndex != -1) { for (int i = start; i < _particleCount; ++i) @@ -923,6 +926,75 @@ void ParticleSystem::setMultiAnimationRandom() _randomAnimations.push_back(a.first); } +void ParticleSystem::setAnimationIndicesAtlas() +{ + // VERTICAL + if (_texture->getPixelsHigh() > _texture->getPixelsWide()) + { + setAnimationIndicesAtlas(_texture->getPixelsWide(), + ParticleSystem::TexAnimDir::VERTICAL); + return; + } + + // HORIZONTAL + if (_texture->getPixelsWide() > _texture->getPixelsHigh()) + { + setAnimationIndicesAtlas(_texture->getPixelsHigh(), + ParticleSystem::TexAnimDir::HORIZONTAL); + return; + } + + CCASSERT(false, "Couldn't figure out the atlas size and direction."); +} + +void ParticleSystem::setAnimationIndicesAtlas(unsigned int unifiedCellSize, TexAnimDir direction) +{ + CCASSERT(unifiedCellSize > 0, "A cell cannot have a size of zero."); + + resetAnimationIndices(); + + auto texWidth = _texture->getPixelsWide(); + auto texHeight = _texture->getPixelsHigh(); + + switch (direction) + { + case TexAnimDir::VERTICAL: + { + for (short i = 0; i < short(texHeight / unifiedCellSize); i++) + { + Rect frame{}; + + frame.origin.x = 0; + frame.origin.y = unifiedCellSize * i; + + frame.size.x = texWidth; + frame.size.y = unifiedCellSize; + + addAnimationIndex(_animIndexCount++, frame); + } + + break; + }; + case TexAnimDir::HORIZONTAL: + { + for (short i = 0; i < short(texWidth / unifiedCellSize); i++) + { + Rect frame{}; + + frame.origin.x = unifiedCellSize * i; + frame.origin.y = 0; + + frame.size.x = unifiedCellSize; + frame.size.y = texHeight; + + addAnimationIndex(_animIndexCount++, frame); + } + + break; + }; + } +} + void ParticleSystem::addAnimationIndex(std::string_view frameName) { addAnimationIndex(_animIndexCount++, frameName); @@ -943,21 +1015,18 @@ void ParticleSystem::addAnimationIndex(cocos2d::SpriteFrame* frame) void ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::SpriteFrame* frame) { - //Not sure how to check texture equality truly but it won't hurt to skip it - //CCASSERT(frame->getTexture() == _texture, "Sprite frame texture and particle system texture should match!"); + addAnimationIndex(index, frame->getRect(), frame->isRotated()); +} - ParticleFrameDescriptor desc{}; +void ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::Rect rect, bool rotated) +{ + auto iter = _animationIndices.find(index); + if (iter == _animationIndices.end()) + iter = _animationIndices.emplace(index, ParticleFrameDescriptor{}).first; - desc.rect = frame->getRect(); - desc.isRotated = frame->isRotated(); - - if (_animationIndices.find(index) == _animationIndices.end()) - _animationIndices.insert({index, desc}); - else - { - _animationIndices.erase(index); - _animationIndices.insert({index, desc}); - } + auto& desc = iter->second; + desc.rect = rect; + desc.isRotated = rotated; } void ParticleSystem::onEnter() @@ -1014,10 +1083,6 @@ void ParticleSystem::update(float dt) _componentContainer->visit(dt); } - // Considerably reduces particle bursts when the game freezes or stutters for a moment. - if (dt > 1.0F / 3.0F) - dt = 1.0F / 3.0F; - if (_isActive && _emissionRate) { float rate = 1.0f / _emissionRate; @@ -1066,8 +1131,8 @@ void ParticleSystem::update(float dt) if (_isLifeAnimated && _animations.empty()) { float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; - percent = _isLifeAnimationReversed ? 1.0F - percent : percent; - _particleData.animCellIndex[i] = (unsigned short)MIN(percent * getTotalAnimationCells(), getTotalAnimationCells() - 1); + percent = _isAnimationReversed ? 1.0F - percent : percent; + _particleData.animCellIndex[i] = (unsigned short)MIN(percent * _animIndexCount, _animIndexCount - 1); } if (_isLifeAnimated && !_animations.empty()) { @@ -1075,12 +1140,12 @@ void ParticleSystem::update(float dt) float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; - percent = (!!_isLifeAnimationReversed != !!anim.reverseIndices) ? 1.0F - percent : percent; + percent = (!!_isAnimationReversed != !!anim.reverseIndices) ? 1.0F - percent : percent; _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; } - if (_isLoopAnimated) + if (_isLoopAnimated && !_animations.empty()) { auto& anim = _animations.at(_particleData.animIndex[i]); @@ -1094,6 +1159,10 @@ void ParticleSystem::update(float dt) _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; } + if (_isLoopAnimated && _animations.empty()) + { + _particleData.animCellIndex[i] = 0; + } } for (int i = 0; i < _particleCount; ++i) diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index e6e7cbfb07..9343cb2f90 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -810,47 +810,15 @@ public: bool isLifeAnimated() { return _isLifeAnimated; } bool isEmitterAnimated() { return _isEmitterAnimated; } + bool isLoopAnimated() { return _isLoopAnimated; } - /** Sets texture animation direction for the particles */ - void setAnimationTexDir(TexAnimDir dir = TexAnimDir::VERTICAL) { _animDir = dir; } - - /** Gets texture animation direction for the particles */ - TexAnimDir getAnimationTexDir() { return _animDir; } - - /** Sets the width and height of a single animated cell *unified* - * Example: if a cell's size in the texture is 32 pixels wide and 32 pixel high then the unified size is 32 */ - void setAnimationCellUnifiedSize(int unifiedSizeInPixels) { _animUnifiedSize = unifiedSizeInPixels; } - - /** Gets the width and height of a single animated cell *unified* - * Example: if a cell's size in the texture is 32 pixels wide and 32 pixel high then the unified size is 32 */ - int getAnimationCellUnifiedSize() { return _animUnifiedSize; } - - /** Gets the total pixels in a texture based on the direction set */ - int getAnimationPixels() - { - switch (_animDir) - { - case TexAnimDir::VERTICAL: - return _texture->getPixelsHigh(); - case TexAnimDir::HORIZONTAL: - return _texture->getPixelsWide(); - default: return 0; - } - } - - /** Gets the total cells viewable in a texture by dividing texture height or width into animation cell size - * animation cell size can be changed using setAnimationCellUnifiedSize(int) - * incase atlas animation is set off it will return the indices added through addAnimationIndex() */ - int getTotalAnimationCells() { return _isAnimationAtlas ? getAnimationPixels() / _animUnifiedSize : _animIndexCount; } + /** Gets the total number of indices. */ + int getTotalAnimationIndices() { return _animIndexCount; } /** Sets wether to start from first cell and go forward (normal) * or last cell and go backward (reversed) when using life animation */ - void setLifeAnimationReverse(bool reverse) { _isLifeAnimationReversed = reverse; } - bool isAnimationLifeReversed() { return _isLifeAnimationReversed; } - - /** Sets wether to use atlas rendering or sprite frame rendering */ - void setAnimationAtlas(bool atlas) { _isAnimationAtlas = atlas; } - bool isAnimationAtlas() { return _isAnimationAtlas; } + void setAnimationReverse(bool reverse) { _isAnimationReversed = reverse; } + bool isAnimationReversed() { return _isAnimationReversed; } /** Resets the count of indices to 0 and empties the index array */ void resetAnimationIndices(); @@ -866,37 +834,58 @@ public: void setMultiAnimationRandomSpecific(std::vector animations) { _randomAnimations = animations; }; /** Choose ALL animation descriptors to be selected at random for particles. - * This function should be called after you've inserted/overwritten any animation descriptors. - */ + * This function should be called after you've inserted/overwritten any animation descriptors. + */ void setMultiAnimationRandom(); - /** Add a particle animation index based on tex coords spicified using a sprite frame if atlas mode is off. + /** Add all particle animation indices based on cells size and direction spicified using a texture atlas. + * will erase the array and add new indices from the atlas. + * This function will automatically figure out your atlas cell size and direction for you! thank her later :) */ + void setAnimationIndicesAtlas(); + + /** Add all particle animation indices based on cells size and direction spicified using a texture atlas. + * will erase the array and add new indices from the atlas. + * + * @param unifiedCellSize The size of cell unified. + * @param direction What direction is the atlas + */ + void setAnimationIndicesAtlas(unsigned int unifiedCellSize, TexAnimDir direction = TexAnimDir::HORIZONTAL); + + /** Add a particle animation index based on tex coords spicified using a sprite frame. * The index is automatically incremented on each addition. * * @param frameName SpriteFrame name to search for */ void addAnimationIndex(std::string_view frameName); - /** Add a particle animation index based on tex coords spicified using a sprite frame if atlas mode is off. + /** Add a particle animation index based on tex coords spicified using a sprite frame. * * @param frameName SpriteFrame name to search for */ void addAnimationIndex(unsigned short index, std::string_view frameName); - /** Add a particle animation index based on tex coords spicified using a sprite frame if atlas mode is off. + /** Add a particle animation index based on tex coords spicified using a sprite frame. * The index is automatically incremented on each addition. * * @param frame SpriteFrame containting data about tex coords */ void addAnimationIndex(cocos2d::SpriteFrame* frame); - /** Add a particle animation index based on tex coords spicified using a sprite frame if atlas mode is off. + /** Add a particle animation index based on tex coords spicified using a sprite frame. * you can specify which index you want to override in this function * @param index Index id to override the index with * @param frame SpriteFrame containting data about tex coords */ void addAnimationIndex(unsigned short index, cocos2d::SpriteFrame* frame); + /** Add a particle animation index based on tex coords spicified. + * you can specify which index you want to override in this function + * @param index Index id to override the index with + * @param rect Rect containting data about tex coords in pixels + * @param rotated Not implemented. + */ + void addAnimationIndex(unsigned short index, cocos2d::Rect rect, bool rotated = false); + /** Add a particle animation descriptor with an index. * * @param indexOfDescriptor Index of the animation to be added, adding to the same index will just override the pervious animation descriptor @@ -1178,17 +1167,10 @@ protected: bool _isEmitterAnimated; /** is the emitter particle system animated */ bool _isLoopAnimated; - /** True if you want to use an atlas with a fixed cell size - * False if you want to use SpriteFrames as your indexes using the function addAnimationIndex() */ - bool _isAnimationAtlas; - /** tex coord animation direction for the system */ - TexAnimDir _animDir; - /** the width and height of an animated cell unified */ - int _animUnifiedSize; - /** variable keeping count of sprite frames added for atlas mode off */ + /** variable keeping count of sprite frames or atlas indices added */ int _animIndexCount; /** wether to start from first or last when using life animation */ - bool _isLifeAnimationReversed; + bool _isAnimationReversed; /** A map that stores particle animation index coords */ std::unordered_map _animationIndices; /** A map that stores particle animation descriptors */ diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index 515e9e897f..a2e08ef371 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -105,6 +105,8 @@ ParticleSystemQuad* ParticleSystemQuad::create(std::string_view filename) ParticleSystemQuad* ParticleSystemQuad::createWithTotalParticles(int numberOfParticles) { + CCASSERT(numberOfParticles <= 10000, "Adding more than 10000 particles will crash the renderer, the mesh generated has an index format of U_SHORT (uint16_t)"); + ParticleSystemQuad* ret = new ParticleSystemQuad(); if (ret->initWithTotalParticles(numberOfParticles)) { @@ -416,38 +418,18 @@ void ParticleSystemQuad::updateParticleQuads() float left = 0.0F, bottom = 0.0F, top = 1.0F, right = 1.0F; - if (_isAnimationAtlas) - { - float texPixels = getAnimationPixels(); - float cellPixels = getAnimationCellUnifiedSize(); + // TODO: index.isRotated should be treated accordingly - left = 0.0F; - right = 1.0F; - top = *cellIndex * cellPixels / texPixels; - bottom = (*cellIndex * cellPixels + cellPixels) / texPixels; + auto& index = _animationIndices.at(*cellIndex); - // Flip texture coords if direction of texture is horizontal - if (_animDir == TexAnimDir::HORIZONTAL) - { - std::swap(top, right); - std::swap(left, bottom); - } - } - else - { - // TODO: index.isRotated should be treated accordingly + auto texWidth = _texture->getPixelsWide(); + auto texHeight = _texture->getPixelsHigh(); - auto& index = _animationIndices.at(*cellIndex); + left = index.rect.origin.x / texWidth; + right = (index.rect.origin.x + index.rect.size.x) / texWidth; - auto texWidth = _texture->getPixelsWide(); - auto texHeight = _texture->getPixelsHigh(); - - left = index.rect.origin.x / texWidth; - right = (index.rect.origin.x + index.rect.size.x) / texWidth; - - top = index.rect.origin.y / texHeight; - bottom = (index.rect.origin.y + index.rect.size.y) / texHeight; - } + top = index.rect.origin.y / texHeight; + bottom = (index.rect.origin.y + index.rect.size.y) / texHeight; quad->bl.texCoords.u = left; quad->bl.texCoords.v = bottom; From 35f0368fe00ce397796adf7396f4104935835398 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 22 May 2022 17:38:43 +0300 Subject: [PATCH 12/61] =?UTF-8?q?Use=20const=20reference=20vectors=20?= =?UTF-8?q?=F0=9F=98=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/2d/CCParticleSystem.cpp | 2 +- core/2d/CCParticleSystem.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 55e69ba99a..444ba7df1c 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -893,7 +893,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati void ParticleSystem::setAnimationDescriptor(unsigned short indexOfDescriptor, float time, float timeVariance, - std::vector indices, + const std::vector &indices, bool reverse) { auto iter = _animations.find(indexOfDescriptor); diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 9343cb2f90..c527cf7fe0 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -831,7 +831,7 @@ public: * * @param animations Array of specific animations to play at random */ - void setMultiAnimationRandomSpecific(std::vector animations) { _randomAnimations = animations; }; + void setMultiAnimationRandomSpecific(const std::vector &animations) { _randomAnimations = animations; }; /** Choose ALL animation descriptors to be selected at random for particles. * This function should be called after you've inserted/overwritten any animation descriptors. @@ -897,7 +897,7 @@ public: void setAnimationDescriptor(unsigned short indexOfDescriptor, float time, float timeVariance, - std::vector indices, + const std::vector &indices, bool reverse = false); /** Add a particle animation descriptor with the index 0. @@ -905,7 +905,7 @@ public: * @param indices An array of the indicies * @param reverse Should the animation indicies be played backwards? (default: false) */ - void setAnimationDescriptor(std::vector indices, bool reverse = false) + void setAnimationDescriptor(const std::vector &indices, bool reverse = false) { setAnimationDescriptor(0, 0, 0, indices, reverse); }; From 13ebf7460fd43da31a924b82f66fc9d4b2a6d21d Mon Sep 17 00:00:00 2001 From: Turky Mohammed <45469625+DelinWorks@users.noreply.github.com> Date: Sun, 22 May 2022 17:42:54 +0300 Subject: [PATCH 13/61] Update core/2d/CCParticleSystem.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 一线灡 --- core/2d/CCParticleSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 444ba7df1c..612e66dbbb 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -684,7 +684,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati int rand0 = abs(RANDOM_M11(&RANDSEED) * _randomAnimations.size()); int index = MIN(rand0, _randomAnimations.size() - 1); _particleData.animIndex[i] = _randomAnimations[index]; - auto descriptor = _animations.at(_particleData.animIndex[i]); + auto& descriptor = _animations.at(_particleData.animIndex[i]); _particleData.animTimeLength[i] = descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_M11(&RANDSEED); } From c006fd51c0b8d9555d91225cb2086cc265503f2e Mon Sep 17 00:00:00 2001 From: Turky Mohammed <45469625+DelinWorks@users.noreply.github.com> Date: Sun, 22 May 2022 17:51:42 +0300 Subject: [PATCH 14/61] Update core/2d/CCParticleSystem.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 一线灡 --- core/2d/CCParticleSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 612e66dbbb..d6b315424a 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -711,7 +711,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati for (int i = start; i < _particleCount; ++i) { _particleData.animIndex[i] = animationIndex; - auto descriptor = _animations.at(_particleData.animIndex[i]); + auto& descriptor = _animations.at(_particleData.animIndex[i]); _particleData.animTimeLength[i] = descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_M11(&RANDSEED); } From ba5eb463cc4355833fc1c054a63af1d7bd88f3bb Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 22 May 2022 20:51:56 +0300 Subject: [PATCH 15/61] =?UTF-8?q?Recheck=20code=20and=20improve=20it=20(No?= =?UTF-8?q?=20CI=20I=20trust=20it=20=F0=9F=A5=B6)=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/2d/CCParticleSystem.cpp | 38 ++++++---- core/2d/CCParticleSystem.h | 130 ++++++++++++++++++++--------------- 2 files changed, 100 insertions(+), 68 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index d6b315424a..ba218b7a6b 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -140,8 +140,8 @@ bool ParticleData::init(int count) deltaRotation = (float*)malloc(count * sizeof(float)); totalTimeToLive = (float*)malloc(count * sizeof(float)); timeToLive = (float*)malloc(count * sizeof(float)); - animTimeDelta = (float*)malloc(count * sizeof(float)); animTimeLength = (float*)malloc(count * sizeof(float)); + animTimeDelta = (float*)malloc(count * sizeof(float)); animIndex = (unsigned short*)malloc(count * sizeof(unsigned short)); animCellIndex = (unsigned short*)malloc(count * sizeof(unsigned short)); atlasIndex = (unsigned int*)malloc(count * sizeof(unsigned int)); @@ -156,9 +156,10 @@ bool ParticleData::init(int count) modeB.deltaRadius = (float*)malloc(count * sizeof(float)); modeB.radius = (float*)malloc(count * sizeof(float)); - return posx && posy && startPosY && startPosX && colorR && colorG && colorB && colorA && deltaColorR && - deltaColorG && deltaColorB && deltaColorA && size && deltaSize && rotation && deltaRotation && totalTimeToLive && - timeToLive && atlasIndex && modeA.dirX && modeA.dirY && modeA.radialAccel && modeA.tangentialAccel && + return posx && posy && startPosX && startPosY && colorR && colorG && colorB && colorA && deltaColorR && + deltaColorG && deltaColorB && deltaColorA && size && deltaSize && rotation && staticRotation && + deltaRotation && totalTimeToLive && timeToLive && animTimeLength && animTimeDelta && animIndex && + animCellIndex && atlasIndex && modeA.dirX && modeA.dirY && modeA.radialAccel && modeA.tangentialAccel && modeB.angle && modeB.degreesPerSecond && modeB.deltaRadius && modeB.radius; } @@ -183,8 +184,8 @@ void ParticleData::release() CC_SAFE_FREE(deltaRotation); CC_SAFE_FREE(totalTimeToLive); CC_SAFE_FREE(timeToLive); - CC_SAFE_FREE(animTimeDelta); CC_SAFE_FREE(animTimeLength); + CC_SAFE_FREE(animTimeDelta); CC_SAFE_FREE(animIndex); CC_SAFE_FREE(animCellIndex); CC_SAFE_FREE(atlasIndex); @@ -230,6 +231,8 @@ ParticleSystem::ParticleSystem() , _startSpinVar(0) , _endSpin(0) , _endSpinVar(0) + , _staticRotation(0) + , _staticRotationVar(0) , _emissionRate(0) , _totalParticles(0) , _texture(nullptr) @@ -995,30 +998,33 @@ void ParticleSystem::setAnimationIndicesAtlas(unsigned int unifiedCellSize, TexA } } -void ParticleSystem::addAnimationIndex(std::string_view frameName) +bool ParticleSystem::addAnimationIndex(std::string_view frameName) { - addAnimationIndex(_animIndexCount++, frameName); + return addAnimationIndex(_animIndexCount, frameName); } -void ParticleSystem::addAnimationIndex(unsigned short index, std::string_view frameName) +bool ParticleSystem::addAnimationIndex(unsigned short index, std::string_view frameName) { auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(frameName); if (frame) - addAnimationIndex(index, frame); + return addAnimationIndex(index, frame); + return false; } -void ParticleSystem::addAnimationIndex(cocos2d::SpriteFrame* frame) +bool ParticleSystem::addAnimationIndex(cocos2d::SpriteFrame* frame) { - addAnimationIndex(_animIndexCount++, frame); + return addAnimationIndex(_animIndexCount, frame); } -void ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::SpriteFrame* frame) +bool ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::SpriteFrame* frame) { - addAnimationIndex(index, frame->getRect(), frame->isRotated()); + if (frame) + return addAnimationIndex(index, frame->getRect(), frame->isRotated()); + return false; } -void ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::Rect rect, bool rotated) +bool ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::Rect rect, bool rotated) { auto iter = _animationIndices.find(index); if (iter == _animationIndices.end()) @@ -1027,6 +1033,10 @@ void ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::Rect rect, auto& desc = iter->second; desc.rect = rect; desc.isRotated = rotated; + + ++_animIndexCount; + + return true; } void ParticleSystem::onEnter() diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index c527cf7fe0..cbbb8621ba 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -743,16 +743,27 @@ public: */ void setEndSpinVar(float endSpinVar) { _endSpinVar = endSpinVar; } - /** Sets the static rotation of each particle + /** Gets the fixed angle of each particle * - * @param angle The angle in degrees that the particle will exist with + * @return The angle in degrees of each particle. */ - virtual void setStaticRotation(float angle) { _staticRotation = angle; }; - /** Sets the static rotation variance of each particle. + float getSpawnRotation() { return _staticRotation; } + /** Sets the fixed angle of each particle * - * @param angle The angle in degrees variance + * @param angle The angle in degrees of each particle. */ - virtual void setStaticRotationVar(float angle) { _staticRotationVar = angle; }; + void setSpawnRotation(float angle) { _staticRotation = angle; } + + /** Sets the fixed angle variance of each particle. + * + * @return The angle variance in degrees of each particle. + */ + float getSpawnRotationVar() { return _staticRotationVar; } + /** Sets the fixed angle variance of each particle. + * + * @param angle The angle variance in degrees of each particle. + */ + void setSpawnRotationVar(float angle) { _staticRotationVar = angle; } /** Gets the emission rate of the particles. * @@ -788,8 +799,7 @@ public: _isLoopAnimated = false; } - /** Enables or disables tex coord animations that are set by the emitter randomly when a particle is emitted. - * WARNING: this won't matter if particle life animation is enabled ie. setLifeAnimation(true) */ + /** Enables or disables tex coord animations that are set by the emitter randomly when a particle is emitted. */ void setEmitterAnimation(bool enabled) { _isEmitterAnimated = enabled; @@ -797,10 +807,7 @@ public: _isLoopAnimated = false; } - /** Enables or disables tex coord animations that are used to make particles play a sequence forever until they die - * This interduces a new concept of animation where you specify the indices and then specify animations descriptors that tell how these indices are used and what speed they're played at. - * Functions that effect this are: setMultiAnimationParticles(), resetAnimationDescriptors(), resetAnimationIndices(), addAnimationIndex(), setAnimationDescriptor(), setMultiAnimationRandom(), setMultiAnimationRandomSpecific(), - */ + /** Enables or disables tex coord animations that are used to make particles play a sequence forever until they die */ void setLoopAnimation(bool enabled) { _isLoopAnimated = enabled; @@ -812,79 +819,94 @@ public: bool isEmitterAnimated() { return _isEmitterAnimated; } bool isLoopAnimated() { return _isLoopAnimated; } - /** Gets the total number of indices. */ + /** Gets the total number of indices. + * + * @return The size of the list holding animation indices. + */ int getTotalAnimationIndices() { return _animIndexCount; } - /** Sets wether to start from first cell and go forward (normal) - * or last cell and go backward (reversed) when using life animation */ + /** Sets wether to start from first cell and go forwards (normal) or last cell and go backwards (reversed) */ void setAnimationReverse(bool reverse) { _isAnimationReversed = reverse; } bool isAnimationReversed() { return _isAnimationReversed; } - /** Resets the count of indices to 0 and empties the index array */ + /** Resets the count of indices to 0 and empties the animation index array */ void resetAnimationIndices(); - /** Resets the container of animation descriptors empties the random array */ + /** Empties the container of animation descriptors */ void resetAnimationDescriptors(); - /** Choose what animation descriptors are to be selected at random for particles + /** Choose what animation descriptors are to be selected at random for particles. * This function should be called after you've inserted/overwritten any animation descriptors. * - * @param animations Array of specific animations to play at random + * @param animations Array of specific indices of animations to play at random */ void setMultiAnimationRandomSpecific(const std::vector &animations) { _randomAnimations = animations; }; /** Choose ALL animation descriptors to be selected at random for particles. - * This function should be called after you've inserted/overwritten any animation descriptors. - */ + * This function should be called after you've inserted/overwritten any animation descriptors. + */ void setMultiAnimationRandom(); /** Add all particle animation indices based on cells size and direction spicified using a texture atlas. - * will erase the array and add new indices from the atlas. - * This function will automatically figure out your atlas cell size and direction for you! thank her later :) */ + * will erase the array and add new indices from the atlas. + * This function will automatically figure out your atlas cell size and direction for you! thank her later :) */ void setAnimationIndicesAtlas(); - /** Add all particle animation indices based on cells size and direction spicified using a texture atlas. - * will erase the array and add new indices from the atlas. - * - * @param unifiedCellSize The size of cell unified. - * @param direction What direction is the atlas - */ + /** Add all particle animation indices based on cell size and direction spicified if the method of rendering preferred is texture atlas. + * will erase the array and add new indices from the atlas. + * + * @param unifiedCellSize The size of cell unified. + * @param direction What direction is the atlas + */ void setAnimationIndicesAtlas(unsigned int unifiedCellSize, TexAnimDir direction = TexAnimDir::HORIZONTAL); /** Add a particle animation index based on tex coords spicified using a sprite frame. - * The index is automatically incremented on each addition. - * - * @param frameName SpriteFrame name to search for - */ - void addAnimationIndex(std::string_view frameName); + * The index is automatically incremented on each addition. + * + * @param frameName SpriteFrame name to search for + * + * @return Returns true of the index was successfully found and added. Otherwise, false + */ + bool addAnimationIndex(std::string_view frameName); /** Add a particle animation index based on tex coords spicified using a sprite frame. - * - * @param frameName SpriteFrame name to search for - */ - void addAnimationIndex(unsigned short index, std::string_view frameName); + * + * @param index Index id to add the frame to or override it with the new frame + * @param frameName SpriteFrame name to search for + * + * @return Returns true of the index was successfully found and added. Otherwise, false + */ + bool addAnimationIndex(unsigned short index, std::string_view frameName); /** Add a particle animation index based on tex coords spicified using a sprite frame. - * The index is automatically incremented on each addition. - * - * @param frame SpriteFrame containting data about tex coords - */ - void addAnimationIndex(cocos2d::SpriteFrame* frame); + * The index is automatically incremented on each addition. + * + * @param frame SpriteFrame containting data about tex coords + * + * @return Returns true of the index was successfully found and added. Otherwise, false + */ + bool addAnimationIndex(cocos2d::SpriteFrame* frame); /** Add a particle animation index based on tex coords spicified using a sprite frame. - * you can specify which index you want to override in this function - * @param index Index id to override the index with - * @param frame SpriteFrame containting data about tex coords - */ - void addAnimationIndex(unsigned short index, cocos2d::SpriteFrame* frame); + * you can specify which index you want to override in this function + * + * @param index Index id to add the frame to or override it with the new frame + * @param frame SpriteFrame containting data about tex coords + * + * @return Returns true of the index was successfully found and added. Otherwise, false + */ + bool addAnimationIndex(unsigned short index, cocos2d::SpriteFrame* frame); /** Add a particle animation index based on tex coords spicified. - * you can specify which index you want to override in this function - * @param index Index id to override the index with - * @param rect Rect containting data about tex coords in pixels - * @param rotated Not implemented. - */ - void addAnimationIndex(unsigned short index, cocos2d::Rect rect, bool rotated = false); + * you can specify which index you want to override in this function + * + * @param index Index id to add the frame to or override it with the new rect + * @param rect Rect containting data about tex coords in pixels + * @param rotated Not implemented. + * + * @return Returns true of the index was successfully found and added. Otherwise, false + */ + bool addAnimationIndex(unsigned short index, cocos2d::Rect rect, bool rotated = false); /** Add a particle animation descriptor with an index. * From 09fa4da8454ada9abf7fc7fd0d8298542d00f60f Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Mon, 23 May 2022 11:11:55 +0300 Subject: [PATCH 16/61] Improve loops using std::fill_n() [skip ci] --- core/2d/CCParticleSystem.cpp | 48 ++++++------------------------------ 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index ba218b7a6b..1646bd4247 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -670,12 +670,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati } if (animationCellIndex != -1) - { - for (int i = start; i < _particleCount; ++i) - { - _particleData.animCellIndex[i] = animationCellIndex; - } - } + std::fill_n(_particleData.animCellIndex + start, _particleCount - start, animationCellIndex); if (animationIndex == -1 && !_animations.empty()) { @@ -694,27 +689,14 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati } if (_isEmitterAnimated || _isLoopAnimated) - { - for (int i = start; i < _particleCount; ++i) - { - _particleData.animTimeDelta[i] = 0; - } - } - - if (_isLoopAnimated && _animations.empty()) - { - for (int i = start; i < _particleCount; ++i) - { - _particleData.animTimeDelta[i] = 0; - } - } + std::fill_n(_particleData.animTimeDelta + start, _particleCount - start, 0); if (animationIndex != -1) { for (int i = start; i < _particleCount; ++i) { _particleData.animIndex[i] = animationIndex; - auto& descriptor = _animations.at(_particleData.animIndex[i]); + auto& descriptor = _animations.at(animationIndex); _particleData.animTimeLength[i] = descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_M11(&RANDSEED); } @@ -765,12 +747,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati } } else - { - for (int i = start; i < _particleCount; ++i) - { - _particleData.deltaSize[i] = 0.0f; - } - } + std::fill_n(_particleData.deltaSize + start, _particleCount - start, 0.0F); // rotation for (int i = start; i < _particleCount; ++i) @@ -799,14 +776,8 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati { pos = _position; } - for (int i = start; i < _particleCount; ++i) - { - _particleData.startPosX[i] = pos.x; - } - for (int i = start; i < _particleCount; ++i) - { - _particleData.startPosY[i] = pos.y; - } + std::fill_n(_particleData.startPosX + start, _particleCount - start, pos.x); + std::fill_n(_particleData.startPosY + start, _particleCount - start, pos.y); // Mode Gravity: A if (_emitterMode == Mode::GRAVITY) @@ -875,12 +846,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati } if (modeB.endRadius == START_RADIUS_EQUAL_TO_END_RADIUS) - { - for (int i = start; i < _particleCount; ++i) - { - _particleData.modeB.deltaRadius[i] = 0.0f; - } - } + std::fill_n(_particleData.modeB.deltaRadius + start, _particleCount - start, 0.0F); else { for (int i = start; i < _particleCount; ++i) From 3dff79f41675f7cd1ecce140b8f9a0e53f40f734 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Mon, 23 May 2022 11:32:54 +0300 Subject: [PATCH 17/61] Separate quad animation loop from color loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves cache hit rate which in turn increases performance πŸ₯Ά Of course that's viable when you're using pure memory pointer arrays that use simple POD-like data types. --- core/2d/CCParticleSystem.cpp | 16 +++++++--------- core/2d/CCParticleSystemQuad.cpp | 27 +++++++++++++++++---------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 1646bd4247..901855a3e0 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1085,6 +1085,12 @@ void ParticleSystem::update(float dt) } } + // The reason for using for-loops separately for every property is because + // When the processor needs to read from or write to a location in memory, + // it first checks whether a copy of that data is in the cpu's cache. + // And wether if every property's memory of the particle system is continuous, + // for the purpose of improving cache hit rate, we should process only one property in one for-loop. + // It was proved to be effective especially for low-end devices. { for (int i = 0; i < _particleCount; ++i) { @@ -1136,9 +1142,7 @@ void ParticleSystem::update(float dt) anim.animationIndices.size() - 1)]; } if (_isLoopAnimated && _animations.empty()) - { - _particleData.animCellIndex[i] = 0; - } + std::fill_n(_particleData.animTimeDelta, _particleCount, 0); } for (int i = 0; i < _particleCount; ++i) @@ -1211,12 +1215,6 @@ void ParticleSystem::update(float dt) } else { - // Why use so many for-loop separately instead of putting them together? - // When the processor needs to read from or write to a location in memory, - // it first checks whether a copy of that data is in the cache. - // And every property's memory of the particle system is continuous, - // for the purpose of improving cache hit rate, we should process only one property in one for-loop AFAP. - // It was proved to be effective especially for low-end machine. for (int i = 0; i < _particleCount; ++i) { _particleData.modeB.angle[i] += _particleData.modeB.degreesPerSecond[i] * dt; diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index a2e08ef371..658ef5bb35 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -453,9 +453,8 @@ void ParticleSystemQuad::updateParticleQuads() float* g = _particleData.colorG; float* b = _particleData.colorB; float* a = _particleData.colorA; - unsigned short* cellIndex = _particleData.animCellIndex; - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++cellIndex) + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) { uint8_t colorR = *r * *a * 255; uint8_t colorG = *g * *a * 255; @@ -465,9 +464,6 @@ void ParticleSystemQuad::updateParticleQuads() quad->br.colors.set(colorR, colorG, colorB, colorA); quad->tl.colors.set(colorR, colorG, colorB, colorA); quad->tr.colors.set(colorR, colorG, colorB, colorA); - - if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) - setTexCoords(quad, cellIndex); } } else @@ -477,9 +473,8 @@ void ParticleSystemQuad::updateParticleQuads() float* g = _particleData.colorG; float* b = _particleData.colorB; float* a = _particleData.colorA; - unsigned short* cellIndex = _particleData.animCellIndex; - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++cellIndex) + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) { uint8_t colorR = *r * 255; uint8_t colorG = *g * 255; @@ -489,11 +484,23 @@ void ParticleSystemQuad::updateParticleQuads() quad->br.colors.set(colorR, colorG, colorB, colorA); quad->tl.colors.set(colorR, colorG, colorB, colorA); quad->tr.colors.set(colorR, colorG, colorB, colorA); - - if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) - setTexCoords(quad, cellIndex); } } + + // The reason for using for-loops separately for every property is because + // When the processor needs to read from or write to a location in memory, + // it first checks whether a copy of that data is in the cpu's cache. + // And wether if every property's memory of the particle system is continuous, + // for the purpose of improving cache hit rate, we should process only one property in one for-loop. + // It was proved to be effective especially for low-end devices. + if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) + { + V3F_C4B_T2F_Quad* quad = startQuad; + unsigned short* cellIndex = _particleData.animCellIndex; + + for (int i = 0; i < _particleCount; ++i, ++quad, ++cellIndex) + setTexCoords(quad, cellIndex); + } } // overriding draw method From 6e1d48835e1be5d39303e63800dee1037320b7b0 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Mon, 23 May 2022 16:04:09 +0300 Subject: [PATCH 18/61] Improve particle RNG and add useful functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced M11 RNG with KISS RNG which is better and fixes a problem with particle strafing, added the ability to pause the entire particle system (useful for pause menus) and ability to control particle system time speed multiplier πŸ’ͺπŸ₯Ά --- core/2d/CCParticleSystem.cpp | 115 +++++++++++++++++++++++------------ core/2d/CCParticleSystem.h | 31 +++++++++- 2 files changed, 104 insertions(+), 42 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 901855a3e0..4a44107171 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -98,18 +98,23 @@ inline void normalize_point(float x, float y, particle_point* out) } /** - A more effective random number generator function, made by ejoy2d. + A more effective random number generator function that fixes strafing for position variance, made by kiss rng. + KEEP IT SIMPLE STUPID (KISS) rng example: https://gist.github.com/3ki5tj/7b1d51e96d1f9bfb89bc */ -inline static float RANDOM_M11(unsigned int* seed) +inline static float RANDOM_KISS(void) { - *seed = *seed * 134775813 + 1; - union - { - uint32_t d; - float f; - } u; - u.d = (((uint32_t)(*seed) & 0x7fff) << 8) | 0x40000000; - return u.f - 3.0f; +#define kiss_znew(z) (z = 36969 * (z & 65535) + (z >> 16)) +#define kiss_wnew(w) (w = 18000 * (w & 65535) + (w >> 16)) +#define kiss_MWC(z, w) ((kiss_znew(z) << 16) + kiss_wnew(w)) +#define kiss_SHR3(jsr) (jsr ^= (jsr << 17), jsr ^= (jsr >> 13), jsr ^= (jsr << 5)) +#define kiss_CONG(jc) (jc = 69069 * jc + 1234567) +#define kiss_KISS(z, w, jc, jsr) ((kiss_MWC(z, w) ^ kiss_CONG(jc)) + kiss_SHR3(jsr)) + + static unsigned kiss_z = rand(), kiss_w = rand(), kiss_jsr = rand(), kiss_jcong = rand(); + // Generate two random floats and add them to get a total of 2.0 and then subtract 1.0 + // to get a random number between -1.0 and 1.0 INCLUSIVE. + return -1.0F + ((kiss_KISS(kiss_z, kiss_w, kiss_jcong, kiss_jsr) / 4294967296.0) + + (kiss_KISS(kiss_z, kiss_w, kiss_jcong, kiss_jsr) / 4294967296.0)); } ParticleData::ParticleData() @@ -246,6 +251,8 @@ ParticleSystem::ParticleSystem() , _yCoordFlipped(1) , _positionType(PositionType::FREE) , _paused(false) + , _updatePaused(false) + , _timeScale(1) , _sourcePositionCompatible(true) // In the furture this member's default value maybe false or be removed. { modeA.gravity.setZero(); @@ -632,8 +639,6 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati { if (_paused) return; - uint32_t RANDSEED = rand(); - animationCellIndex = MIN(animationCellIndex, _animIndexCount - 1); animationIndex = MIN(animationIndex, _animIndexCount - 1); @@ -644,7 +649,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati // life for (int i = start; i < _particleCount; ++i) { - float particleLife = _life + _lifeVar * RANDOM_M11(&RANDSEED); + float particleLife = _life + _lifeVar * RANDOM_KISS(); _particleData.totalTimeToLive[i] = MAX(0, particleLife); _particleData.timeToLive[i] = MAX(0, particleLife); } @@ -652,19 +657,20 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati // position for (int i = start; i < _particleCount; ++i) { - _particleData.posx[i] = _sourcePosition.x + _posVar.x * RANDOM_M11(&RANDSEED); + auto f = RANDOM_KISS(); + _particleData.posx[i] = _sourcePosition.x + _posVar.x * RANDOM_KISS(); } for (int i = start; i < _particleCount; ++i) { - _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_M11(&RANDSEED); + _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_KISS(); } if (animationCellIndex == -1 && _isEmitterAnimated) { for (int i = start; i < _particleCount; ++i) { - int rand0 = abs(RANDOM_M11(&RANDSEED) * _animIndexCount); + int rand0 = abs(RANDOM_KISS() * _animIndexCount); _particleData.animCellIndex[i] = MIN(rand0, _animIndexCount - 1); } } @@ -679,12 +685,12 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati for (int i = start; i < _particleCount; ++i) { - int rand0 = abs(RANDOM_M11(&RANDSEED) * _randomAnimations.size()); + int rand0 = abs(RANDOM_KISS() * _randomAnimations.size()); int index = MIN(rand0, _randomAnimations.size() - 1); _particleData.animIndex[i] = _randomAnimations[index]; auto& descriptor = _animations.at(_particleData.animIndex[i]); _particleData.animTimeLength[i] = - descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_M11(&RANDSEED); + descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_KISS(); } } @@ -698,7 +704,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati _particleData.animIndex[i] = animationIndex; auto& descriptor = _animations.at(animationIndex); _particleData.animTimeLength[i] = - descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_M11(&RANDSEED); + descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_KISS(); } } @@ -706,7 +712,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati #define SET_COLOR(c, b, v) \ for (int i = start; i < _particleCount; ++i) \ { \ - c[i] = clampf(b + v * RANDOM_M11(&RANDSEED), 0, 1); \ + c[i] = clampf(b + v * RANDOM_KISS(), 0, 1); \ } SET_COLOR(_particleData.colorR, _startColor.r, _startColorVar.r); @@ -733,7 +739,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati // size for (int i = start; i < _particleCount; ++i) { - _particleData.size[i] = _startSize + _startSizeVar * RANDOM_M11(&RANDSEED); + _particleData.size[i] = _startSize + _startSizeVar * RANDOM_KISS(); _particleData.size[i] = MAX(0, _particleData.size[i]); } @@ -741,7 +747,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati { for (int i = start; i < _particleCount; ++i) { - float endSize = _endSize + _endSizeVar * RANDOM_M11(&RANDSEED); + float endSize = _endSize + _endSizeVar * RANDOM_KISS(); endSize = MAX(0, endSize); _particleData.deltaSize[i] = (endSize - _particleData.size[i]) / _particleData.timeToLive[i]; } @@ -752,18 +758,18 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati // rotation for (int i = start; i < _particleCount; ++i) { - _particleData.rotation[i] = _startSpin + _startSpinVar * RANDOM_M11(&RANDSEED); + _particleData.rotation[i] = _startSpin + _startSpinVar * RANDOM_KISS(); } for (int i = start; i < _particleCount; ++i) { - float endA = _endSpin + _endSpinVar * RANDOM_M11(&RANDSEED); + float endA = _endSpin + _endSpinVar * RANDOM_KISS(); _particleData.deltaRotation[i] = (endA - _particleData.rotation[i]) / _particleData.timeToLive[i]; } // static rotation for (int i = start; i < _particleCount; ++i) { - _particleData.staticRotation[i] = _staticRotation + _staticRotationVar * RANDOM_M11(&RANDSEED); + _particleData.staticRotation[i] = _staticRotation + _staticRotationVar * RANDOM_KISS(); } // position @@ -786,14 +792,14 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati // radial accel for (int i = start; i < _particleCount; ++i) { - _particleData.modeA.radialAccel[i] = modeA.radialAccel + modeA.radialAccelVar * RANDOM_M11(&RANDSEED); + _particleData.modeA.radialAccel[i] = modeA.radialAccel + modeA.radialAccelVar * RANDOM_KISS(); } // tangential accel for (int i = start; i < _particleCount; ++i) { _particleData.modeA.tangentialAccel[i] = - modeA.tangentialAccel + modeA.tangentialAccelVar * RANDOM_M11(&RANDSEED); + modeA.tangentialAccel + modeA.tangentialAccelVar * RANDOM_KISS(); } // rotation is dir @@ -801,9 +807,9 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati { for (int i = start; i < _particleCount; ++i) { - float a = CC_DEGREES_TO_RADIANS(_angle + _angleVar * RANDOM_M11(&RANDSEED)); + float a = CC_DEGREES_TO_RADIANS(_angle + _angleVar * RANDOM_KISS()); Vec2 v(cosf(a), sinf(a)); - float s = modeA.speed + modeA.speedVar * RANDOM_M11(&RANDSEED); + float s = modeA.speed + modeA.speedVar * RANDOM_KISS(); Vec2 dir = v * s; _particleData.modeA.dirX[i] = dir.x; // v * s ; _particleData.modeA.dirY[i] = dir.y; @@ -814,9 +820,9 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati { for (int i = start; i < _particleCount; ++i) { - float a = CC_DEGREES_TO_RADIANS(_angle + _angleVar * RANDOM_M11(&RANDSEED)); + float a = CC_DEGREES_TO_RADIANS(_angle + _angleVar * RANDOM_KISS()); Vec2 v(cosf(a), sinf(a)); - float s = modeA.speed + modeA.speedVar * RANDOM_M11(&RANDSEED); + float s = modeA.speed + modeA.speedVar * RANDOM_KISS(); Vec2 dir = v * s; _particleData.modeA.dirX[i] = dir.x; // v * s ; _particleData.modeA.dirY[i] = dir.y; @@ -831,18 +837,18 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati // Set the default diameter of the particle from the source position for (int i = start; i < _particleCount; ++i) { - _particleData.modeB.radius[i] = modeB.startRadius + modeB.startRadiusVar * RANDOM_M11(&RANDSEED); + _particleData.modeB.radius[i] = modeB.startRadius + modeB.startRadiusVar * RANDOM_KISS(); } for (int i = start; i < _particleCount; ++i) { - _particleData.modeB.angle[i] = CC_DEGREES_TO_RADIANS(_angle + _angleVar * RANDOM_M11(&RANDSEED)); + _particleData.modeB.angle[i] = CC_DEGREES_TO_RADIANS(_angle + _angleVar * RANDOM_KISS()); } for (int i = start; i < _particleCount; ++i) { _particleData.modeB.degreesPerSecond[i] = - CC_DEGREES_TO_RADIANS(modeB.rotatePerSecond + modeB.rotatePerSecondVar * RANDOM_M11(&RANDSEED)); + CC_DEGREES_TO_RADIANS(modeB.rotatePerSecond + modeB.rotatePerSecondVar * RANDOM_KISS()); } if (modeB.endRadius == START_RADIUS_EQUAL_TO_END_RADIUS) @@ -851,7 +857,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati { for (int i = start; i < _particleCount; ++i) { - float endRadius = modeB.endRadius + modeB.endRadiusVar * RANDOM_M11(&RANDSEED); + float endRadius = modeB.endRadius + modeB.endRadiusVar * RANDOM_KISS(); _particleData.modeB.deltaRadius[i] = (endRadius - _particleData.modeB.radius[i]) / _particleData.timeToLive[i]; } @@ -1052,6 +1058,10 @@ bool ParticleSystem::isFull() // ParticleSystem - MainLoop void ParticleSystem::update(float dt) { + // don't process particles nor update gl buffer when this node is invisible. + if (!_visible || _updatePaused) + return; + CC_PROFILER_START_CATEGORY(kProfilerCategoryParticles, "CCParticleSystem - update"); if (_componentContainer && !_componentContainer->isEmpty()) @@ -1059,6 +1069,8 @@ void ParticleSystem::update(float dt) _componentContainer->visit(dt); } + dt *= _timeScale; + if (_isActive && _emissionRate) { float rate = 1.0f / _emissionRate; @@ -1068,8 +1080,7 @@ void ParticleSystem::update(float dt) if (_particleCount < totalParticles) { _emitCounter += dt; - if (_emitCounter < 0.f) - _emitCounter = 0.f; + _emitCounter = MAX(0.0F, _emitCounter); } int emitCount = MIN(totalParticles - _particleCount, _emitCounter / rate); @@ -1101,8 +1112,7 @@ void ParticleSystem::update(float dt) if (_particleData.animTimeDelta[i] > _particleData.animTimeLength[i]) { auto& anim = _animations.at(_particleData.animIndex[i]); - uint32_t RANDSEED = rand(); - float percent = abs(RANDOM_M11(&RANDSEED)); + float percent = abs(RANDOM_KISS()); percent = anim.reverseIndices ? 1.0F - percent : percent; _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), @@ -1272,7 +1282,7 @@ void ParticleSystem::update(float dt) _transformSystemDirty = false; } - // only update gl buffer when visible + // update and send gl buffer only when this node is visible. if (_visible && !_batchNode) { postStep(); @@ -1643,4 +1653,29 @@ void ParticleSystem::resumeEmissions() _paused = false; } +bool ParticleSystem::isUpdatePaused() const +{ + return _updatePaused; +} + +void ParticleSystem::pauseUpdate() +{ + _updatePaused = true; +} + +void ParticleSystem::resumeUpdate() +{ + _updatePaused = false; +} + +float ParticleSystem::getTimeScale() +{ + return _timeScale; +} + +void ParticleSystem::setTimeScale(float scale) +{ + _timeScale = scale; +} + NS_CC_END diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index cbbb8621ba..f54df2c7ef 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -1016,12 +1016,33 @@ public: */ virtual bool isPaused() const; - /* Pause the emissions*/ + /* Pause the emissions */ virtual void pauseEmissions(); - /* UnPause the emissions*/ + /* Unpause the emissions */ virtual void resumeEmissions(); + /** Is system update paused + @return True if the emissions are paused, else false + */ + virtual bool isUpdatePaused() const; + + /* Pause the particles from being updated */ + virtual void pauseUpdate(); + + /* Unpause the particles from being updated */ + virtual void resumeUpdate(); + + /** Gets the time scale of the particle system. + @return Time scale of the particle system. + */ + virtual float getTimeScale(); + + /** Gets the time scale of the particle system. + @param Time scale of the particle system. (default: 1.0) + */ + virtual void setTimeScale(float scale = 1.0F); + protected: virtual void updateBlendFunc(); @@ -1210,6 +1231,12 @@ protected: /** is the emitter paused */ bool _paused; + /** is system update paused */ + bool _updatePaused; + + /** time scale of the particle system */ + float _timeScale; + /** is sourcePosition compatible */ bool _sourcePositionCompatible; From 355eee6aace1588979f781bfb87f38ebe785a9f6 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Mon, 23 May 2022 18:35:36 +0300 Subject: [PATCH 19/61] Fix particle bursts when using manual emissions. --- core/2d/CCParticleSystem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 4a44107171..2dec103ff3 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -640,6 +640,9 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati if (_paused) return; + // Try to add as many particles as you can without overflowing. + count = MIN(int(_totalParticles * __totalParticleCountFactor) - _particleCount, count); + animationCellIndex = MIN(animationCellIndex, _animIndexCount - 1); animationIndex = MIN(animationIndex, _animIndexCount - 1); From 27eecf1857430cd15a1b0187cc736e671c20b5f6 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Tue, 24 May 2022 00:46:38 +0300 Subject: [PATCH 20/61] Improve code and add useful functions. Renamed some variables and functions correctly, added the ability to simulate particles and give them a true random look. --- core/2d/CCParticleSystem.cpp | 76 ++++++++++++++++++++++++++++++++---- core/2d/CCParticleSystem.h | 70 +++++++++++++++++++++++++++------ 2 files changed, 128 insertions(+), 18 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 2dec103ff3..c08a0eadf5 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1,3 +1,7 @@ +#include "CCParticleSystem.h" +#include "CCParticleSystem.h" +#include "CCParticleSystem.h" +#include "CCParticleSystem.h" /**************************************************************************** Copyright (c) 2008-2010 Ricardo Quesada Copyright (c) 2010-2012 cocos2d-x.org @@ -236,8 +240,8 @@ ParticleSystem::ParticleSystem() , _startSpinVar(0) , _endSpin(0) , _endSpinVar(0) - , _staticRotation(0) - , _staticRotationVar(0) + , _spawnAngle(0) + , _spawnAngleVar(0) , _emissionRate(0) , _totalParticles(0) , _texture(nullptr) @@ -253,6 +257,8 @@ ParticleSystem::ParticleSystem() , _paused(false) , _updatePaused(false) , _timeScale(1) + , _fixedFPS(0) + , _fixedFPSDelta(0) , _sourcePositionCompatible(true) // In the furture this member's default value maybe false or be removed. { modeA.gravity.setZero(); @@ -772,7 +778,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati // static rotation for (int i = start; i < _particleCount; ++i) { - _particleData.staticRotation[i] = _staticRotation + _staticRotationVar * RANDOM_KISS(); + _particleData.staticRotation[i] = _spawnAngle + _spawnAngleVar * RANDOM_KISS(); } // position @@ -1014,6 +1020,46 @@ bool ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::Rect rect, return true; } +void ParticleSystem::simulate(float seconds) +{ + simulateFPS(seconds, 1.0F / Director::getInstance()->getAnimationInterval()); +} + +void ParticleSystem::simulateFPS(float seconds, float frameRate) +{ + auto l_updatePaused = _updatePaused; + _updatePaused = false; + seconds = seconds == SIMULATION_USE_PARTICLE_LIFETIME ? + getLife() + getLifeVar() : seconds; + auto delta = 1.0F / frameRate; + float lastDelta = 0.0F; + if (seconds > delta) + { + while (seconds > 0.0F) + { + this->update(delta); + lastDelta = seconds; + seconds -= delta; + } + this->update(seconds); + } + else + this->update(seconds); + _updatePaused = l_updatePaused; +} + +void ParticleSystem::resimulate(float seconds) +{ + this->resetSystem(); + this->simulate(seconds); +} + +void ParticleSystem::resimulateFPS(float seconds, float frameRate) +{ + this->resetSystem(); + this->simulateFPS(seconds, frameRate); +} + void ParticleSystem::onEnter() { Node::onEnter(); @@ -1047,10 +1093,7 @@ void ParticleSystem::resetSystem() { _isActive = true; _elapsed = 0; - for (int i = 0; i < _particleCount; ++i) - { - _particleData.timeToLive[i] = 0.0f; - } + std::fill_n(_particleData.timeToLive, _particleCount, 0.0F); } bool ParticleSystem::isFull() @@ -1072,6 +1115,15 @@ void ParticleSystem::update(float dt) _componentContainer->visit(dt); } + if (_fixedFPS != 0) + { + _fixedFPSDelta += dt; + if (_fixedFPSDelta < 1.0F / _fixedFPS) + return; + dt = _fixedFPSDelta; + _fixedFPSDelta = 0.0F; + } + dt *= _timeScale; if (_isActive && _emissionRate) @@ -1681,4 +1733,14 @@ void ParticleSystem::setTimeScale(float scale) _timeScale = scale; } +float ParticleSystem::getFixedFPS() +{ + return _fixedFPS; +} + +void ParticleSystem::setFixedFPS(float frameRate) +{ + _fixedFPS = frameRate; +} + NS_CC_END diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index f54df2c7ef..68d6b921b4 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -268,6 +268,9 @@ public: /** The starting radius of the particle is equal to the ending radius. */ START_RADIUS_EQUAL_TO_END_RADIUS = -1, + + /** The simulation's seconds are set to the particles' lifetime specified inclusive of variant. */ + SIMULATION_USE_PARTICLE_LIFETIME = -1, }; /** Creates an initializes a ParticleSystem from a plist file. @@ -743,27 +746,27 @@ public: */ void setEndSpinVar(float endSpinVar) { _endSpinVar = endSpinVar; } - /** Gets the fixed angle of each particle + /** Gets the spawn angle of each particle * * @return The angle in degrees of each particle. */ - float getSpawnRotation() { return _staticRotation; } - /** Sets the fixed angle of each particle + float getSpawnAngle() { return _spawnAngle; } + /** Sets the spawn angle of each particle * * @param angle The angle in degrees of each particle. */ - void setSpawnRotation(float angle) { _staticRotation = angle; } + void setSpawnAngle(float angle) { _spawnAngle = angle; } - /** Sets the fixed angle variance of each particle. + /** Sets the spawn angle variance of each particle. * * @return The angle variance in degrees of each particle. */ - float getSpawnRotationVar() { return _staticRotationVar; } - /** Sets the fixed angle variance of each particle. + float getSpawnAngleVar() { return _spawnAngleVar; } + /** Sets the spawn angle variance of each particle. * * @param angle The angle variance in degrees of each particle. */ - void setSpawnRotationVar(float angle) { _staticRotationVar = angle; } + void setSpawnAngleVar(float angle) { _spawnAngleVar = angle; } /** Gets the emission rate of the particles. * @@ -945,6 +948,35 @@ public: */ void setPositionType(PositionType type) { _positionType = type; } + /** Advance the particle system and make it seem like it ran for this many seconds. + * The frame rate used for simulation accuracy is the screens refresh rate. + * + * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) + */ + void simulate(float seconds = SIMULATION_USE_PARTICLE_LIFETIME); + + /** Advance the particle system and make it seem like it ran for this many seconds. + * + * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) + * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more accurate the simulation will be at the cost of performance. + */ + void simulateFPS(float seconds = SIMULATION_USE_PARTICLE_LIFETIME, float frameRate = 30.0F); + + /** Resets the particle system and then advances the particle system and make it seem like it ran for this many + * seconds. The frame rate used for simulation accuracy is the screens refresh rate. + * + * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) + */ + void resimulate(float seconds = SIMULATION_USE_PARTICLE_LIFETIME); + + /** Resets the particle system and then advances the particle system and make it seem like it ran for this many + * seconds. The frame rate used for simulation accuracy is the screens refresh rate. + * + * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) + * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more accurate the simulation will be at the cost of performance. + */ + void resimulateFPS(float seconds = SIMULATION_USE_PARTICLE_LIFETIME, float frameRate = 30.0F); + // Overrides virtual void onEnter() override; virtual void onExit() override; @@ -1043,6 +1075,16 @@ public: */ virtual void setTimeScale(float scale = 1.0F); + /** Gets the fixed frame rate count of the particle system. + @return Fixed frame rate count of the particle system. + */ + virtual float getFixedFPS(); + + /** Sets the fixed frame rate count of the particle system. + @param Fixed frame rate count of the particle system. (default: 0.0) + */ + virtual void setFixedFPS(float frameRate = 0.0F); + protected: virtual void updateBlendFunc(); @@ -1191,9 +1233,9 @@ protected: //* initial angle of each particle float _endSpinVar; //* initial rotation of each particle - float _staticRotation; + float _spawnAngle; //* initial rotation of each particle - float _staticRotationVar; + float _spawnAngleVar; /** emission rate of the particles */ float _emissionRate; /** maximum particles of the system */ @@ -1231,12 +1273,18 @@ protected: /** is the emitter paused */ bool _paused; - /** is system update paused */ + /** is particle system update paused */ bool _updatePaused; /** time scale of the particle system */ float _timeScale; + /** Fixed frame rate of the particle system */ + float _fixedFPS; + + /** Fixed frame rate delta (internal) */ + float _fixedFPSDelta; + /** is sourcePosition compatible */ bool _sourcePositionCompatible; From 9a175cb49b1fc055e66bf0507f0b442c3e4278de Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Tue, 24 May 2022 00:49:17 +0300 Subject: [PATCH 21/61] Add fixed FPS function for particle system. Useful for low-end devices. --- core/2d/CCParticleSystem.cpp | 20 ++++++++++---------- core/2d/CCParticleSystem.h | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index c08a0eadf5..1dd2d2fdfd 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1723,16 +1723,6 @@ void ParticleSystem::resumeUpdate() _updatePaused = false; } -float ParticleSystem::getTimeScale() -{ - return _timeScale; -} - -void ParticleSystem::setTimeScale(float scale) -{ - _timeScale = scale; -} - float ParticleSystem::getFixedFPS() { return _fixedFPS; @@ -1743,4 +1733,14 @@ void ParticleSystem::setFixedFPS(float frameRate) _fixedFPS = frameRate; } +float ParticleSystem::getTimeScale() +{ + return _timeScale; +} + +void ParticleSystem::setTimeScale(float scale) +{ + _timeScale = scale; +} + NS_CC_END diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 68d6b921b4..5bd83f541f 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -1065,16 +1065,6 @@ public: /* Unpause the particles from being updated */ virtual void resumeUpdate(); - /** Gets the time scale of the particle system. - @return Time scale of the particle system. - */ - virtual float getTimeScale(); - - /** Gets the time scale of the particle system. - @param Time scale of the particle system. (default: 1.0) - */ - virtual void setTimeScale(float scale = 1.0F); - /** Gets the fixed frame rate count of the particle system. @return Fixed frame rate count of the particle system. */ @@ -1085,6 +1075,16 @@ public: */ virtual void setFixedFPS(float frameRate = 0.0F); + /** Gets the time scale of the particle system. + @return Time scale of the particle system. + */ + virtual float getTimeScale(); + + /** Gets the time scale of the particle system. + @param Time scale of the particle system. (default: 1.0) + */ + virtual void setTimeScale(float scale = 1.0F); + protected: virtual void updateBlendFunc(); From 3c3069b4fce1051bf8dc80d181f7dbc4359d65a8 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Tue, 24 May 2022 08:58:49 +0300 Subject: [PATCH 22/61] =?UTF-8?q?Stop=20annoying=20me=20visual=20studio=20?= =?UTF-8?q?=F0=9F=98=A0=20ur=20not=20helping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/2d/CCParticleSystem.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 1dd2d2fdfd..8a2081994f 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1,7 +1,3 @@ -#include "CCParticleSystem.h" -#include "CCParticleSystem.h" -#include "CCParticleSystem.h" -#include "CCParticleSystem.h" /**************************************************************************** Copyright (c) 2008-2010 Ricardo Quesada Copyright (c) 2010-2012 cocos2d-x.org From ab3854f51a2190c564bce8138ed0acae6483d1d7 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Tue, 24 May 2022 09:42:27 +0300 Subject: [PATCH 23/61] Improve code and add stutter compensation. --- core/2d/CCParticleSystem.cpp | 29 ++++++++++++----------------- core/2d/CCParticleSystem.h | 27 +++++++++------------------ 2 files changed, 21 insertions(+), 35 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 8a2081994f..1ff6b17547 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1016,25 +1016,20 @@ bool ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::Rect rect, return true; } -void ParticleSystem::simulate(float seconds) -{ - simulateFPS(seconds, 1.0F / Director::getInstance()->getAnimationInterval()); -} - -void ParticleSystem::simulateFPS(float seconds, float frameRate) +void ParticleSystem::simulate(float seconds, float frameRate) { auto l_updatePaused = _updatePaused; _updatePaused = false; - seconds = seconds == SIMULATION_USE_PARTICLE_LIFETIME ? + seconds = seconds == SIMULATION_USE_PARTICLE_LIFETIME ? getLife() + getLifeVar() : seconds; + frameRate = frameRate == SIMULATION_USE_GAME_ANIMATION_INTERVAL ? + 1.0F / Director::getInstance()->getAnimationInterval() : frameRate; auto delta = 1.0F / frameRate; - float lastDelta = 0.0F; if (seconds > delta) { while (seconds > 0.0F) { this->update(delta); - lastDelta = seconds; seconds -= delta; } this->update(seconds); @@ -1044,16 +1039,10 @@ void ParticleSystem::simulateFPS(float seconds, float frameRate) _updatePaused = l_updatePaused; } -void ParticleSystem::resimulate(float seconds) +void ParticleSystem::resimulate(float seconds, float frameRate) { this->resetSystem(); - this->simulate(seconds); -} - -void ParticleSystem::resimulateFPS(float seconds, float frameRate) -{ - this->resetSystem(); - this->simulateFPS(seconds, frameRate); + this->simulate(seconds, frameRate); } void ParticleSystem::onEnter() @@ -1111,6 +1100,12 @@ void ParticleSystem::update(float dt) _componentContainer->visit(dt); } + if (dt > 0.5F) + { + this->simulate(dt, 15); + return; + } + if (_fixedFPS != 0) { _fixedFPSDelta += dt; diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 5bd83f541f..ad67e233aa 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -271,6 +271,9 @@ public: /** The simulation's seconds are set to the particles' lifetime specified inclusive of variant. */ SIMULATION_USE_PARTICLE_LIFETIME = -1, + + /** The simulation's framerate is set to the animation interval specified in director. */ + SIMULATION_USE_GAME_ANIMATION_INTERVAL = -1, }; /** Creates an initializes a ParticleSystem from a plist file. @@ -948,34 +951,22 @@ public: */ void setPositionType(PositionType type) { _positionType = type; } - /** Advance the particle system and make it seem like it ran for this many seconds. - * The frame rate used for simulation accuracy is the screens refresh rate. - * - * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) - */ - void simulate(float seconds = SIMULATION_USE_PARTICLE_LIFETIME); - /** Advance the particle system and make it seem like it ran for this many seconds. * * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) - * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more accurate the simulation will be at the cost of performance. + * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more accurate the simulation will be at the cost of performance. value of -1 means (SIMULATION_USE_GAME_ANIMATION_INTERVAL) */ - void simulateFPS(float seconds = SIMULATION_USE_PARTICLE_LIFETIME, float frameRate = 30.0F); + void simulate(float seconds = SIMULATION_USE_PARTICLE_LIFETIME, + float frameRate = SIMULATION_USE_GAME_ANIMATION_INTERVAL); /** Resets the particle system and then advances the particle system and make it seem like it ran for this many * seconds. The frame rate used for simulation accuracy is the screens refresh rate. * * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) + * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more accurate the simulation will be at the cost of performance. value of -1 means (SIMULATION_USE_GAME_ANIMATION_INTERVAL) */ - void resimulate(float seconds = SIMULATION_USE_PARTICLE_LIFETIME); - - /** Resets the particle system and then advances the particle system and make it seem like it ran for this many - * seconds. The frame rate used for simulation accuracy is the screens refresh rate. - * - * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) - * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more accurate the simulation will be at the cost of performance. - */ - void resimulateFPS(float seconds = SIMULATION_USE_PARTICLE_LIFETIME, float frameRate = 30.0F); + void resimulate(float seconds = SIMULATION_USE_PARTICLE_LIFETIME, + float frameRate = SIMULATION_USE_GAME_ANIMATION_INTERVAL); // Overrides virtual void onEnter() override; From 30f7decf25bad6847bbc8cd9df78b1e101d4a454 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Tue, 24 May 2022 11:26:53 +0300 Subject: [PATCH 24/61] Improve code and add frame animation independence and null indices. --- core/2d/CCParticleSystem.cpp | 20 ++++++---- core/2d/CCParticleSystem.h | 17 +++++++++ core/2d/CCParticleSystemQuad.cpp | 65 +++++++++++++++++--------------- 3 files changed, 63 insertions(+), 39 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 1ff6b17547..a6202c60ac 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -248,6 +248,7 @@ ParticleSystem::ParticleSystem() , _isLoopAnimated(false) , _animIndexCount(0) , _isAnimationReversed(false) + , _undefinedIndexRect({0,0,0,0}) , _yCoordFlipped(1) , _positionType(PositionType::FREE) , _paused(false) @@ -1115,6 +1116,7 @@ void ParticleSystem::update(float dt) _fixedFPSDelta = 0.0F; } + float pureDt = dt; dt *= _timeScale; if (_isActive && _emissionRate) @@ -1154,15 +1156,15 @@ void ParticleSystem::update(float dt) _particleData.timeToLive[i] -= dt; if (_isEmitterAnimated && !_animations.empty()) { - _particleData.animTimeDelta[i] += dt; + _particleData.animTimeDelta[i] += (_animationTimescaleInd ? pureDt : dt); if (_particleData.animTimeDelta[i] > _particleData.animTimeLength[i]) { - auto& anim = _animations.at(_particleData.animIndex[i]); - float percent = abs(RANDOM_KISS()); - percent = anim.reverseIndices ? 1.0F - percent : percent; + auto& anim = _animations.at(_particleData.animIndex[i]); + float percent = abs(RANDOM_KISS()); + percent = anim.reverseIndices ? 1.0F - percent : percent; - _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), - anim.animationIndices.size() - 1)]; + _particleData.animCellIndex[i] = anim.animationIndices[MIN( + percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; _particleData.animTimeDelta[i] = 0; } } @@ -1179,6 +1181,7 @@ void ParticleSystem::update(float dt) float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; percent = (!!_isAnimationReversed != !!anim.reverseIndices) ? 1.0F - percent : percent; + percent = MAX(0.0F, percent); _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; @@ -1187,12 +1190,13 @@ void ParticleSystem::update(float dt) { auto& anim = _animations.at(_particleData.animIndex[i]); - _particleData.animTimeDelta[i] += dt; + _particleData.animTimeDelta[i] += (_animationTimescaleInd ? pureDt : dt); if (_particleData.animTimeDelta[i] >= _particleData.animTimeLength[i]) _particleData.animTimeDelta[i] = 0; float percent = _particleData.animTimeDelta[i] / _particleData.animTimeLength[i]; - percent = anim.reverseIndices ? 1.0F - percent : percent; + percent = anim.reverseIndices ? 1.0F - percent : percent; + percent = MAX(0.0F, percent); _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index ad67e233aa..a89062aa90 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -914,6 +914,12 @@ public: */ bool addAnimationIndex(unsigned short index, cocos2d::Rect rect, bool rotated = false); + /** You can specify what rect is used if an index in an animation descriptor wasn't found. + * + * @param rect Rect containting data about tex coords in pixels + */ + void setRectForUndefinedIndices(cocos2d::Rect rect) { _undefinedIndexRect = rect; }; + /** Add a particle animation descriptor with an index. * * @param indexOfDescriptor Index of the animation to be added, adding to the same index will just override the pervious animation descriptor @@ -938,6 +944,13 @@ public: setAnimationDescriptor(0, 0, 0, indices, reverse); }; + /** Sets wether the animation descriptors should follow the time scale of the system or not. + * + * @param independent Should the animation descriptor speeds be played independently? (default: false) + */ + void setAnimationSpeedTimescaleIndependent(bool independent) { _animationTimescaleInd = independent; }; + bool isAnimationSpeedTimescaleIndependent() { return _animationTimescaleInd; }; + /** Gets the particles movement type: Free or Grouped. @since v0.8 * @@ -1253,6 +1266,10 @@ protected: std::unordered_map _animations; /** A vector that stores ids of animation descriptors that are choosen at random */ std::vector _randomAnimations; + /** Wether the animation goes with the time scale of the system or is independent. */ + bool _animationTimescaleInd; + /** A rect that is used instead when an index is not found */ + cocos2d::Rect _undefinedIndexRect; /** does FlippedY variance of each particle */ int _yCoordFlipped; diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index 658ef5bb35..f0bb09df59 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -414,37 +414,6 @@ void ParticleSystemQuad::updateParticleQuads() } } - auto setTexCoords = [this](V3F_C4B_T2F_Quad* quad, unsigned short* cellIndex) { - - float left = 0.0F, bottom = 0.0F, top = 1.0F, right = 1.0F; - - // TODO: index.isRotated should be treated accordingly - - auto& index = _animationIndices.at(*cellIndex); - - auto texWidth = _texture->getPixelsWide(); - auto texHeight = _texture->getPixelsHigh(); - - left = index.rect.origin.x / texWidth; - right = (index.rect.origin.x + index.rect.size.x) / texWidth; - - top = index.rect.origin.y / texHeight; - bottom = (index.rect.origin.y + index.rect.size.y) / texHeight; - - quad->bl.texCoords.u = left; - quad->bl.texCoords.v = bottom; - - quad->br.texCoords.u = right; - quad->br.texCoords.v = bottom; - - quad->tl.texCoords.u = left; - quad->tl.texCoords.v = top; - - quad->tr.texCoords.u = right; - quad->tr.texCoords.v = top; - - }; - // set color if (_opacityModifyRGB) { @@ -487,6 +456,40 @@ void ParticleSystemQuad::updateParticleQuads() } } + auto setTexCoords = [this](V3F_C4B_T2F_Quad* quad, unsigned short* cellIndex) { + float left = 0.0F, bottom = 0.0F, top = 1.0F, right = 1.0F; + + // TODO: index.isRotated should be treated accordingly + + ParticleFrameDescriptor index; + auto iter = _animationIndices.find(*cellIndex); + if (iter == _animationIndices.end()) + index.rect = _undefinedIndexRect; + else + index = iter->second; + + auto texWidth = _texture->getPixelsWide(); + auto texHeight = _texture->getPixelsHigh(); + + left = index.rect.origin.x / texWidth; + right = (index.rect.origin.x + index.rect.size.x) / texWidth; + + top = index.rect.origin.y / texHeight; + bottom = (index.rect.origin.y + index.rect.size.y) / texHeight; + + quad->bl.texCoords.u = left; + quad->bl.texCoords.v = bottom; + + quad->br.texCoords.u = right; + quad->br.texCoords.v = bottom; + + quad->tl.texCoords.u = left; + quad->tl.texCoords.v = top; + + quad->tr.texCoords.u = right; + quad->tr.texCoords.v = top; + }; + // The reason for using for-loops separately for every property is because // When the processor needs to read from or write to a location in memory, // it first checks whether a copy of that data is in the cpu's cache. From c3ca96bcbc53fc58d2f75f75fa3fde9aa8ffc0f4 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Wed, 25 May 2022 10:20:35 +0300 Subject: [PATCH 25/61] Revert stashed changes temporarily. --- core/2d/CCParticleSystem.cpp | 45 +++++++++-- core/2d/CCParticleSystem.h | 133 ++++++++++++++++++++++++++++--- core/2d/CCParticleSystemQuad.cpp | 109 ++++++++++++++++++------- 3 files changed, 239 insertions(+), 48 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index a6202c60ac..f190e6f89c 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -138,6 +138,9 @@ bool ParticleData::init(int count) deltaColorG = (float*)malloc(count * sizeof(float)); deltaColorB = (float*)malloc(count * sizeof(float)); deltaColorA = (float*)malloc(count * sizeof(float)); + hueValue = (float*)malloc(count * sizeof(float)); + saturationValue = (float*)malloc(count * sizeof(float)); + luminanceValue = (float*)malloc(count * sizeof(float)); size = (float*)malloc(count * sizeof(float)); deltaSize = (float*)malloc(count * sizeof(float)); rotation = (float*)malloc(count * sizeof(float)); @@ -162,10 +165,11 @@ bool ParticleData::init(int count) modeB.radius = (float*)malloc(count * sizeof(float)); return posx && posy && startPosX && startPosY && colorR && colorG && colorB && colorA && deltaColorR && - deltaColorG && deltaColorB && deltaColorA && size && deltaSize && rotation && staticRotation && - deltaRotation && totalTimeToLive && timeToLive && animTimeLength && animTimeDelta && animIndex && - animCellIndex && atlasIndex && modeA.dirX && modeA.dirY && modeA.radialAccel && modeA.tangentialAccel && - modeB.angle && modeB.degreesPerSecond && modeB.deltaRadius && modeB.radius; + deltaColorG && deltaColorB && deltaColorA && size && hueValue && saturationValue && luminanceValue && + deltaSize && rotation && staticRotation && deltaRotation && totalTimeToLive && timeToLive && + animTimeLength && animTimeDelta && animIndex && animCellIndex && atlasIndex && modeA.dirX && modeA.dirY && + modeA.radialAccel && modeA.tangentialAccel && modeB.angle && modeB.degreesPerSecond && modeB.deltaRadius && + modeB.radius; } void ParticleData::release() @@ -182,6 +186,9 @@ void ParticleData::release() CC_SAFE_FREE(deltaColorG); CC_SAFE_FREE(deltaColorB); CC_SAFE_FREE(deltaColorA); + CC_SAFE_FREE(hueValue); + CC_SAFE_FREE(saturationValue); + CC_SAFE_FREE(luminanceValue); CC_SAFE_FREE(size); CC_SAFE_FREE(deltaSize); CC_SAFE_FREE(rotation); @@ -228,6 +235,13 @@ ParticleSystem::ParticleSystem() , _angle(0) , _angleVar(0) , _emitterMode(Mode::GRAVITY) + , _isHsv(false) + , _hueValue(0) + , _hueValueVar(0) + , _saturationValue(1) + , _saturationValueVar(0) + , _luminanceValue(1) + , _luminanceValueVar(0) , _startSize(0) , _startSizeVar(0) , _endSize(0) @@ -249,6 +263,7 @@ ParticleSystem::ParticleSystem() , _animIndexCount(0) , _isAnimationReversed(false) , _undefinedIndexRect({0,0,0,0}) + , _animationTimescaleInd(false) , _yCoordFlipped(1) , _positionType(PositionType::FREE) , _paused(false) @@ -742,6 +757,24 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati SET_DELTA_COLOR(_particleData.colorB, _particleData.deltaColorB); SET_DELTA_COLOR(_particleData.colorA, _particleData.deltaColorA); + // hue saturation luminance color values + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.hueValue[i] = _hueValue + _hueValueVar * RANDOM_KISS(); + } + + for (int i = start; i < _particleCount; ++i) + { + _particleData.saturationValue[i] = _saturationValue + _saturationValueVar * RANDOM_KISS(); + } + + for (int i = start; i < _particleCount; ++i) + { + _particleData.luminanceValue[i] = _luminanceValue + _luminanceValueVar * RANDOM_KISS(); + } + } + // size for (int i = start; i < _particleCount; ++i) { @@ -1101,9 +1134,9 @@ void ParticleSystem::update(float dt) _componentContainer->visit(dt); } - if (dt > 0.5F) + if (dt > 0.3F) { - this->simulate(dt, 15); + this->simulate(dt, 10); return; } diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index a89062aa90..4bfdf93264 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -92,6 +92,10 @@ public: float* deltaColorB; float* deltaColorA; + float* hueValue; + float* saturationValue; + float* luminanceValue; + float* size; float* deltaSize; float* rotation; @@ -131,10 +135,10 @@ public: void copyParticle(int p1, int p2) { - posx[p1] = posx[p2]; - posy[p1] = posy[p2]; - startPosX[p1] = startPosX[p2]; - startPosY[p1] = startPosY[p2]; + posx[p1] = posx[p2]; + posy[p1] = posy[p2]; + startPosX[p1] = startPosX[p2]; + startPosY[p1] = startPosY[p2]; colorR[p1] = colorR[p2]; colorG[p1] = colorG[p2]; @@ -146,17 +150,24 @@ public: deltaColorB[p1] = deltaColorB[p2]; deltaColorA[p1] = deltaColorA[p2]; - size[p1] = size[p2]; - deltaSize[p1] = deltaSize[p2]; - - rotation[p1] = rotation[p2]; - deltaRotation[p1] = deltaRotation[p2]; + hueValue[p1] = hueValue[p2]; + saturationValue[p1] = saturationValue[p2]; + luminanceValue[p1] = luminanceValue[p2]; + size[p1] = size[p2]; + deltaSize[p1] = deltaSize[p2]; + rotation[p1] = rotation[p2]; + staticRotation[p1] = staticRotation[p2]; + deltaRotation[p1] = deltaRotation[p2]; + totalTimeToLive[p1] = totalTimeToLive[p2]; - timeToLive[p1] = timeToLive[p2]; + timeToLive[p1] = timeToLive[p2]; + animTimeDelta[p1] = animTimeDelta[p2]; + animTimeLength[p1] = animTimeLength[p2]; + animIndex[p1] = animIndex[p2]; animCellIndex[p1] = animCellIndex[p2]; - atlasIndex[p1] = atlasIndex[p2]; + atlasIndex[p1] = atlasIndex[p2]; modeA.dirX[p1] = modeA.dirX[p2]; modeA.dirY[p1] = modeA.dirY[p2]; @@ -705,6 +716,92 @@ public: */ void setEndColorVar(const Color4F& color) { _endColorVar = color; } + /** Sets wether to use HSV color system. + * WARNING: careful when using HSV with too many particles because it's expensive. + * + * @param hsv Use hsv color system. + */ + void useHSV(bool hsv) { _isHsv = hsv; }; + bool isHSV() { return _isHsv; }; + + /** Gets the hue value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @return The hue value of each particle in degress (i.e. 360). + */ + float getHue() const { return _hueValue; } + /** Sets the hue value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @param degrees The hue value of each particle in degress (i.e. 360). + */ + void setHue(float degrees) { _hueValue = degrees; } + + /** Gets the hue variance value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @return The hue variance value of each particle in degress (i.e. 360). + */ + float getHueVar() const { return _hueValueVar; } + /** Sets the hue variance value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @param degrees The hue variance value of each particle in degress (i.e. 360). + */ + void setHueVar(float degrees) { _hueValueVar = degrees; } + + /** Gets the saturation value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @return The saturation value of each particle. + */ + float getSaturation() const { return _saturationValue; } + /** Sets the saturation value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @param value The saturation value of each particle. + */ + void setSaturation(float value) { _saturationValue = value; } + + /** Gets the saturation variance value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @return The saturation variance value of each particle. + */ + float getSaturationVar() const { return _saturationValueVar; } + /** Sets the saturation variance value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @param value The saturation variance value of each particle. + */ + void setSaturationVar(float value) { _saturationValueVar = value; } + + /** Gets the luminance value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @return The luminance value of each particle. + */ + float getLuminance() const { return _luminanceValue; } + /** Sets the luminance value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @param value The luminance value of each particle. + */ + void setLuminance(float value) { _luminanceValue = value; } + + /** Gets the luminance variance value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @return The luminance variance value of each particle. + */ + float getLuminanceVar() const { return _luminanceValueVar; } + /** Sets the luminance variance value of each particle. + * NOTE: hsv has to be enabled using useHSV(true) for this function to work. + * + * @param value The luminance variance value of each particle. + */ + void setLuminanceVar(float value) { _luminanceValueVar = value; } + /** Gets the start spin of each particle. * * @return The start spin of each particle. @@ -1228,6 +1325,20 @@ protected: Color4F _endColor; /** end color variance of each particle */ Color4F _endColorVar; + //* Is the hsv system used or not. + bool _isHsv; + //* Hue value of each particle + float _hueValue; + //* Hue value variance of each particle + float _hueValueVar; + //* Saturation value of each particle + float _saturationValue; + //* Saturation value variance of each particle + float _saturationValueVar; + //* Luminance value of each particle + float _luminanceValue; + //* Luminance value variance of each particle + float _luminanceValueVar; //* initial angle of each particle float _startSpin; //* initial angle of each particle diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index f0bb09df59..c8b12732a6 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -415,44 +415,91 @@ void ParticleSystemQuad::updateParticleQuads() } // set color - if (_opacityModifyRGB) - { - V3F_C4B_T2F_Quad* quad = startQuad; - float* r = _particleData.colorR; - float* g = _particleData.colorG; - float* b = _particleData.colorB; - float* a = _particleData.colorA; + V3F_C4B_T2F_Quad* quad = startQuad; + float* r = _particleData.colorR; + float* g = _particleData.colorG; + float* b = _particleData.colorB; + float* a = _particleData.colorA; - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + // HSV calculation is expensive, so we should skip it if it's not enabled. + if (_isHsv) + { + float* hue = _particleData.hueValue; + float* sat = _particleData.saturationValue; + float* lum = _particleData.luminanceValue; + + if (_opacityModifyRGB) { - uint8_t colorR = *r * *a * 255; - uint8_t colorG = *g * *a * 255; - uint8_t colorB = *b * *a * 255; - uint8_t colorA = *a * 255; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); + auto hsv = HSV(); + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++lum) + { + float colorR = *r * *a; + float colorG = *g * *a; + float colorB = *b * *a; + float colorA = *a; + hsv.rgb(colorR, colorG, colorB, colorA); + hsv.h += *hue; + hsv.h = abs(fmod(hsv.h, 360.0F)); + hsv.s = abs(*sat); + hsv.v = abs(*lum); + auto col = Color4B(hsv); + quad->bl.colors.set(col.r, col.g, col.b, col.a); + quad->br.colors.set(col.r, col.g, col.b, col.a); + quad->tl.colors.set(col.r, col.g, col.b, col.a); + quad->tr.colors.set(col.r, col.g, col.b, col.a); + } + } + else + { + auto hsv = HSV(); + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++lum) + { + float colorR = *r; + float colorG = *g; + float colorB = *b; + float colorA = *a; + hsv.rgb(colorR, colorG, colorB, colorA); + hsv.h += *hue; + hsv.h = abs(fmod(hsv.h, 360.0F)); + hsv.s = abs(*sat); + hsv.v = abs(*lum); + auto col = Color4B(hsv); + quad->bl.colors.set(col.r, col.g, col.b, col.a); + quad->br.colors.set(col.r, col.g, col.b, col.a); + quad->tl.colors.set(col.r, col.g, col.b, col.a); + quad->tr.colors.set(col.r, col.g, col.b, col.a); + } } } else { - V3F_C4B_T2F_Quad* quad = startQuad; - float* r = _particleData.colorR; - float* g = _particleData.colorG; - float* b = _particleData.colorB; - float* a = _particleData.colorA; - - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + if (_opacityModifyRGB) { - uint8_t colorR = *r * 255; - uint8_t colorG = *g * 255; - uint8_t colorB = *b * 255; - uint8_t colorA = *a * 255; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + { + uint8_t colorR = *r * *a * 255.0F; + uint8_t colorG = *g * *a * 255.0F; + uint8_t colorB = *b * *a * 255.0F; + uint8_t colorA = *a * 255.0F; + quad->bl.colors.set(colorR, colorG, colorB, colorA); + quad->br.colors.set(colorR, colorG, colorB, colorA); + quad->tl.colors.set(colorR, colorG, colorB, colorA); + quad->tr.colors.set(colorR, colorG, colorB, colorA); + } + } + else + { + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + { + uint8_t colorR = *r * 255.0F; + uint8_t colorG = *g * 255.0F; + uint8_t colorB = *b * 255.0F; + uint8_t colorA = *a * 255.0F; + quad->bl.colors.set(colorR, colorG, colorB, colorA); + quad->br.colors.set(colorR, colorG, colorB, colorA); + quad->tl.colors.set(colorR, colorG, colorB, colorA); + quad->tr.colors.set(colorR, colorG, colorB, colorA); + } } } From f0d650c6ef3e43a1a2f31c08ae62f0d9a108bf0d Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Wed, 25 May 2022 10:46:54 +0300 Subject: [PATCH 26/61] Revert "Revert stashed changes temporarily." This reverts commit c3ca96bcbc53fc58d2f75f75fa3fde9aa8ffc0f4. --- core/2d/CCParticleSystem.cpp | 45 ++--------- core/2d/CCParticleSystem.h | 133 +++---------------------------- core/2d/CCParticleSystemQuad.cpp | 107 +++++++------------------ 3 files changed, 47 insertions(+), 238 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index f190e6f89c..a6202c60ac 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -138,9 +138,6 @@ bool ParticleData::init(int count) deltaColorG = (float*)malloc(count * sizeof(float)); deltaColorB = (float*)malloc(count * sizeof(float)); deltaColorA = (float*)malloc(count * sizeof(float)); - hueValue = (float*)malloc(count * sizeof(float)); - saturationValue = (float*)malloc(count * sizeof(float)); - luminanceValue = (float*)malloc(count * sizeof(float)); size = (float*)malloc(count * sizeof(float)); deltaSize = (float*)malloc(count * sizeof(float)); rotation = (float*)malloc(count * sizeof(float)); @@ -165,11 +162,10 @@ bool ParticleData::init(int count) modeB.radius = (float*)malloc(count * sizeof(float)); return posx && posy && startPosX && startPosY && colorR && colorG && colorB && colorA && deltaColorR && - deltaColorG && deltaColorB && deltaColorA && size && hueValue && saturationValue && luminanceValue && - deltaSize && rotation && staticRotation && deltaRotation && totalTimeToLive && timeToLive && - animTimeLength && animTimeDelta && animIndex && animCellIndex && atlasIndex && modeA.dirX && modeA.dirY && - modeA.radialAccel && modeA.tangentialAccel && modeB.angle && modeB.degreesPerSecond && modeB.deltaRadius && - modeB.radius; + deltaColorG && deltaColorB && deltaColorA && size && deltaSize && rotation && staticRotation && + deltaRotation && totalTimeToLive && timeToLive && animTimeLength && animTimeDelta && animIndex && + animCellIndex && atlasIndex && modeA.dirX && modeA.dirY && modeA.radialAccel && modeA.tangentialAccel && + modeB.angle && modeB.degreesPerSecond && modeB.deltaRadius && modeB.radius; } void ParticleData::release() @@ -186,9 +182,6 @@ void ParticleData::release() CC_SAFE_FREE(deltaColorG); CC_SAFE_FREE(deltaColorB); CC_SAFE_FREE(deltaColorA); - CC_SAFE_FREE(hueValue); - CC_SAFE_FREE(saturationValue); - CC_SAFE_FREE(luminanceValue); CC_SAFE_FREE(size); CC_SAFE_FREE(deltaSize); CC_SAFE_FREE(rotation); @@ -235,13 +228,6 @@ ParticleSystem::ParticleSystem() , _angle(0) , _angleVar(0) , _emitterMode(Mode::GRAVITY) - , _isHsv(false) - , _hueValue(0) - , _hueValueVar(0) - , _saturationValue(1) - , _saturationValueVar(0) - , _luminanceValue(1) - , _luminanceValueVar(0) , _startSize(0) , _startSizeVar(0) , _endSize(0) @@ -263,7 +249,6 @@ ParticleSystem::ParticleSystem() , _animIndexCount(0) , _isAnimationReversed(false) , _undefinedIndexRect({0,0,0,0}) - , _animationTimescaleInd(false) , _yCoordFlipped(1) , _positionType(PositionType::FREE) , _paused(false) @@ -757,24 +742,6 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati SET_DELTA_COLOR(_particleData.colorB, _particleData.deltaColorB); SET_DELTA_COLOR(_particleData.colorA, _particleData.deltaColorA); - // hue saturation luminance color values - { - for (int i = start; i < _particleCount; ++i) - { - _particleData.hueValue[i] = _hueValue + _hueValueVar * RANDOM_KISS(); - } - - for (int i = start; i < _particleCount; ++i) - { - _particleData.saturationValue[i] = _saturationValue + _saturationValueVar * RANDOM_KISS(); - } - - for (int i = start; i < _particleCount; ++i) - { - _particleData.luminanceValue[i] = _luminanceValue + _luminanceValueVar * RANDOM_KISS(); - } - } - // size for (int i = start; i < _particleCount; ++i) { @@ -1134,9 +1101,9 @@ void ParticleSystem::update(float dt) _componentContainer->visit(dt); } - if (dt > 0.3F) + if (dt > 0.5F) { - this->simulate(dt, 10); + this->simulate(dt, 15); return; } diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 4bfdf93264..a89062aa90 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -92,10 +92,6 @@ public: float* deltaColorB; float* deltaColorA; - float* hueValue; - float* saturationValue; - float* luminanceValue; - float* size; float* deltaSize; float* rotation; @@ -135,10 +131,10 @@ public: void copyParticle(int p1, int p2) { - posx[p1] = posx[p2]; - posy[p1] = posy[p2]; - startPosX[p1] = startPosX[p2]; - startPosY[p1] = startPosY[p2]; + posx[p1] = posx[p2]; + posy[p1] = posy[p2]; + startPosX[p1] = startPosX[p2]; + startPosY[p1] = startPosY[p2]; colorR[p1] = colorR[p2]; colorG[p1] = colorG[p2]; @@ -150,24 +146,17 @@ public: deltaColorB[p1] = deltaColorB[p2]; deltaColorA[p1] = deltaColorA[p2]; - hueValue[p1] = hueValue[p2]; - saturationValue[p1] = saturationValue[p2]; - luminanceValue[p1] = luminanceValue[p2]; + size[p1] = size[p2]; + deltaSize[p1] = deltaSize[p2]; + + rotation[p1] = rotation[p2]; + deltaRotation[p1] = deltaRotation[p2]; - size[p1] = size[p2]; - deltaSize[p1] = deltaSize[p2]; - rotation[p1] = rotation[p2]; - staticRotation[p1] = staticRotation[p2]; - deltaRotation[p1] = deltaRotation[p2]; - totalTimeToLive[p1] = totalTimeToLive[p2]; - timeToLive[p1] = timeToLive[p2]; - animTimeDelta[p1] = animTimeDelta[p2]; - animTimeLength[p1] = animTimeLength[p2]; + timeToLive[p1] = timeToLive[p2]; - animIndex[p1] = animIndex[p2]; animCellIndex[p1] = animCellIndex[p2]; - atlasIndex[p1] = atlasIndex[p2]; + atlasIndex[p1] = atlasIndex[p2]; modeA.dirX[p1] = modeA.dirX[p2]; modeA.dirY[p1] = modeA.dirY[p2]; @@ -716,92 +705,6 @@ public: */ void setEndColorVar(const Color4F& color) { _endColorVar = color; } - /** Sets wether to use HSV color system. - * WARNING: careful when using HSV with too many particles because it's expensive. - * - * @param hsv Use hsv color system. - */ - void useHSV(bool hsv) { _isHsv = hsv; }; - bool isHSV() { return _isHsv; }; - - /** Gets the hue value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @return The hue value of each particle in degress (i.e. 360). - */ - float getHue() const { return _hueValue; } - /** Sets the hue value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @param degrees The hue value of each particle in degress (i.e. 360). - */ - void setHue(float degrees) { _hueValue = degrees; } - - /** Gets the hue variance value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @return The hue variance value of each particle in degress (i.e. 360). - */ - float getHueVar() const { return _hueValueVar; } - /** Sets the hue variance value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @param degrees The hue variance value of each particle in degress (i.e. 360). - */ - void setHueVar(float degrees) { _hueValueVar = degrees; } - - /** Gets the saturation value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @return The saturation value of each particle. - */ - float getSaturation() const { return _saturationValue; } - /** Sets the saturation value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @param value The saturation value of each particle. - */ - void setSaturation(float value) { _saturationValue = value; } - - /** Gets the saturation variance value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @return The saturation variance value of each particle. - */ - float getSaturationVar() const { return _saturationValueVar; } - /** Sets the saturation variance value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @param value The saturation variance value of each particle. - */ - void setSaturationVar(float value) { _saturationValueVar = value; } - - /** Gets the luminance value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @return The luminance value of each particle. - */ - float getLuminance() const { return _luminanceValue; } - /** Sets the luminance value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @param value The luminance value of each particle. - */ - void setLuminance(float value) { _luminanceValue = value; } - - /** Gets the luminance variance value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @return The luminance variance value of each particle. - */ - float getLuminanceVar() const { return _luminanceValueVar; } - /** Sets the luminance variance value of each particle. - * NOTE: hsv has to be enabled using useHSV(true) for this function to work. - * - * @param value The luminance variance value of each particle. - */ - void setLuminanceVar(float value) { _luminanceValueVar = value; } - /** Gets the start spin of each particle. * * @return The start spin of each particle. @@ -1325,20 +1228,6 @@ protected: Color4F _endColor; /** end color variance of each particle */ Color4F _endColorVar; - //* Is the hsv system used or not. - bool _isHsv; - //* Hue value of each particle - float _hueValue; - //* Hue value variance of each particle - float _hueValueVar; - //* Saturation value of each particle - float _saturationValue; - //* Saturation value variance of each particle - float _saturationValueVar; - //* Luminance value of each particle - float _luminanceValue; - //* Luminance value variance of each particle - float _luminanceValueVar; //* initial angle of each particle float _startSpin; //* initial angle of each particle diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index c8b12732a6..f0bb09df59 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -415,91 +415,44 @@ void ParticleSystemQuad::updateParticleQuads() } // set color - V3F_C4B_T2F_Quad* quad = startQuad; - float* r = _particleData.colorR; - float* g = _particleData.colorG; - float* b = _particleData.colorB; - float* a = _particleData.colorA; - - // HSV calculation is expensive, so we should skip it if it's not enabled. - if (_isHsv) + if (_opacityModifyRGB) { - float* hue = _particleData.hueValue; - float* sat = _particleData.saturationValue; - float* lum = _particleData.luminanceValue; + V3F_C4B_T2F_Quad* quad = startQuad; + float* r = _particleData.colorR; + float* g = _particleData.colorG; + float* b = _particleData.colorB; + float* a = _particleData.colorA; - if (_opacityModifyRGB) + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) { - auto hsv = HSV(); - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++lum) - { - float colorR = *r * *a; - float colorG = *g * *a; - float colorB = *b * *a; - float colorA = *a; - hsv.rgb(colorR, colorG, colorB, colorA); - hsv.h += *hue; - hsv.h = abs(fmod(hsv.h, 360.0F)); - hsv.s = abs(*sat); - hsv.v = abs(*lum); - auto col = Color4B(hsv); - quad->bl.colors.set(col.r, col.g, col.b, col.a); - quad->br.colors.set(col.r, col.g, col.b, col.a); - quad->tl.colors.set(col.r, col.g, col.b, col.a); - quad->tr.colors.set(col.r, col.g, col.b, col.a); - } - } - else - { - auto hsv = HSV(); - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++lum) - { - float colorR = *r; - float colorG = *g; - float colorB = *b; - float colorA = *a; - hsv.rgb(colorR, colorG, colorB, colorA); - hsv.h += *hue; - hsv.h = abs(fmod(hsv.h, 360.0F)); - hsv.s = abs(*sat); - hsv.v = abs(*lum); - auto col = Color4B(hsv); - quad->bl.colors.set(col.r, col.g, col.b, col.a); - quad->br.colors.set(col.r, col.g, col.b, col.a); - quad->tl.colors.set(col.r, col.g, col.b, col.a); - quad->tr.colors.set(col.r, col.g, col.b, col.a); - } + uint8_t colorR = *r * *a * 255; + uint8_t colorG = *g * *a * 255; + uint8_t colorB = *b * *a * 255; + uint8_t colorA = *a * 255; + quad->bl.colors.set(colorR, colorG, colorB, colorA); + quad->br.colors.set(colorR, colorG, colorB, colorA); + quad->tl.colors.set(colorR, colorG, colorB, colorA); + quad->tr.colors.set(colorR, colorG, colorB, colorA); } } else { - if (_opacityModifyRGB) + V3F_C4B_T2F_Quad* quad = startQuad; + float* r = _particleData.colorR; + float* g = _particleData.colorG; + float* b = _particleData.colorB; + float* a = _particleData.colorA; + + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) { - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) - { - uint8_t colorR = *r * *a * 255.0F; - uint8_t colorG = *g * *a * 255.0F; - uint8_t colorB = *b * *a * 255.0F; - uint8_t colorA = *a * 255.0F; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); - } - } - else - { - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) - { - uint8_t colorR = *r * 255.0F; - uint8_t colorG = *g * 255.0F; - uint8_t colorB = *b * 255.0F; - uint8_t colorA = *a * 255.0F; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); - } + uint8_t colorR = *r * 255; + uint8_t colorG = *g * 255; + uint8_t colorB = *b * 255; + uint8_t colorA = *a * 255; + quad->bl.colors.set(colorR, colorG, colorB, colorA); + quad->br.colors.set(colorR, colorG, colorB, colorA); + quad->tl.colors.set(colorR, colorG, colorB, colorA); + quad->tr.colors.set(colorR, colorG, colorB, colorA); } } From 7599aa29b14416a6ab380133ae46e8af8b668f0e Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 26 May 2022 14:00:33 +0300 Subject: [PATCH 27/61] Add HSV support and remove frame compensation. --- core/2d/CCParticleSystem.cpp | 41 +++++-- core/2d/CCParticleSystem.h | 81 ++++++++++++-- core/2d/CCParticleSystemQuad.cpp | 177 +++++++++++++++++++------------ 3 files changed, 216 insertions(+), 83 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index a6202c60ac..c8a8435c74 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -138,6 +138,9 @@ bool ParticleData::init(int count) deltaColorG = (float*)malloc(count * sizeof(float)); deltaColorB = (float*)malloc(count * sizeof(float)); deltaColorA = (float*)malloc(count * sizeof(float)); + hue = (float*)malloc(count * sizeof(float)); + sat = (float*)malloc(count * sizeof(float)); + val = (float*)malloc(count * sizeof(float)); size = (float*)malloc(count * sizeof(float)); deltaSize = (float*)malloc(count * sizeof(float)); rotation = (float*)malloc(count * sizeof(float)); @@ -162,10 +165,10 @@ bool ParticleData::init(int count) modeB.radius = (float*)malloc(count * sizeof(float)); return posx && posy && startPosX && startPosY && colorR && colorG && colorB && colorA && deltaColorR && - deltaColorG && deltaColorB && deltaColorA && size && deltaSize && rotation && staticRotation && - deltaRotation && totalTimeToLive && timeToLive && animTimeLength && animTimeDelta && animIndex && - animCellIndex && atlasIndex && modeA.dirX && modeA.dirY && modeA.radialAccel && modeA.tangentialAccel && - modeB.angle && modeB.degreesPerSecond && modeB.deltaRadius && modeB.radius; + deltaColorG && deltaColorB && deltaColorA && hue && sat && val && size && deltaSize && rotation && + staticRotation && deltaRotation && totalTimeToLive && timeToLive && animTimeLength && animTimeDelta && + animIndex && animCellIndex && atlasIndex && modeA.dirX && modeA.dirY && modeA.radialAccel && + modeA.tangentialAccel && modeB.angle && modeB.degreesPerSecond && modeB.deltaRadius && modeB.radius; } void ParticleData::release() @@ -182,6 +185,9 @@ void ParticleData::release() CC_SAFE_FREE(deltaColorG); CC_SAFE_FREE(deltaColorB); CC_SAFE_FREE(deltaColorA); + CC_SAFE_FREE(hue); + CC_SAFE_FREE(sat); + CC_SAFE_FREE(val); CC_SAFE_FREE(size); CC_SAFE_FREE(deltaSize); CC_SAFE_FREE(rotation); @@ -238,6 +244,8 @@ ParticleSystem::ParticleSystem() , _endSpinVar(0) , _spawnAngle(0) , _spawnAngleVar(0) + , _hsv(0, 1, 1) + , _hsvVar(0, 0, 0) , _emissionRate(0) , _totalParticles(0) , _texture(nullptr) @@ -249,6 +257,7 @@ ParticleSystem::ParticleSystem() , _animIndexCount(0) , _isAnimationReversed(false) , _undefinedIndexRect({0,0,0,0}) + , _animationTimescaleInd(false) , _yCoordFlipped(1) , _positionType(PositionType::FREE) , _paused(false) @@ -742,6 +751,24 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati SET_DELTA_COLOR(_particleData.colorB, _particleData.deltaColorB); SET_DELTA_COLOR(_particleData.colorA, _particleData.deltaColorA); + // hue saturation value color + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.hue[i] = _hsv.h + _hsvVar.h * RANDOM_KISS(); + } + + for (int i = start; i < _particleCount; ++i) + { + _particleData.sat[i] = _hsv.s + _hsvVar.s * RANDOM_KISS(); + } + + for (int i = start; i < _particleCount; ++i) + { + _particleData.val[i] = _hsv.v + _hsvVar.v * RANDOM_KISS(); + } + } + // size for (int i = start; i < _particleCount; ++i) { @@ -1101,12 +1128,6 @@ void ParticleSystem::update(float dt) _componentContainer->visit(dt); } - if (dt > 0.5F) - { - this->simulate(dt, 15); - return; - } - if (_fixedFPS != 0) { _fixedFPSDelta += dt; diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index a89062aa90..040db16a75 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -92,6 +92,10 @@ public: float* deltaColorB; float* deltaColorA; + float* hue; + float* sat; + float* val; + float* size; float* deltaSize; float* rotation; @@ -146,17 +150,24 @@ public: deltaColorB[p1] = deltaColorB[p2]; deltaColorA[p1] = deltaColorA[p2]; - size[p1] = size[p2]; - deltaSize[p1] = deltaSize[p2]; + hue[p1] = hue[p2]; + sat[p1] = sat[p2]; + val[p1] = val[p2]; - rotation[p1] = rotation[p2]; - deltaRotation[p1] = deltaRotation[p2]; + size[p1] = size[p2]; + deltaSize[p1] = deltaSize[p2]; + rotation[p1] = rotation[p2]; + staticRotation[p1] = staticRotation[p2]; + deltaRotation[p1] = deltaRotation[p2]; totalTimeToLive[p1] = totalTimeToLive[p2]; - timeToLive[p1] = timeToLive[p2]; + timeToLive[p1] = timeToLive[p2]; + animTimeDelta[p1] = animTimeDelta[p2]; + animTimeLength[p1] = animTimeLength[p2]; + animIndex[p1] = animIndex[p2]; animCellIndex[p1] = animCellIndex[p2]; - atlasIndex[p1] = atlasIndex[p2]; + atlasIndex[p1] = atlasIndex[p2]; modeA.dirX[p1] = modeA.dirX[p2]; modeA.dirY[p1] = modeA.dirY[p2]; @@ -705,6 +716,58 @@ public: */ void setEndColorVar(const Color4F& color) { _endColorVar = color; } + /** Sets wether to use HSV color system. + * WARNING: becareful when using HSV with too many particles because it's expensive. + * + * @param hsv Use HSV color system. + */ + void useHSV(bool hsv) { _isHsv = hsv; }; + bool isHSV() { return _isHsv; }; + + /** Gets the hue of each particle. + * + * @return The hue of each particle. + */ + float getHue() const { return _hsv.h; } + /** Sets the hue of each particle. + * + * @param hsv The hue color of each particle. + */ + void setHue(float hue) { _hsv.h = hue; } + + /** Gets the hue variance of each particle. + * + * @return The hue variance of each particle. + */ + float getHueVar() const { return _hsvVar.h; } + /** Sets the hue variance of each particle. + * + * @param hsv The hue variance color of each particle. + */ + void setHueVar(float hue) { _hsvVar.h = hue; } + + /** Gets the HSV color of each particle. + * + * @return The HSV color of each particle. + */ + const HSV& getHSV() const { return _hsv; } + /** Sets the HSV color of each particle. + * + * @param hsv The HSV color of each particle. + */ + void setHSV(const HSV& hsv) { _hsv = hsv; } + + /** Gets the HSV color variance of each particle. + * + * @return The HSV color variance of each particle. + */ + const HSV& getHSVVar() const { return _hsvVar; } + /** Sets the HSV color variance of each particle. + * + * @param hsv The HSV color variance of each particle. + */ + void setHSVVar(const HSV& hsv) { _hsvVar = hsv; } + /** Gets the start spin of each particle. * * @return The start spin of each particle. @@ -1228,6 +1291,12 @@ protected: Color4F _endColor; /** end color variance of each particle */ Color4F _endColorVar; + //* Is the hsv system used or not. + bool _isHsv; + /** hsv color of each particle */ + HSV _hsv; + /** hsv color variance of each particle */ + HSV _hsvVar; //* initial angle of each particle float _startSpin; //* initial angle of each particle diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index f0bb09df59..365fe0c847 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -414,82 +414,93 @@ void ParticleSystemQuad::updateParticleQuads() } } - // set color - if (_opacityModifyRGB) - { - V3F_C4B_T2F_Quad* quad = startQuad; - float* r = _particleData.colorR; - float* g = _particleData.colorG; - float* b = _particleData.colorB; - float* a = _particleData.colorA; + V3F_C4B_T2F_Quad* quad = startQuad; + float* r = _particleData.colorR; + float* g = _particleData.colorG; + float* b = _particleData.colorB; + float* a = _particleData.colorA; - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + // HSV calculation is expensive, so we should skip it if it's not enabled. + if (_isHsv) + { + float* hue = _particleData.hue; + float* sat = _particleData.sat; + float* val = _particleData.val; + + if (_opacityModifyRGB) { - uint8_t colorR = *r * *a * 255; - uint8_t colorG = *g * *a * 255; - uint8_t colorB = *b * *a * 255; - uint8_t colorA = *a * 255; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); + auto hsv = HSV(); + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) + { + float colorR = *r * *a; + float colorG = *g * *a; + float colorB = *b * *a; + float colorA = *a; + hsv.set(colorR, colorG, colorB, colorA); + hsv.h += *hue; + hsv.s = abs(*sat); + hsv.v = abs(*val); + auto col = hsv.toColor4B(); + quad->bl.colors.set(col.r, col.g, col.b, col.a); + quad->br.colors.set(col.r, col.g, col.b, col.a); + quad->tl.colors.set(col.r, col.g, col.b, col.a); + quad->tr.colors.set(col.r, col.g, col.b, col.a); + } + } + else + { + auto hsv = HSV(); + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) + { + float colorR = *r; + float colorG = *g; + float colorB = *b; + float colorA = *a; + hsv.set(colorR, colorG, colorB, colorA); + hsv.h += *hue; + hsv.s = abs(*sat); + hsv.v = abs(*val); + auto col = hsv.toColor4B(); + quad->bl.colors.set(col.r, col.g, col.b, col.a); + quad->br.colors.set(col.r, col.g, col.b, col.a); + quad->tl.colors.set(col.r, col.g, col.b, col.a); + quad->tr.colors.set(col.r, col.g, col.b, col.a); + } } } else { - V3F_C4B_T2F_Quad* quad = startQuad; - float* r = _particleData.colorR; - float* g = _particleData.colorG; - float* b = _particleData.colorB; - float* a = _particleData.colorA; - - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + // set color + if (_opacityModifyRGB) { - uint8_t colorR = *r * 255; - uint8_t colorG = *g * 255; - uint8_t colorB = *b * 255; - uint8_t colorA = *a * 255; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + { + uint8_t colorR = *r * *a * 255; + uint8_t colorG = *g * *a * 255; + uint8_t colorB = *b * *a * 255; + uint8_t colorA = *a * 255; + quad->bl.colors.set(colorR, colorG, colorB, colorA); + quad->br.colors.set(colorR, colorG, colorB, colorA); + quad->tl.colors.set(colorR, colorG, colorB, colorA); + quad->tr.colors.set(colorR, colorG, colorB, colorA); + } + } + else + { + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + { + uint8_t colorR = *r * 255; + uint8_t colorG = *g * 255; + uint8_t colorB = *b * 255; + uint8_t colorA = *a * 255; + quad->bl.colors.set(colorR, colorG, colorB, colorA); + quad->br.colors.set(colorR, colorG, colorB, colorA); + quad->tl.colors.set(colorR, colorG, colorB, colorA); + quad->tr.colors.set(colorR, colorG, colorB, colorA); + } } } - auto setTexCoords = [this](V3F_C4B_T2F_Quad* quad, unsigned short* cellIndex) { - float left = 0.0F, bottom = 0.0F, top = 1.0F, right = 1.0F; - - // TODO: index.isRotated should be treated accordingly - - ParticleFrameDescriptor index; - auto iter = _animationIndices.find(*cellIndex); - if (iter == _animationIndices.end()) - index.rect = _undefinedIndexRect; - else - index = iter->second; - - auto texWidth = _texture->getPixelsWide(); - auto texHeight = _texture->getPixelsHigh(); - - left = index.rect.origin.x / texWidth; - right = (index.rect.origin.x + index.rect.size.x) / texWidth; - - top = index.rect.origin.y / texHeight; - bottom = (index.rect.origin.y + index.rect.size.y) / texHeight; - - quad->bl.texCoords.u = left; - quad->bl.texCoords.v = bottom; - - quad->br.texCoords.u = right; - quad->br.texCoords.v = bottom; - - quad->tl.texCoords.u = left; - quad->tl.texCoords.v = top; - - quad->tr.texCoords.u = right; - quad->tr.texCoords.v = top; - }; - // The reason for using for-loops separately for every property is because // When the processor needs to read from or write to a location in memory, // it first checks whether a copy of that data is in the cpu's cache. @@ -501,8 +512,40 @@ void ParticleSystemQuad::updateParticleQuads() V3F_C4B_T2F_Quad* quad = startQuad; unsigned short* cellIndex = _particleData.animCellIndex; + ParticleFrameDescriptor index; for (int i = 0; i < _particleCount; ++i, ++quad, ++cellIndex) - setTexCoords(quad, cellIndex); + { + float left = 0.0F, bottom = 0.0F, top = 1.0F, right = 1.0F; + + // TODO: index.isRotated should be treated accordingly + + auto iter = _animationIndices.find(*cellIndex); + if (iter == _animationIndices.end()) + index.rect = _undefinedIndexRect; + else + index = iter->second; + + auto texWidth = _texture->getPixelsWide(); + auto texHeight = _texture->getPixelsHigh(); + + left = index.rect.origin.x / texWidth; + right = (index.rect.origin.x + index.rect.size.x) / texWidth; + + top = index.rect.origin.y / texHeight; + bottom = (index.rect.origin.y + index.rect.size.y) / texHeight; + + quad->bl.texCoords.u = left; + quad->bl.texCoords.v = bottom; + + quad->br.texCoords.u = right; + quad->br.texCoords.v = bottom; + + quad->tl.texCoords.u = left; + quad->tl.texCoords.v = top; + + quad->tr.texCoords.u = right; + quad->tr.texCoords.v = top; + } } } From 67098c979c7e4f5150442e16499c07fde42d4ab6 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 26 May 2022 15:56:56 +0300 Subject: [PATCH 28/61] Conserve memory allocations & Improve animation system. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use memory when we need it 😠 --- core/2d/CCParticleSystem.cpp | 283 +++++++++++++++++++++---------- core/2d/CCParticleSystem.h | 55 +++--- core/2d/CCParticleSystemQuad.cpp | 4 +- 3 files changed, 228 insertions(+), 114 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index c8a8435c74..3aefab8b3b 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -138,9 +138,7 @@ bool ParticleData::init(int count) deltaColorG = (float*)malloc(count * sizeof(float)); deltaColorB = (float*)malloc(count * sizeof(float)); deltaColorA = (float*)malloc(count * sizeof(float)); - hue = (float*)malloc(count * sizeof(float)); - sat = (float*)malloc(count * sizeof(float)); - val = (float*)malloc(count * sizeof(float)); + size = (float*)malloc(count * sizeof(float)); deltaSize = (float*)malloc(count * sizeof(float)); rotation = (float*)malloc(count * sizeof(float)); @@ -148,10 +146,6 @@ bool ParticleData::init(int count) deltaRotation = (float*)malloc(count * sizeof(float)); totalTimeToLive = (float*)malloc(count * sizeof(float)); timeToLive = (float*)malloc(count * sizeof(float)); - animTimeLength = (float*)malloc(count * sizeof(float)); - animTimeDelta = (float*)malloc(count * sizeof(float)); - animIndex = (unsigned short*)malloc(count * sizeof(unsigned short)); - animCellIndex = (unsigned short*)malloc(count * sizeof(unsigned short)); atlasIndex = (unsigned int*)malloc(count * sizeof(unsigned int)); modeA.dirX = (float*)malloc(count * sizeof(float)); @@ -165,10 +159,10 @@ bool ParticleData::init(int count) modeB.radius = (float*)malloc(count * sizeof(float)); return posx && posy && startPosX && startPosY && colorR && colorG && colorB && colorA && deltaColorR && - deltaColorG && deltaColorB && deltaColorA && hue && sat && val && size && deltaSize && rotation && - staticRotation && deltaRotation && totalTimeToLive && timeToLive && animTimeLength && animTimeDelta && - animIndex && animCellIndex && atlasIndex && modeA.dirX && modeA.dirY && modeA.radialAccel && - modeA.tangentialAccel && modeB.angle && modeB.degreesPerSecond && modeB.deltaRadius && modeB.radius; + deltaColorG && deltaColorB && deltaColorA && size && deltaSize && rotation && staticRotation && + deltaRotation && totalTimeToLive && timeToLive && atlasIndex && modeA.dirX && modeA.dirY && + modeA.radialAccel && modeA.tangentialAccel && modeB.angle && modeB.degreesPerSecond && modeB.deltaRadius && + modeB.radius; } void ParticleData::release() @@ -226,6 +220,8 @@ ParticleSystem::ParticleSystem() , _atlasIndex(0) , _transformSystemDirty(false) , _allocatedParticles(0) + , _isAnimAllocated(false) + , _isHSVAllocated(false) , _isActive(true) , _particleCount(0) , _duration(0) @@ -244,6 +240,7 @@ ParticleSystem::ParticleSystem() , _endSpinVar(0) , _spawnAngle(0) , _spawnAngleVar(0) + , _isHsv(false) , _hsv(0, 1, 1) , _hsvVar(0, 0, 0) , _emissionRate(0) @@ -314,6 +311,51 @@ Vector& ParticleSystem::getAllParticleSystems() return __allInstances; } +bool ParticleSystem::allocAnimationMem() +{ + if (!_isAnimAllocated) + { + _particleData.animTimeLength = (float*)malloc(_totalParticles * sizeof(float)); + _particleData.animTimeDelta = (float*)malloc(_totalParticles * sizeof(float)); + _particleData.animIndex = (unsigned short*)malloc(_totalParticles * sizeof(unsigned short)); + _particleData.animCellIndex = (unsigned short*)malloc(_totalParticles * sizeof(unsigned short)); + } + return _isAnimAllocated = _particleData.animTimeLength && _particleData.animTimeDelta && + _particleData.animIndex && _particleData.animCellIndex; +} + +void ParticleSystem::deallocAnimationMem() +{ + if (!_isAnimAllocated) + { + CC_SAFE_FREE(_particleData.animTimeLength); + CC_SAFE_FREE(_particleData.animTimeDelta); + CC_SAFE_FREE(_particleData.animIndex); + CC_SAFE_FREE(_particleData.animCellIndex); + } +} + +bool ParticleSystem::allocHSVMem() +{ + if (!_isHSVAllocated) + { + _particleData.hue = (float*)malloc(_totalParticles * sizeof(float)); + _particleData.sat = (float*)malloc(_totalParticles * sizeof(float)); + _particleData.val = (float*)malloc(_totalParticles * sizeof(float)); + } + return _isHSVAllocated = _particleData.hue && _particleData.sat && _particleData.val; +} + +void ParticleSystem::deallocHSVMem() +{ + if (!_isHSVAllocated) + { + CC_SAFE_FREE(_particleData.hue); + CC_SAFE_FREE(_particleData.sat); + CC_SAFE_FREE(_particleData.val); + } +} + void ParticleSystem::setTotalParticleCountFactor(float factor) { __totalParticleCountFactor = factor; @@ -647,7 +689,7 @@ ParticleSystem::~ParticleSystem() CC_SAFE_RELEASE(_texture); } -void ParticleSystem::addParticles(int count, int animationCellIndex, int animationIndex) +void ParticleSystem::addParticles(int count, int animationIndex, int animationCellIndex) { if (_paused) return; @@ -681,46 +723,57 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_KISS(); } - if (animationCellIndex == -1 && _isEmitterAnimated) + if (animationCellIndex != -1 || animationIndex != -1) + allocAnimationMem(); + + if (_isAnimAllocated) { - for (int i = start; i < _particleCount; ++i) + if (animationCellIndex != -1) + std::fill_n(_particleData.animCellIndex + start, _particleCount - start, animationCellIndex); + else + std::fill_n(_particleData.animCellIndex + start, _particleCount - start, 0xFFFF); + + if (animationIndex != -1) { - int rand0 = abs(RANDOM_KISS() * _animIndexCount); - _particleData.animCellIndex[i] = MIN(rand0, _animIndexCount - 1); + for (int i = start; i < _particleCount; ++i) + { + _particleData.animIndex[i] = animationIndex; + auto& descriptor = _animations.at(animationIndex); + _particleData.animTimeLength[i] = + descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_KISS(); + } } } - if (animationCellIndex != -1) - std::fill_n(_particleData.animCellIndex + start, _particleCount - start, animationCellIndex); - - if (animationIndex == -1 && !_animations.empty()) + if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) { - if (_randomAnimations.empty()) - setMultiAnimationRandom(); - - for (int i = start; i < _particleCount; ++i) + if (animationCellIndex == -1 && _isEmitterAnimated) { - int rand0 = abs(RANDOM_KISS() * _randomAnimations.size()); - int index = MIN(rand0, _randomAnimations.size() - 1); - _particleData.animIndex[i] = _randomAnimations[index]; - auto& descriptor = _animations.at(_particleData.animIndex[i]); - _particleData.animTimeLength[i] = - descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_KISS(); + for (int i = start; i < _particleCount; ++i) + { + int rand0 = abs(RANDOM_KISS() * _animIndexCount); + _particleData.animCellIndex[i] = MIN(rand0, _animIndexCount - 1); + } } - } - if (_isEmitterAnimated || _isLoopAnimated) - std::fill_n(_particleData.animTimeDelta + start, _particleCount - start, 0); - - if (animationIndex != -1) - { - for (int i = start; i < _particleCount; ++i) + if (animationIndex == -1 && !_animations.empty()) { - _particleData.animIndex[i] = animationIndex; - auto& descriptor = _animations.at(animationIndex); - _particleData.animTimeLength[i] = - descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_KISS(); + if (_randomAnimations.empty()) + setMultiAnimationRandom(); + + for (int i = start; i < _particleCount; ++i) + { + int rand0 = abs(RANDOM_KISS() * _randomAnimations.size()); + int index = MIN(rand0, _randomAnimations.size() - 1); + _particleData.animIndex[i] = _randomAnimations[index]; + auto& descriptor = _animations.at(_particleData.animIndex[i]); + _particleData.animTimeLength[i] = + descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_KISS(); + } } + + if (_isEmitterAnimated || _isLoopAnimated) + std::fill_n(_particleData.animTimeDelta + start, _particleCount - start, 0); } // color @@ -752,6 +805,7 @@ void ParticleSystem::addParticles(int count, int animationCellIndex, int animati SET_DELTA_COLOR(_particleData.colorA, _particleData.deltaColorA); // hue saturation value color + if (_isHSVAllocated) { for (int i = start; i < _particleCount; ++i) { @@ -915,6 +969,45 @@ void ParticleSystem::setAnimationDescriptor(unsigned short indexOfDescriptor, desc.reverseIndices = reverse; } +void ParticleSystem::setLifeAnimation(bool enabled) +{ + if (enabled && !allocAnimationMem()) + return; + + if (!enabled) + deallocAnimationMem(); + + _isLifeAnimated = enabled; + _isEmitterAnimated = false; + _isLoopAnimated = false; +} + +void ParticleSystem::setEmitterAnimation(bool enabled) +{ + if (enabled && !allocAnimationMem()) + return; + + if (!enabled) + deallocAnimationMem(); + + _isEmitterAnimated = enabled; + _isLifeAnimated = false; + _isLoopAnimated = false; +} + +void ParticleSystem::setLoopAnimation(bool enabled) +{ + if (enabled && !allocAnimationMem()) + return; + + if (!enabled) + deallocAnimationMem(); + + _isLoopAnimated = enabled; + _isEmitterAnimated = false; + _isLifeAnimated = false; +} + void ParticleSystem::resetAnimationIndices() { _animIndexCount = 0; @@ -1175,55 +1268,64 @@ void ParticleSystem::update(float dt) for (int i = 0; i < _particleCount; ++i) { _particleData.timeToLive[i] -= dt; - if (_isEmitterAnimated && !_animations.empty()) + } + + if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) + { + for (int i = 0; i < _particleCount; ++i) { - _particleData.animTimeDelta[i] += (_animationTimescaleInd ? pureDt : dt); - if (_particleData.animTimeDelta[i] > _particleData.animTimeLength[i]) + if (_isEmitterAnimated && !_animations.empty()) { - auto& anim = _animations.at(_particleData.animIndex[i]); - float percent = abs(RANDOM_KISS()); - percent = anim.reverseIndices ? 1.0F - percent : percent; + _particleData.animTimeDelta[i] += (_animationTimescaleInd ? pureDt : dt); + if (_particleData.animTimeDelta[i] > _particleData.animTimeLength[i]) + { + auto& anim = _animations.at(_particleData.animIndex[i]); + float percent = abs(RANDOM_KISS()); + percent = anim.reverseIndices ? 1.0F - percent : percent; - _particleData.animCellIndex[i] = anim.animationIndices[MIN( - percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; - _particleData.animTimeDelta[i] = 0; + _particleData.animCellIndex[i] = anim.animationIndices[MIN( + percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; + _particleData.animTimeDelta[i] = 0; + } } + if (_isLifeAnimated && _animations.empty()) + { + float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / + _particleData.totalTimeToLive[i]; + percent = _isAnimationReversed ? 1.0F - percent : percent; + _particleData.animCellIndex[i] = + (unsigned short)MIN(percent * _animIndexCount, _animIndexCount - 1); + } + if (_isLifeAnimated && !_animations.empty()) + { + auto& anim = _animations.at(_particleData.animIndex[i]); + + float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / + _particleData.totalTimeToLive[i]; + percent = (!!_isAnimationReversed != !!anim.reverseIndices) ? 1.0F - percent : percent; + percent = MAX(0.0F, percent); + + _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), + anim.animationIndices.size() - 1)]; + } + if (_isLoopAnimated && !_animations.empty()) + { + auto& anim = _animations.at(_particleData.animIndex[i]); + + _particleData.animTimeDelta[i] += (_animationTimescaleInd ? pureDt : dt); + if (_particleData.animTimeDelta[i] >= _particleData.animTimeLength[i]) + _particleData.animTimeDelta[i] = 0; + + float percent = _particleData.animTimeDelta[i] / _particleData.animTimeLength[i]; + percent = anim.reverseIndices ? 1.0F - percent : percent; + percent = MAX(0.0F, percent); + + _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), + anim.animationIndices.size() - 1)]; + } + if (_isLoopAnimated && _animations.empty()) + std::fill_n(_particleData.animTimeDelta, _particleCount, 0); } - if (_isLifeAnimated && _animations.empty()) - { - float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; - percent = _isAnimationReversed ? 1.0F - percent : percent; - _particleData.animCellIndex[i] = (unsigned short)MIN(percent * _animIndexCount, _animIndexCount - 1); - } - if (_isLifeAnimated && !_animations.empty()) - { - auto& anim = _animations.at(_particleData.animIndex[i]); - - float percent = - (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; - percent = (!!_isAnimationReversed != !!anim.reverseIndices) ? 1.0F - percent : percent; - percent = MAX(0.0F, percent); - - _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), - anim.animationIndices.size() - 1)]; - } - if (_isLoopAnimated && !_animations.empty()) - { - auto& anim = _animations.at(_particleData.animIndex[i]); - - _particleData.animTimeDelta[i] += (_animationTimescaleInd ? pureDt : dt); - if (_particleData.animTimeDelta[i] >= _particleData.animTimeLength[i]) - _particleData.animTimeDelta[i] = 0; - - float percent = _particleData.animTimeDelta[i] / _particleData.animTimeLength[i]; - percent = anim.reverseIndices ? 1.0F - percent : percent; - percent = MAX(0.0F, percent); - - _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), - anim.animationIndices.size() - 1)]; - } - if (_isLoopAnimated && _animations.empty()) - std::fill_n(_particleData.animTimeDelta, _particleCount, 0); } for (int i = 0; i < _particleCount; ++i) @@ -1614,6 +1716,17 @@ bool ParticleSystem::isActive() const return _isActive; } +void ParticleSystem::useHSV(bool hsv) +{ + if (hsv && !allocHSVMem()) + return; + + if (!hsv) + deallocHSVMem(); + + _isHsv = hsv; +}; + int ParticleSystem::getTotalParticles() const { return _totalParticles; diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 040db16a75..afcac79e52 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -150,9 +150,12 @@ public: deltaColorB[p1] = deltaColorB[p2]; deltaColorA[p1] = deltaColorA[p2]; - hue[p1] = hue[p2]; - sat[p1] = sat[p2]; - val[p1] = val[p2]; + if (hue && sat && val) + { + hue[p1] = hue[p2]; + sat[p1] = sat[p2]; + val[p1] = val[p2]; + } size[p1] = size[p2]; deltaSize[p1] = deltaSize[p2]; @@ -162,11 +165,15 @@ public: totalTimeToLive[p1] = totalTimeToLive[p2]; timeToLive[p1] = timeToLive[p2]; - animTimeDelta[p1] = animTimeDelta[p2]; - animTimeLength[p1] = animTimeLength[p2]; - animIndex[p1] = animIndex[p2]; - animCellIndex[p1] = animCellIndex[p2]; + if (animTimeDelta && animTimeLength && animIndex && animCellIndex) + { + animTimeDelta[p1] = animTimeDelta[p2]; + animTimeLength[p1] = animTimeLength[p2]; + animIndex[p1] = animIndex[p2]; + animCellIndex[p1] = animCellIndex[p2]; + } + atlasIndex[p1] = atlasIndex[p2]; modeA.dirX[p1] = modeA.dirX[p2]; @@ -309,8 +316,17 @@ public: */ static Vector& getAllParticleSystems(); +protected: + bool allocAnimationMem(); + void deallocAnimationMem(); + bool _isAnimAllocated; + + bool allocHSVMem(); + void deallocHSVMem(); + bool _isHSVAllocated; + public: - void addParticles(int count, int animationCellIndex = -1, int animationIndex = -1); + void addParticles(int count, int animationIndex = -1, int animationCellIndex = -1); void stopSystem(); /** Kill all living particles. @@ -721,7 +737,7 @@ public: * * @param hsv Use HSV color system. */ - void useHSV(bool hsv) { _isHsv = hsv; }; + void useHSV(bool hsv); bool isHSV() { return _isHsv; }; /** Gets the hue of each particle. @@ -861,28 +877,13 @@ public: bool isOpacityModifyRGB() const override { return _opacityModifyRGB; } /** Enables or disables tex coord animations that are set based on particle life. */ - void setLifeAnimation(bool enabled) - { - _isLifeAnimated = enabled; - _isEmitterAnimated = false; - _isLoopAnimated = false; - } + void setLifeAnimation(bool enabled); /** Enables or disables tex coord animations that are set by the emitter randomly when a particle is emitted. */ - void setEmitterAnimation(bool enabled) - { - _isEmitterAnimated = enabled; - _isLifeAnimated = false; - _isLoopAnimated = false; - } + void setEmitterAnimation(bool enabled); /** Enables or disables tex coord animations that are used to make particles play a sequence forever until they die */ - void setLoopAnimation(bool enabled) - { - _isLoopAnimated = enabled; - _isEmitterAnimated = false; - _isLifeAnimated = false; - } + void setLoopAnimation(bool enabled); bool isLifeAnimated() { return _isLifeAnimated; } bool isEmitterAnimated() { return _isEmitterAnimated; } diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index 365fe0c847..ea06e9a033 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -507,7 +507,7 @@ void ParticleSystemQuad::updateParticleQuads() // And wether if every property's memory of the particle system is continuous, // for the purpose of improving cache hit rate, we should process only one property in one for-loop. // It was proved to be effective especially for low-end devices. - if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) + if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated || _isAnimAllocated) { V3F_C4B_T2F_Quad* quad = startQuad; unsigned short* cellIndex = _particleData.animCellIndex; @@ -521,7 +521,7 @@ void ParticleSystemQuad::updateParticleQuads() auto iter = _animationIndices.find(*cellIndex); if (iter == _animationIndices.end()) - index.rect = _undefinedIndexRect; + index.rect = {0, 0, float(_texture->getPixelsWide()), float(_texture->getPixelsHigh())}; else index = iter->second; From caa1b7594ea8069ffa32890350a7992af2357459 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 26 May 2022 16:53:26 +0300 Subject: [PATCH 29/61] Fix texture atlas index unintended increment bug. [skip ci] --- core/2d/CCParticleSystem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 3aefab8b3b..284e53a11b 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1071,7 +1071,7 @@ void ParticleSystem::setAnimationIndicesAtlas(unsigned int unifiedCellSize, TexA frame.size.x = texWidth; frame.size.y = unifiedCellSize; - addAnimationIndex(_animIndexCount++, frame); + addAnimationIndex(_animIndexCount, frame); } break; @@ -1088,7 +1088,7 @@ void ParticleSystem::setAnimationIndicesAtlas(unsigned int unifiedCellSize, TexA frame.size.x = unifiedCellSize; frame.size.y = texHeight; - addAnimationIndex(_animIndexCount++, frame); + addAnimationIndex(_animIndexCount, frame); } break; From 83655d27980677aa535b7b100bdb64eed7764ec6 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 26 May 2022 19:59:48 +0300 Subject: [PATCH 30/61] Add spawn fade in feature. --- core/2d/CCParticleSystem.cpp | 66 +++++++++- core/2d/CCParticleSystem.h | 39 ++++++ core/2d/CCParticleSystemQuad.cpp | 205 ++++++++++++++++++++++--------- 3 files changed, 251 insertions(+), 59 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 284e53a11b..3f6b1b2cf5 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -182,6 +182,8 @@ void ParticleData::release() CC_SAFE_FREE(hue); CC_SAFE_FREE(sat); CC_SAFE_FREE(val); + CC_SAFE_FREE(opacityFadeInDelta); + CC_SAFE_FREE(opacityFadeInLength); CC_SAFE_FREE(size); CC_SAFE_FREE(deltaSize); CC_SAFE_FREE(rotation); @@ -243,6 +245,9 @@ ParticleSystem::ParticleSystem() , _isHsv(false) , _hsv(0, 1, 1) , _hsvVar(0, 0, 0) + , _isOpacityFadeInAllocated(false) + , _spawnFadeIn(0) + , _spawnFadeInVar(0) , _emissionRate(0) , _totalParticles(0) , _texture(nullptr) @@ -326,7 +331,7 @@ bool ParticleSystem::allocAnimationMem() void ParticleSystem::deallocAnimationMem() { - if (!_isAnimAllocated) + if (_isAnimAllocated) { CC_SAFE_FREE(_particleData.animTimeLength); CC_SAFE_FREE(_particleData.animTimeDelta); @@ -348,7 +353,7 @@ bool ParticleSystem::allocHSVMem() void ParticleSystem::deallocHSVMem() { - if (!_isHSVAllocated) + if (_isHSVAllocated) { CC_SAFE_FREE(_particleData.hue); CC_SAFE_FREE(_particleData.sat); @@ -356,6 +361,25 @@ void ParticleSystem::deallocHSVMem() } } +bool ParticleSystem::allocOpacityFadeInMem() +{ + if (!_isOpacityFadeInAllocated) + { + _particleData.opacityFadeInDelta = (float*)malloc(_totalParticles * sizeof(float)); + _particleData.opacityFadeInLength = (float*)malloc(_totalParticles * sizeof(float)); + } + return _isOpacityFadeInAllocated = _particleData.opacityFadeInDelta && _particleData.opacityFadeInLength; +} + +void ParticleSystem::deallocOpacityFadeInMem() +{ + if (_isOpacityFadeInAllocated) + { + CC_SAFE_FREE(_particleData.opacityFadeInDelta); + CC_SAFE_FREE(_particleData.opacityFadeInLength); + } +} + void ParticleSystem::setTotalParticleCountFactor(float factor) { __totalParticleCountFactor = factor; @@ -694,7 +718,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe if (_paused) return; - // Try to add as many particles as you can without overflowing. + // Try to add as many particles as possible without overflowing. count = MIN(int(_totalParticles * __totalParticleCountFactor) - _particleCount, count); animationCellIndex = MIN(animationCellIndex, _animIndexCount - 1); @@ -804,6 +828,16 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe SET_DELTA_COLOR(_particleData.colorB, _particleData.deltaColorB); SET_DELTA_COLOR(_particleData.colorA, _particleData.deltaColorA); + // opacity fade in + if (_isOpacityFadeInAllocated) + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.opacityFadeInLength[i] = _spawnFadeIn + _spawnFadeInVar * RANDOM_KISS(); + } + std::fill_n(_particleData.opacityFadeInDelta + start, _particleCount - start, 0.0F); + } + // hue saturation value color if (_isHSVAllocated) { @@ -1270,6 +1304,16 @@ void ParticleSystem::update(float dt) _particleData.timeToLive[i] -= dt; } + if (_isOpacityFadeInAllocated) + { + for (int i = 0; i < _particleCount; ++i) + { + _particleData.opacityFadeInDelta[i] += dt; + _particleData.opacityFadeInDelta[i] = + MIN(_particleData.opacityFadeInDelta[i], _particleData.opacityFadeInLength[i]); + } + } + if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) { for (int i = 0; i < _particleCount; ++i) @@ -1727,6 +1771,22 @@ void ParticleSystem::useHSV(bool hsv) _isHsv = hsv; }; +void ParticleSystem::setSpawnFadeIn(float time) +{ + if (time != 0.0F && !allocOpacityFadeInMem()) + return; + + _spawnFadeIn = time; +} + +void ParticleSystem::setSpawnFadeInVar(float time) +{ + if (time != 0.0F && !allocOpacityFadeInMem()) + return; + + _spawnFadeInVar = time; +} + int ParticleSystem::getTotalParticles() const { return _totalParticles; diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index afcac79e52..5edd8c8906 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -96,6 +96,9 @@ public: float* sat; float* val; + float* opacityFadeInDelta; + float* opacityFadeInLength; + float* size; float* deltaSize; float* rotation; @@ -157,6 +160,12 @@ public: val[p1] = val[p2]; } + if (opacityFadeInDelta && opacityFadeInLength) + { + opacityFadeInDelta[p1] = opacityFadeInDelta[p2]; + opacityFadeInLength[p1] = opacityFadeInLength[p2]; + } + size[p1] = size[p2]; deltaSize[p1] = deltaSize[p2]; rotation[p1] = rotation[p2]; @@ -325,6 +334,10 @@ protected: void deallocHSVMem(); bool _isHSVAllocated; + bool allocOpacityFadeInMem(); + void deallocOpacityFadeInMem(); + bool _isOpacityFadeInAllocated; + public: void addParticles(int count, int animationIndex = -1, int animationCellIndex = -1); @@ -850,6 +863,28 @@ public: */ void setSpawnAngleVar(float angle) { _spawnAngleVar = angle; } + /** Gets the spawn opacity fade in time of each particle. + * + * @return The spawn opacity fade in time in seconds. + */ + float getSpawnFadeIn() { return _spawnFadeIn; } + /** Sets the spawn opacity fade in time of each particle when it's created. + * + * @param time The spawn opacity fade in time in seconds. + */ + void setSpawnFadeIn(float time); + + /** Gets the spawn opacity fade in time variance of each particle. + * + * @return The spawn opacity fade in time variance in seconds. + */ + float getSpawnFadeInVar() { return _spawnFadeInVar; } + /** Sets the spawn opacity fade in time variance of each particle when it's created. + * + * @param time The spawn opacity fade in time variance in seconds. + */ + void setSpawnFadeInVar(float time); + /** Gets the emission rate of the particles. * * @return The emission rate of the particles. @@ -1310,6 +1345,10 @@ protected: float _spawnAngle; //* initial rotation of each particle float _spawnAngleVar; + //* initial fade in time of each particle + float _spawnFadeIn; + //* initial fade in time variance of each particle + float _spawnFadeInVar; /** emission rate of the particles */ float _emissionRate; /** maximum particles of the system */ diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index ea06e9a033..53064b506b 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -420,83 +420,176 @@ void ParticleSystemQuad::updateParticleQuads() float* b = _particleData.colorB; float* a = _particleData.colorA; - // HSV calculation is expensive, so we should skip it if it's not enabled. - if (_isHsv) + if (_isOpacityFadeInAllocated) { - float* hue = _particleData.hue; - float* sat = _particleData.sat; - float* val = _particleData.val; + float* fadeDt = _particleData.opacityFadeInDelta; + float* fadeLn = _particleData.opacityFadeInLength; - if (_opacityModifyRGB) + // HSV calculation is expensive, so we should skip it if it's not enabled. + if (_isHsv) { - auto hsv = HSV(); - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) + float* hue = _particleData.hue; + float* sat = _particleData.sat; + float* val = _particleData.val; + + if (_opacityModifyRGB) { - float colorR = *r * *a; - float colorG = *g * *a; - float colorB = *b * *a; - float colorA = *a; - hsv.set(colorR, colorG, colorB, colorA); - hsv.h += *hue; - hsv.s = abs(*sat); - hsv.v = abs(*val); - auto col = hsv.toColor4B(); - quad->bl.colors.set(col.r, col.g, col.b, col.a); - quad->br.colors.set(col.r, col.g, col.b, col.a); - quad->tl.colors.set(col.r, col.g, col.b, col.a); - quad->tr.colors.set(col.r, col.g, col.b, col.a); + auto hsv = HSV(); + for (int i = 0; i < _particleCount; + ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val, ++fadeDt, ++fadeLn) + { + float colorR = *r * *a; + float colorG = *g * *a; + float colorB = *b * *a; + float colorA = *a * (*fadeDt / *fadeLn); + hsv.set(colorR, colorG, colorB, colorA); + hsv.h += *hue; + hsv.s = abs(*sat); + hsv.v = abs(*val); + auto col = hsv.toColor4B(); + quad->bl.colors.set(col.r, col.g, col.b, col.a); + quad->br.colors.set(col.r, col.g, col.b, col.a); + quad->tl.colors.set(col.r, col.g, col.b, col.a); + quad->tr.colors.set(col.r, col.g, col.b, col.a); + } + } + else + { + auto hsv = HSV(); + for (int i = 0; i < _particleCount; + ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val, ++fadeDt, ++fadeLn) + { + float colorR = *r; + float colorG = *g; + float colorB = *b; + float colorA = *a * (*fadeDt / *fadeLn); + hsv.set(colorR, colorG, colorB, colorA); + hsv.h += *hue; + hsv.s = abs(*sat); + hsv.v = abs(*val); + auto col = hsv.toColor4B(); + quad->bl.colors.set(col.r, col.g, col.b, col.a); + quad->br.colors.set(col.r, col.g, col.b, col.a); + quad->tl.colors.set(col.r, col.g, col.b, col.a); + quad->tr.colors.set(col.r, col.g, col.b, col.a); + } } } else { - auto hsv = HSV(); - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) + // set color + if (_opacityModifyRGB) { - float colorR = *r; - float colorG = *g; - float colorB = *b; - float colorA = *a; - hsv.set(colorR, colorG, colorB, colorA); - hsv.h += *hue; - hsv.s = abs(*sat); - hsv.v = abs(*val); - auto col = hsv.toColor4B(); - quad->bl.colors.set(col.r, col.g, col.b, col.a); - quad->br.colors.set(col.r, col.g, col.b, col.a); - quad->tl.colors.set(col.r, col.g, col.b, col.a); - quad->tr.colors.set(col.r, col.g, col.b, col.a); + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++fadeDt, ++fadeLn) + { + uint8_t colorR = *r * *a * 255; + uint8_t colorG = *g * *a * 255; + uint8_t colorB = *b * *a * 255; + uint8_t colorA = *a * (*fadeDt / *fadeLn) * 255; + quad->bl.colors.set(colorR, colorG, colorB, colorA); + quad->br.colors.set(colorR, colorG, colorB, colorA); + quad->tl.colors.set(colorR, colorG, colorB, colorA); + quad->tr.colors.set(colorR, colorG, colorB, colorA); + } + } + else + { + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++fadeDt, ++fadeLn) + { + uint8_t colorR = *r * 255; + uint8_t colorG = *g * 255; + uint8_t colorB = *b * 255; + uint8_t colorA = *a * (*fadeDt / *fadeLn) * 255; + quad->bl.colors.set(colorR, colorG, colorB, colorA); + quad->br.colors.set(colorR, colorG, colorB, colorA); + quad->tl.colors.set(colorR, colorG, colorB, colorA); + quad->tr.colors.set(colorR, colorG, colorB, colorA); + } } } } else { - // set color - if (_opacityModifyRGB) + // HSV calculation is expensive, so we should skip it if it's not enabled. + if (_isHsv) { - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + float* hue = _particleData.hue; + float* sat = _particleData.sat; + float* val = _particleData.val; + + if (_opacityModifyRGB) { - uint8_t colorR = *r * *a * 255; - uint8_t colorG = *g * *a * 255; - uint8_t colorB = *b * *a * 255; - uint8_t colorA = *a * 255; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); + auto hsv = HSV(); + for (int i = 0; i < _particleCount; + ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) + { + float colorR = *r * *a; + float colorG = *g * *a; + float colorB = *b * *a; + float colorA = *a; + hsv.set(colorR, colorG, colorB, colorA); + hsv.h += *hue; + hsv.s = abs(*sat); + hsv.v = abs(*val); + auto col = hsv.toColor4B(); + quad->bl.colors.set(col.r, col.g, col.b, col.a); + quad->br.colors.set(col.r, col.g, col.b, col.a); + quad->tl.colors.set(col.r, col.g, col.b, col.a); + quad->tr.colors.set(col.r, col.g, col.b, col.a); + } + } + else + { + auto hsv = HSV(); + for (int i = 0; i < _particleCount; + ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) + { + float colorR = *r; + float colorG = *g; + float colorB = *b; + float colorA = *a; + hsv.set(colorR, colorG, colorB, colorA); + hsv.h += *hue; + hsv.s = abs(*sat); + hsv.v = abs(*val); + auto col = hsv.toColor4B(); + quad->bl.colors.set(col.r, col.g, col.b, col.a); + quad->br.colors.set(col.r, col.g, col.b, col.a); + quad->tl.colors.set(col.r, col.g, col.b, col.a); + quad->tr.colors.set(col.r, col.g, col.b, col.a); + } } } else { - for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + // set color + if (_opacityModifyRGB) { - uint8_t colorR = *r * 255; - uint8_t colorG = *g * 255; - uint8_t colorB = *b * 255; - uint8_t colorA = *a * 255; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + { + uint8_t colorR = *r * *a * 255; + uint8_t colorG = *g * *a * 255; + uint8_t colorB = *b * *a * 255; + uint8_t colorA = *a * 255; + quad->bl.colors.set(colorR, colorG, colorB, colorA); + quad->br.colors.set(colorR, colorG, colorB, colorA); + quad->tl.colors.set(colorR, colorG, colorB, colorA); + quad->tr.colors.set(colorR, colorG, colorB, colorA); + } + } + else + { + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) + { + uint8_t colorR = *r * 255; + uint8_t colorG = *g * 255; + uint8_t colorB = *b * 255; + uint8_t colorA = *a * 255; + quad->bl.colors.set(colorR, colorG, colorB, colorA); + quad->br.colors.set(colorR, colorG, colorB, colorA); + quad->tl.colors.set(colorR, colorG, colorB, colorA); + quad->tr.colors.set(colorR, colorG, colorB, colorA); + } } } } From 6fe25419922ffa2c97119b87ad8f05a4d0fb228f Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 26 May 2022 21:34:15 +0300 Subject: [PATCH 31/61] =?UTF-8?q?Put=20loops=20inside=20ifs=20statements?= =?UTF-8?q?=20not=20the=20opposite=20=F0=9F=A4=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/2d/CCParticleSystem.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 3f6b1b2cf5..59e9bdeb5e 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1316,9 +1316,9 @@ void ParticleSystem::update(float dt) if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) { - for (int i = 0; i < _particleCount; ++i) + if (_isEmitterAnimated && !_animations.empty()) { - if (_isEmitterAnimated && !_animations.empty()) + for (int i = 0; i < _particleCount; ++i) { _particleData.animTimeDelta[i] += (_animationTimescaleInd ? pureDt : dt); if (_particleData.animTimeDelta[i] > _particleData.animTimeLength[i]) @@ -1332,7 +1332,10 @@ void ParticleSystem::update(float dt) _particleData.animTimeDelta[i] = 0; } } - if (_isLifeAnimated && _animations.empty()) + } + if (_isLifeAnimated && _animations.empty()) + { + for (int i = 0; i < _particleCount; ++i) { float percent = (_particleData.totalTimeToLive[i] - _particleData.timeToLive[i]) / _particleData.totalTimeToLive[i]; @@ -1340,7 +1343,10 @@ void ParticleSystem::update(float dt) _particleData.animCellIndex[i] = (unsigned short)MIN(percent * _animIndexCount, _animIndexCount - 1); } - if (_isLifeAnimated && !_animations.empty()) + } + if (_isLifeAnimated && !_animations.empty()) + { + for (int i = 0; i < _particleCount; ++i) { auto& anim = _animations.at(_particleData.animIndex[i]); @@ -1352,7 +1358,10 @@ void ParticleSystem::update(float dt) _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; } - if (_isLoopAnimated && !_animations.empty()) + } + if (_isLoopAnimated && !_animations.empty()) + { + for (int i = 0; i < _particleCount; ++i) { auto& anim = _animations.at(_particleData.animIndex[i]); @@ -1367,9 +1376,9 @@ void ParticleSystem::update(float dt) _particleData.animCellIndex[i] = anim.animationIndices[MIN(percent * anim.animationIndices.size(), anim.animationIndices.size() - 1)]; } - if (_isLoopAnimated && _animations.empty()) - std::fill_n(_particleData.animTimeDelta, _particleCount, 0); } + if (_isLoopAnimated && _animations.empty()) + std::fill_n(_particleData.animTimeDelta, _particleCount, 0); } for (int i = 0; i < _particleCount; ++i) From 298a73c746365ba348c8e25d476059e9b606a4d4 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 26 May 2022 22:53:19 +0300 Subject: [PATCH 32/61] Add spawn scale in feature. --- core/2d/CCParticleSystem.cpp | 59 +++++++++++++++++++++ core/2d/CCParticleSystem.h | 55 +++++++++++++++++-- core/2d/CCParticleSystemQuad.cpp | 90 ++++++++++++++++++++++++-------- 3 files changed, 179 insertions(+), 25 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 59e9bdeb5e..6ce352b0f1 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -184,6 +184,8 @@ void ParticleData::release() CC_SAFE_FREE(val); CC_SAFE_FREE(opacityFadeInDelta); CC_SAFE_FREE(opacityFadeInLength); + CC_SAFE_FREE(scaleInDelta); + CC_SAFE_FREE(scaleInLength); CC_SAFE_FREE(size); CC_SAFE_FREE(deltaSize); CC_SAFE_FREE(rotation); @@ -248,6 +250,9 @@ ParticleSystem::ParticleSystem() , _isOpacityFadeInAllocated(false) , _spawnFadeIn(0) , _spawnFadeInVar(0) + , _isScaleInAllocated(false) + , _spawnScaleIn(0) + , _spawnScaleInVar(0) , _emissionRate(0) , _totalParticles(0) , _texture(nullptr) @@ -380,6 +385,25 @@ void ParticleSystem::deallocOpacityFadeInMem() } } +bool ParticleSystem::allocScaleInMem() +{ + if (!_isScaleInAllocated) + { + _particleData.scaleInDelta = (float*)malloc(_totalParticles * sizeof(float)); + _particleData.scaleInLength = (float*)malloc(_totalParticles * sizeof(float)); + } + return _isScaleInAllocated = _particleData.scaleInDelta && _particleData.scaleInLength; +} + +void ParticleSystem::deallocScaleInMem() +{ + if (_isScaleInAllocated) + { + CC_SAFE_FREE(_particleData.scaleInDelta); + CC_SAFE_FREE(_particleData.scaleInLength); + } +} + void ParticleSystem::setTotalParticleCountFactor(float factor) { __totalParticleCountFactor = factor; @@ -838,6 +862,16 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe std::fill_n(_particleData.opacityFadeInDelta + start, _particleCount - start, 0.0F); } + // scale fade in + if (_isScaleInAllocated) + { + for (int i = start; i < _particleCount; ++i) + { + _particleData.scaleInLength[i] = _spawnScaleIn + _spawnScaleInVar * RANDOM_KISS(); + } + std::fill_n(_particleData.scaleInDelta + start, _particleCount - start, 0.0F); + } + // hue saturation value color if (_isHSVAllocated) { @@ -1314,6 +1348,15 @@ void ParticleSystem::update(float dt) } } + if (_isScaleInAllocated) + { + for (int i = 0; i < _particleCount; ++i) + { + _particleData.scaleInDelta[i] += dt; + _particleData.scaleInDelta[i] = MIN(_particleData.scaleInDelta[i], _particleData.scaleInLength[i]); + } + } + if (_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) { if (_isEmitterAnimated && !_animations.empty()) @@ -1796,6 +1839,22 @@ void ParticleSystem::setSpawnFadeInVar(float time) _spawnFadeInVar = time; } +void ParticleSystem::setSpawnScaleIn(float time) +{ + if (time != 0.0F && !allocScaleInMem()) + return; + + _spawnScaleIn = time; +} + +void ParticleSystem::setSpawnScaleInVar(float time) +{ + if (time != 0.0F && !allocScaleInMem()) + return; + + _spawnScaleInVar = time; +} + int ParticleSystem::getTotalParticles() const { return _totalParticles; diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 5edd8c8906..66c1e8fe14 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -99,6 +99,9 @@ public: float* opacityFadeInDelta; float* opacityFadeInLength; + float* scaleInDelta; + float* scaleInLength; + float* size; float* deltaSize; float* rotation; @@ -166,6 +169,12 @@ public: opacityFadeInLength[p1] = opacityFadeInLength[p2]; } + if (scaleInDelta && scaleInLength) + { + scaleInDelta[p1] = scaleInDelta[p2]; + scaleInLength[p1] = scaleInLength[p2]; + } + size[p1] = size[p2]; deltaSize[p1] = deltaSize[p2]; rotation[p1] = rotation[p2]; @@ -338,6 +347,10 @@ protected: void deallocOpacityFadeInMem(); bool _isOpacityFadeInAllocated; + bool allocScaleInMem(); + void deallocScaleInMem(); + bool _isScaleInAllocated; + public: void addParticles(int count, int animationIndex = -1, int animationCellIndex = -1); @@ -864,27 +877,57 @@ public: void setSpawnAngleVar(float angle) { _spawnAngleVar = angle; } /** Gets the spawn opacity fade in time of each particle. - * + * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a specified time. + * @return The spawn opacity fade in time in seconds. */ float getSpawnFadeIn() { return _spawnFadeIn; } /** Sets the spawn opacity fade in time of each particle when it's created. - * + * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a specified time. + * * @param time The spawn opacity fade in time in seconds. */ void setSpawnFadeIn(float time); /** Gets the spawn opacity fade in time variance of each particle. - * + * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a specified time. + * * @return The spawn opacity fade in time variance in seconds. */ float getSpawnFadeInVar() { return _spawnFadeInVar; } /** Sets the spawn opacity fade in time variance of each particle when it's created. - * + * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a specified time. + * * @param time The spawn opacity fade in time variance in seconds. */ void setSpawnFadeInVar(float time); + /** Gets the spawn opacity fade in time of each particle. + * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified time. + * + * @return The spawn opacity fade in time in seconds. + */ + float getSpawnScaleIn() { return _spawnScaleIn; } + /** Sets the spawn opacity fade in time of each particle when it's created. + * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified time. + * + * @param time The spawn opacity fade in time in seconds. + */ + void setSpawnScaleIn(float time); + + /** Gets the spawn opacity fade in time variance of each particle. + * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified time. + * + * @return The spawn opacity fade in time variance in seconds. + */ + float getSpawnScaleInVar() { return _spawnScaleInVar; } + /** Sets the spawn opacity fade in time variance of each particle when it's created. + * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified time. + * + * @param time The spawn opacity fade in time variance in seconds. + */ + void setSpawnScaleInVar(float time); + /** Gets the emission rate of the particles. * * @return The emission rate of the particles. @@ -1349,6 +1392,10 @@ protected: float _spawnFadeIn; //* initial fade in time variance of each particle float _spawnFadeInVar; + //* initial scale in time of each particle + float _spawnScaleIn; + //* initial scale in time variance of each particle + float _spawnScaleInVar; /** emission rate of the particles */ float _emissionRate; /** maximum particles of the system */ diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index 53064b506b..1bc7294d27 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -278,16 +278,17 @@ void ParticleSystemQuad::initIndices() inline void updatePosWithParticle(V3F_C4B_T2F_Quad* quad, const Vec2& newPosition, float size, + float scaleInSize, float rotation, float staticRotation) { // vertices float size_2 = size / 2; - float x1 = -size_2; - float y1 = -size_2; + float x1 = -size_2 * scaleInSize; + float y1 = -size_2 * scaleInSize; - float x2 = size_2; - float y2 = size_2; + float x2 = size_2 * scaleInSize; + float y2 = size_2 * scaleInSize; float x = newPosition.x; float y = newPosition.y; @@ -364,16 +365,34 @@ void ParticleSystemQuad::updateParticleQuads() float* s = _particleData.size; float* r = _particleData.rotation; float* sr = _particleData.staticRotation; + float* sid = _particleData.scaleInDelta; + float* sil = _particleData.scaleInLength; V3F_C4B_T2F_Quad* quadStart = startQuad; - for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr) + if (_isScaleInAllocated) { - p2.set(*startX, *startY, 0); - worldToNodeTM.transformPoint(&p2); - newPos.set(*x, *y); - p2 = p1 - p2; - newPos.x -= p2.x - pos.x; - newPos.y -= p2.y - pos.y; - updatePosWithParticle(quadStart, newPos, *s, *r, *sr); + for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr, ++sid, ++sil) + { + p2.set(*startX, *startY, 0); + worldToNodeTM.transformPoint(&p2); + newPos.set(*x, *y); + p2 = p1 - p2; + newPos.x -= p2.x - pos.x; + newPos.y -= p2.y - pos.y; + updatePosWithParticle(quadStart, newPos, *s, *sid / *sil, *r, *sr); + } + } + else + { + for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr) + { + p2.set(*startX, *startY, 0); + worldToNodeTM.transformPoint(&p2); + newPos.set(*x, *y); + p2 = p1 - p2; + newPos.x -= p2.x - pos.x; + newPos.y -= p2.y - pos.y; + updatePosWithParticle(quadStart, newPos, *s, 1.0F, *r, *sr); + } } } else if (_positionType == PositionType::RELATIVE) @@ -386,14 +405,30 @@ void ParticleSystemQuad::updateParticleQuads() float* s = _particleData.size; float* r = _particleData.rotation; float* sr = _particleData.staticRotation; + float* sid = _particleData.scaleInDelta; + float* sil = _particleData.scaleInLength; V3F_C4B_T2F_Quad* quadStart = startQuad; - for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr) + if (_isScaleInAllocated) { - newPos.set(*x, *y); - newPos.x = *x - (currentPosition.x - *startX); - newPos.y = *y - (currentPosition.y - *startY); - newPos += pos; - updatePosWithParticle(quadStart, newPos, *s, *r, *sr); + for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr, ++sid, ++sil) + { + newPos.set(*x, *y); + newPos.x = *x - (currentPosition.x - *startX); + newPos.y = *y - (currentPosition.y - *startY); + newPos += pos; + updatePosWithParticle(quadStart, newPos, *s, *sid / *sil, *r, *sr); + } + } + else + { + for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr) + { + newPos.set(*x, *y); + newPos.x = *x - (currentPosition.x - *startX); + newPos.y = *y - (currentPosition.y - *startY); + newPos += pos; + updatePosWithParticle(quadStart, newPos, *s, 1.0F, *r, *sr); + } } } else @@ -406,11 +441,24 @@ void ParticleSystemQuad::updateParticleQuads() float* s = _particleData.size; float* r = _particleData.rotation; float* sr = _particleData.staticRotation; + float* sid = _particleData.scaleInDelta; + float* sil = _particleData.scaleInLength; V3F_C4B_T2F_Quad* quadStart = startQuad; - for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr) + if (_isScaleInAllocated) { - newPos.set(*x + pos.x, *y + pos.y); - updatePosWithParticle(quadStart, newPos, *s, *r, *sr); + for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr, ++sid, ++sil) + { + newPos.set(*x + pos.x, *y + pos.y); + updatePosWithParticle(quadStart, newPos, *s, *sid / *sil, *r, *sr); + } + } + else + { + for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr) + { + newPos.set(*x + pos.x, *y + pos.y); + updatePosWithParticle(quadStart, newPos, *s, 1.0F, *r, *sr); + } } } From eda8c1376f33f074eb86c9ad067c8a5f6874e8f4 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Fri, 27 May 2022 00:29:53 +0300 Subject: [PATCH 33/61] =?UTF-8?q?Make=20scale=20in=20smooth=20=F0=9F=98=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/2d/CCParticleSystemQuad.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index 1bc7294d27..a228724b33 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -42,6 +42,7 @@ THE SOFTWARE. #include "base/ccUTF8.h" #include "renderer/ccShaders.h" #include "renderer/backend/ProgramState.h" +#include "2d/CCTweenFunction.h" NS_CC_BEGIN @@ -378,7 +379,7 @@ void ParticleSystemQuad::updateParticleQuads() p2 = p1 - p2; newPos.x -= p2.x - pos.x; newPos.y -= p2.y - pos.y; - updatePosWithParticle(quadStart, newPos, *s, *sid / *sil, *r, *sr); + updatePosWithParticle(quadStart, newPos, *s, tweenfunc::expoEaseOut(*sid / *sil), *r, *sr); } } else @@ -416,7 +417,7 @@ void ParticleSystemQuad::updateParticleQuads() newPos.x = *x - (currentPosition.x - *startX); newPos.y = *y - (currentPosition.y - *startY); newPos += pos; - updatePosWithParticle(quadStart, newPos, *s, *sid / *sil, *r, *sr); + updatePosWithParticle(quadStart, newPos, *s, tweenfunc::expoEaseOut(*sid / *sil), *r, *sr); } } else @@ -449,7 +450,7 @@ void ParticleSystemQuad::updateParticleQuads() for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr, ++sid, ++sil) { newPos.set(*x + pos.x, *y + pos.y); - updatePosWithParticle(quadStart, newPos, *s, *sid / *sil, *r, *sr); + updatePosWithParticle(quadStart, newPos, *s, tweenfunc::expoEaseOut(*sid / *sil), *r, *sr); } } else From 5d360a61bcd786ca9e4b665fe7e7c6a21ea25d86 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Fri, 27 May 2022 13:36:38 +0300 Subject: [PATCH 34/61] Improve allocation safety. --- core/2d/CCParticleSystem.cpp | 76 +++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 6ce352b0f1..f8e11d942d 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -326,23 +326,26 @@ bool ParticleSystem::allocAnimationMem() if (!_isAnimAllocated) { _particleData.animTimeLength = (float*)malloc(_totalParticles * sizeof(float)); - _particleData.animTimeDelta = (float*)malloc(_totalParticles * sizeof(float)); - _particleData.animIndex = (unsigned short*)malloc(_totalParticles * sizeof(unsigned short)); - _particleData.animCellIndex = (unsigned short*)malloc(_totalParticles * sizeof(unsigned short)); + _particleData.animTimeDelta = (float*)malloc(_totalParticles * sizeof(float)); + _particleData.animIndex = (unsigned short*)malloc(_totalParticles * sizeof(unsigned short)); + _particleData.animCellIndex = (unsigned short*)malloc(_totalParticles * sizeof(unsigned short)); + if (_particleData.animTimeLength && _particleData.animTimeDelta && + _particleData.animIndex && _particleData.animCellIndex) + return _isAnimAllocated = true; + else + // If any of the above allocations fail, then we safely deallocate the ones that succeeded. + deallocAnimationMem(); } - return _isAnimAllocated = _particleData.animTimeLength && _particleData.animTimeDelta && - _particleData.animIndex && _particleData.animCellIndex; + return false; } void ParticleSystem::deallocAnimationMem() { - if (_isAnimAllocated) - { - CC_SAFE_FREE(_particleData.animTimeLength); - CC_SAFE_FREE(_particleData.animTimeDelta); - CC_SAFE_FREE(_particleData.animIndex); - CC_SAFE_FREE(_particleData.animCellIndex); - } + CC_SAFE_FREE(_particleData.animTimeLength); + CC_SAFE_FREE(_particleData.animTimeDelta); + CC_SAFE_FREE(_particleData.animIndex); + CC_SAFE_FREE(_particleData.animCellIndex); + _isAnimAllocated = false; } bool ParticleSystem::allocHSVMem() @@ -352,37 +355,43 @@ bool ParticleSystem::allocHSVMem() _particleData.hue = (float*)malloc(_totalParticles * sizeof(float)); _particleData.sat = (float*)malloc(_totalParticles * sizeof(float)); _particleData.val = (float*)malloc(_totalParticles * sizeof(float)); + if (_particleData.hue && _particleData.sat && _particleData.val) + return _isHSVAllocated = true; + else + // If any of the above allocations fail, then we safely deallocate the ones that succeeded. + deallocHSVMem(); } - return _isHSVAllocated = _particleData.hue && _particleData.sat && _particleData.val; + return false; } void ParticleSystem::deallocHSVMem() { - if (_isHSVAllocated) - { - CC_SAFE_FREE(_particleData.hue); - CC_SAFE_FREE(_particleData.sat); - CC_SAFE_FREE(_particleData.val); - } + CC_SAFE_FREE(_particleData.hue); + CC_SAFE_FREE(_particleData.sat); + CC_SAFE_FREE(_particleData.val); + _isHSVAllocated = false; } bool ParticleSystem::allocOpacityFadeInMem() { if (!_isOpacityFadeInAllocated) { - _particleData.opacityFadeInDelta = (float*)malloc(_totalParticles * sizeof(float)); + _particleData.opacityFadeInDelta = (float*)malloc(_totalParticles * sizeof(float)); _particleData.opacityFadeInLength = (float*)malloc(_totalParticles * sizeof(float)); + if (_particleData.opacityFadeInDelta && _particleData.opacityFadeInLength) + return _isOpacityFadeInAllocated = true; + else + // If any of the above allocations fail, then we safely deallocate the ones that succeeded. + deallocOpacityFadeInMem(); } - return _isOpacityFadeInAllocated = _particleData.opacityFadeInDelta && _particleData.opacityFadeInLength; + return false; } void ParticleSystem::deallocOpacityFadeInMem() { - if (_isOpacityFadeInAllocated) - { - CC_SAFE_FREE(_particleData.opacityFadeInDelta); - CC_SAFE_FREE(_particleData.opacityFadeInLength); - } + CC_SAFE_FREE(_particleData.opacityFadeInDelta); + CC_SAFE_FREE(_particleData.opacityFadeInLength); + _isOpacityFadeInAllocated = false; } bool ParticleSystem::allocScaleInMem() @@ -391,17 +400,20 @@ bool ParticleSystem::allocScaleInMem() { _particleData.scaleInDelta = (float*)malloc(_totalParticles * sizeof(float)); _particleData.scaleInLength = (float*)malloc(_totalParticles * sizeof(float)); + if (_particleData.scaleInDelta && _particleData.scaleInLength) + return _isScaleInAllocated = true; + else + // If any of the above allocations fail, then we safely deallocate the ones that succeeded. + deallocScaleInMem(); } - return _isScaleInAllocated = _particleData.scaleInDelta && _particleData.scaleInLength; + return false; } void ParticleSystem::deallocScaleInMem() { - if (_isScaleInAllocated) - { - CC_SAFE_FREE(_particleData.scaleInDelta); - CC_SAFE_FREE(_particleData.scaleInLength); - } + CC_SAFE_FREE(_particleData.scaleInDelta); + CC_SAFE_FREE(_particleData.scaleInLength); + _isScaleInAllocated = false; } void ParticleSystem::setTotalParticleCountFactor(float factor) From 2e8370f61c6e35300734b28d58b41ed3f82b3b6a Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Fri, 27 May 2022 14:08:25 +0300 Subject: [PATCH 35/61] Remove useless `_isHSV` --- core/2d/CCParticleSystem.cpp | 7 ++----- core/2d/CCParticleSystem.h | 4 +--- core/2d/CCParticleSystemQuad.cpp | 4 ++-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index f8e11d942d..8ab0749948 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -226,6 +226,8 @@ ParticleSystem::ParticleSystem() , _allocatedParticles(0) , _isAnimAllocated(false) , _isHSVAllocated(false) + , _isOpacityFadeInAllocated(false) + , _isScaleInAllocated(false) , _isActive(true) , _particleCount(0) , _duration(0) @@ -244,13 +246,10 @@ ParticleSystem::ParticleSystem() , _endSpinVar(0) , _spawnAngle(0) , _spawnAngleVar(0) - , _isHsv(false) , _hsv(0, 1, 1) , _hsvVar(0, 0, 0) - , _isOpacityFadeInAllocated(false) , _spawnFadeIn(0) , _spawnFadeInVar(0) - , _isScaleInAllocated(false) , _spawnScaleIn(0) , _spawnScaleInVar(0) , _emissionRate(0) @@ -1831,8 +1830,6 @@ void ParticleSystem::useHSV(bool hsv) if (!hsv) deallocHSVMem(); - - _isHsv = hsv; }; void ParticleSystem::setSpawnFadeIn(float time) diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 66c1e8fe14..eb2e3c9478 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -764,7 +764,7 @@ public: * @param hsv Use HSV color system. */ void useHSV(bool hsv); - bool isHSV() { return _isHsv; }; + bool isHSV() { return _isHSVAllocated; }; /** Gets the hue of each particle. * @@ -1370,8 +1370,6 @@ protected: Color4F _endColor; /** end color variance of each particle */ Color4F _endColorVar; - //* Is the hsv system used or not. - bool _isHsv; /** hsv color of each particle */ HSV _hsv; /** hsv color variance of each particle */ diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index a228724b33..916b7c48f9 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -475,7 +475,7 @@ void ParticleSystemQuad::updateParticleQuads() float* fadeLn = _particleData.opacityFadeInLength; // HSV calculation is expensive, so we should skip it if it's not enabled. - if (_isHsv) + if (_isHSVAllocated) { float* hue = _particleData.hue; float* sat = _particleData.sat; @@ -560,7 +560,7 @@ void ParticleSystemQuad::updateParticleQuads() else { // HSV calculation is expensive, so we should skip it if it's not enabled. - if (_isHsv) + if (_isHSVAllocated) { float* hue = _particleData.hue; float* sat = _particleData.sat; From 5a74146e18096466d6d18b0297337828aa6c8930 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sat, 28 May 2022 20:34:19 +0300 Subject: [PATCH 36/61] Fix HSV Blending bug. --- core/2d/CCParticleSystemQuad.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index 916b7c48f9..4fd57eedcc 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -487,15 +487,17 @@ void ParticleSystemQuad::updateParticleQuads() for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val, ++fadeDt, ++fadeLn) { - float colorR = *r * *a; - float colorG = *g * *a; - float colorB = *b * *a; + float colorR = *r; + float colorG = *g; + float colorB = *b; float colorA = *a * (*fadeDt / *fadeLn); hsv.set(colorR, colorG, colorB, colorA); hsv.h += *hue; - hsv.s = abs(*sat); - hsv.v = abs(*val); - auto col = hsv.toColor4B(); + hsv.s = abs(*sat); + hsv.v = abs(*val); + auto colF = hsv.toColor4F(); + auto col = Color4B(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); quad->bl.colors.set(col.r, col.g, col.b, col.a); quad->br.colors.set(col.r, col.g, col.b, col.a); quad->tl.colors.set(col.r, col.g, col.b, col.a); @@ -572,15 +574,17 @@ void ParticleSystemQuad::updateParticleQuads() for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) { - float colorR = *r * *a; - float colorG = *g * *a; - float colorB = *b * *a; + float colorR = *r; + float colorG = *g; + float colorB = *b; float colorA = *a; hsv.set(colorR, colorG, colorB, colorA); hsv.h += *hue; hsv.s = abs(*sat); hsv.v = abs(*val); - auto col = hsv.toColor4B(); + auto colF = hsv.toColor4F(); + auto col = Color4B(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); quad->bl.colors.set(col.r, col.g, col.b, col.a); quad->br.colors.set(col.r, col.g, col.b, col.a); quad->tl.colors.set(col.r, col.g, col.b, col.a); From b822680e3a0dbd9b252d41742c161a08c0ef06d1 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sat, 28 May 2022 20:36:04 +0300 Subject: [PATCH 37/61] Improve performance. --- core/2d/CCParticleSystemQuad.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index 4fd57eedcc..c86c75a616 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -484,6 +484,7 @@ void ParticleSystemQuad::updateParticleQuads() if (_opacityModifyRGB) { auto hsv = HSV(); + auto col = Color4B(); for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val, ++fadeDt, ++fadeLn) { @@ -496,8 +497,8 @@ void ParticleSystemQuad::updateParticleQuads() hsv.s = abs(*sat); hsv.v = abs(*val); auto colF = hsv.toColor4F(); - auto col = Color4B(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); + col.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); quad->bl.colors.set(col.r, col.g, col.b, col.a); quad->br.colors.set(col.r, col.g, col.b, col.a); quad->tl.colors.set(col.r, col.g, col.b, col.a); @@ -571,6 +572,7 @@ void ParticleSystemQuad::updateParticleQuads() if (_opacityModifyRGB) { auto hsv = HSV(); + auto col = Color4B(); for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) { @@ -583,8 +585,8 @@ void ParticleSystemQuad::updateParticleQuads() hsv.s = abs(*sat); hsv.v = abs(*val); auto colF = hsv.toColor4F(); - auto col = Color4B(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); + col.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); quad->bl.colors.set(col.r, col.g, col.b, col.a); quad->br.colors.set(col.r, col.g, col.b, col.a); quad->tl.colors.set(col.r, col.g, col.b, col.a); From d3d30711f5c5d1a9c9af1d278dd525527a370cbb Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 29 May 2022 08:13:22 +0300 Subject: [PATCH 38/61] Optimize code. --- core/2d/CCParticleSystemQuad.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index c86c75a616..8f7a71d005 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -484,7 +484,6 @@ void ParticleSystemQuad::updateParticleQuads() if (_opacityModifyRGB) { auto hsv = HSV(); - auto col = Color4B(); for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val, ++fadeDt, ++fadeLn) { @@ -497,12 +496,14 @@ void ParticleSystemQuad::updateParticleQuads() hsv.s = abs(*sat); hsv.v = abs(*val); auto colF = hsv.toColor4F(); - col.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); - quad->bl.colors.set(col.r, col.g, col.b, col.a); - quad->br.colors.set(col.r, col.g, col.b, col.a); - quad->tl.colors.set(col.r, col.g, col.b, col.a); - quad->tr.colors.set(col.r, col.g, col.b, col.a); + quad->bl.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); + quad->br.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); + quad->tl.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); + quad->tr.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); } } else @@ -572,7 +573,6 @@ void ParticleSystemQuad::updateParticleQuads() if (_opacityModifyRGB) { auto hsv = HSV(); - auto col = Color4B(); for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) { @@ -585,12 +585,14 @@ void ParticleSystemQuad::updateParticleQuads() hsv.s = abs(*sat); hsv.v = abs(*val); auto colF = hsv.toColor4F(); - col.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); - quad->bl.colors.set(col.r, col.g, col.b, col.a); - quad->br.colors.set(col.r, col.g, col.b, col.a); - quad->tl.colors.set(col.r, col.g, col.b, col.a); - quad->tr.colors.set(col.r, col.g, col.b, col.a); + quad->bl.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); + quad->br.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); + quad->tl.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); + quad->tr.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, + colF.a * 255.0F); } } else From 1640b1bbfa4d902d5f7ba0f6489c10682c1ba774 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 29 May 2022 08:21:08 +0300 Subject: [PATCH 39/61] Oops [skip ci] --- core/2d/CCParticleSystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index eb2e3c9478..b5eb08c3d1 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -1226,7 +1226,7 @@ public: */ virtual float getTimeScale(); - /** Gets the time scale of the particle system. + /** Sets the time scale of the particle system. @param Time scale of the particle system. (default: 1.0) */ virtual void setTimeScale(float scale = 1.0F); From 2ccd40155530fadaf3030c921cd4f04f83cc1b34 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 2 Jun 2022 19:26:02 +0300 Subject: [PATCH 40/61] Add emission shapes support. --- core/2d/CCParticleSystem.cpp | 164 +++++++++++++++++++++++++++++++++-- core/2d/CCParticleSystem.h | 65 ++++++++++++++ 2 files changed, 222 insertions(+), 7 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 8ab0749948..024d82eb3f 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -770,16 +770,87 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe _particleData.timeToLive[i] = MAX(0, particleLife); } - // position - for (int i = start; i < _particleCount; ++i) + if (_isEmissionShapes) { - auto f = RANDOM_KISS(); - _particleData.posx[i] = _sourcePosition.x + _posVar.x * RANDOM_KISS(); - } + for (int i = start; i < _particleCount; ++i) + { + if (_emissionShapes.empty()) + { + _particleData.posx[i] = _sourcePosition.x + _posVar.x * RANDOM_KISS(); + _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_KISS(); + continue; + } - for (int i = start; i < _particleCount; ++i) + auto randElem = abs(RANDOM_KISS()); + auto& shape = _emissionShapes[MIN(randElem * _emissionShapes.size(), _emissionShapes.size() - 1)]; + + switch (shape.type) + { + case EmissionShapeType::POINT: + { + _particleData.posx[i] = _sourcePosition.x + shape.x; + _particleData.posy[i] = _sourcePosition.y + shape.y; + + break; + } + case EmissionShapeType::RECT: + { + _particleData.posx[i] = _sourcePosition.x + shape.x + shape.innerWidth / 2 * RANDOM_KISS(); + _particleData.posy[i] = _sourcePosition.y + shape.y + shape.innerHeight / 2 * RANDOM_KISS(); + + break; + } + case EmissionShapeType::RECTTORUS: + { + float width = (shape.outerWidth - shape.innerWidth) * abs(RANDOM_KISS()) + shape.outerWidth; + float height = (shape.outerHeight - shape.innerHeight) * abs(RANDOM_KISS()) + shape.outerHeight; + width = RANDOM_KISS() < 0.0F ? (width - ((shape.outerWidth - shape.innerWidth) / 2)) * -1 : width - ((shape.outerWidth - shape.innerWidth) / 2); + height = RANDOM_KISS() < 0.0F ? (height - ((shape.outerHeight - shape.innerHeight) / 2)) * -1 : height - ((shape.outerHeight - shape.innerHeight) / 2); + float prob = RANDOM_KISS(); + _particleData.posx[i] = _sourcePosition.x + shape.x + width / 2 * (prob >= 0.0F ? 1.0F : RANDOM_KISS()); + _particleData.posy[i] = _sourcePosition.y + shape.y + height / 2 * (prob < 0.0F ? 1.0F : RANDOM_KISS()); + + break; + } + case EmissionShapeType::CIRCLE: + { + auto val = abs(RANDOM_KISS()) * shape.innerRadius / shape.innerRadius; + val = powf(val, 1 / shape.edgeElasticity); + auto point = Vec2(val * shape.innerRadius * (RANDOM_KISS() < 0.0F ? 1.0F : -1.0F), 0.0F); + point = point.rotateByAngle(Vec2::ZERO, CC_DEGREES_TO_RADIANS(RANDOM_KISS() * 360)); + _particleData.posx[i] = _sourcePosition.x + shape.x + point.x / 2; + _particleData.posy[i] = _sourcePosition.y + shape.y + point.y / 2; + + break; + } + case EmissionShapeType::TORUS: + { + auto val = abs(RANDOM_KISS()) * shape.outerRadius / shape.outerRadius; + val = powf(val, 1 / shape.edgeElasticity); + auto point = Vec2(((val * (shape.outerRadius - shape.innerRadius) + shape.outerRadius) - + (shape.outerRadius - shape.innerRadius)) * + (RANDOM_KISS() < 0.0F ? 1.0F : -1.0F), 0.0F); + point = point.rotateByAngle(Vec2::ZERO, CC_DEGREES_TO_RADIANS(RANDOM_KISS() * 360)); + _particleData.posx[i] = _sourcePosition.x + shape.x + point.x / 2; + _particleData.posy[i] = _sourcePosition.y + shape.y + point.y / 2; + + break; + } + } + } + } + else { - _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_KISS(); + // position + for (int i = start; i < _particleCount; ++i) + { + _particleData.posx[i] = _sourcePosition.x + _posVar.x * RANDOM_KISS(); + } + + for (int i = start; i < _particleCount; ++i) + { + _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_KISS(); + } } if (animationCellIndex != -1 || animationIndex != -1) @@ -1048,6 +1119,85 @@ void ParticleSystem::setAnimationDescriptor(unsigned short indexOfDescriptor, desc.reverseIndices = reverse; } +void ParticleSystem::addEmissionShapePoint(Vec2 pos) +{ + EmissionShape shape{}; + + shape.type = EmissionShapeType::POINT; + + shape.x = pos.x; + shape.y = pos.y; + + _emissionShapes.push_back(shape); +} + +void ParticleSystem::addEmissionShapeRect(Vec2 pos, Size size) +{ + EmissionShape shape{}; + + shape.type = EmissionShapeType::RECT; + + shape.x = pos.x; + shape.y = pos.y; + + shape.innerWidth = size.x; + shape.innerHeight = size.y; + + _emissionShapes.push_back(shape); +} + +void ParticleSystem::addEmissionShapeRectTorus(Vec2 pos, Size innerSize, Size outerSize) +{ + EmissionShape shape{}; + + shape.type = EmissionShapeType::RECTTORUS; + + shape.x = pos.x; + shape.y = pos.y; + + shape.innerWidth = innerSize.x; + shape.innerHeight = innerSize.y; + + shape.outerWidth = outerSize.x; + shape.outerHeight = outerSize.y; + + _emissionShapes.push_back(shape); +} + +void ParticleSystem::addEmissionShapeCircle(Vec2 pos, float radius, float edgeElasticity) +{ + EmissionShape shape{}; + + shape.type = EmissionShapeType::CIRCLE; + + shape.x = pos.x; + shape.y = pos.y; + + shape.innerRadius = radius; + + shape.edgeElasticity = edgeElasticity; + + _emissionShapes.push_back(shape); +} + +void ParticleSystem::addEmissionShapeTorus(Vec2 pos, float innerRadius, float outerRadius, float edgeElasticity) +{ + EmissionShape shape{}; + + shape.type = EmissionShapeType::TORUS; + + shape.x = pos.x; + shape.y = pos.y; + + shape.innerRadius = innerRadius; + + shape.outerRadius = outerRadius; + + shape.edgeElasticity = edgeElasticity; + + _emissionShapes.push_back(shape); +} + void ParticleSystem::setLifeAnimation(bool enabled) { if (enabled && !allocAnimationMem()) diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index b5eb08c3d1..56b32535d2 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -54,6 +54,42 @@ struct particle_point float y; }; +/** + * Particle emission shapes. + * Current supported shapes are Point, Rectangle, RectangularTorus, Circle, Torus + * @since adxe-1.0.0b7 + */ +enum class EmissionShapeType +{ + POINT, + RECT, + RECTTORUS, + CIRCLE, + TORUS, +}; + +/** + * Particle emission shapes. + * Current supported shapes are Point, Rectangle, RectangularTorus, Circle, Torus + * @since adxe-1.0.0b7 + */ +struct EmissionShape +{ + EmissionShapeType type; + + float x; + float y; + + float innerWidth; + float innerHeight; + float outerWidth; + float outerHeight; + + float innerRadius; + float outerRadius; + float edgeElasticity; +}; + /** @struct ParticleAnimationDescriptor Structure that contains animation description */ @@ -1093,6 +1129,29 @@ public: void setAnimationSpeedTimescaleIndependent(bool independent) { _animationTimescaleInd = independent; }; bool isAnimationSpeedTimescaleIndependent() { return _animationTimescaleInd; }; + /** Sets wether to use emission shapes for this particle system or not */ + void setEmissionShapes(bool enabled) { _isEmissionShapes = enabled; } + bool isEmissionShapes() { return _isEmissionShapes; } + + /** Adds an emission shape of type point to the system. */ + void addEmissionShapePoint(Vec2 pos); + + /** Adds an emission shape of type Rectangle to the system. */ + void addEmissionShapeRect(Vec2 pos, Size size); + + /** Adds an emission shape of type Rectangular Torus to the system. */ + void addEmissionShapeRectTorus(Vec2 pos, Size innerSize, Size outerSize); + + /** Adds an emission shape of type Circle to the system. + * @param edgeElasticity If the value is greater than 1.0 then particles will bias towards the edge of the circle more often the greater the value is; If the value is lower than 1.0 then particles will bias towards the center of the circle more often the closer the value is to 0.0; If the value is exactly 1.0 then there will be no bias behaviour. + */ + void addEmissionShapeCircle(Vec2 pos, float radius, float edgeElasticity = 1.0F); + + /** Adds an emission shape of type Torus to the system. + * @param edgeElasticity If the value is greater than 1.0 then particles will bias towards the edge of the torus more often the greater the value is; If the value is lower than 1.0 then particles will bias towards the inner radius of the torus more often the closer the value is to 0.0; If the value is exactly 1.0 then there will be no bias behaviour. + */ + void addEmissionShapeTorus(Vec2 pos, float innerRadius, float outerRadius, float edgeElasticity = 1.0F); + /** Gets the particles movement type: Free or Grouped. @since v0.8 * @@ -1427,6 +1486,12 @@ protected: /** does FlippedY variance of each particle */ int _yCoordFlipped; + /** Wether to use emission shapes for this particle system or not */ + bool _isEmissionShapes; + + /** A vector that contains emission shapes. */ + std::vector _emissionShapes; + /** particles movement type: Free or Grouped @since v0.8 */ From fcbd50daa81f8b83c891126c7f2765833daa578c Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 2 Jun 2022 19:29:19 +0300 Subject: [PATCH 41/61] Add ability to clear emission shapes. --- core/2d/CCParticleSystem.cpp | 5 +++++ core/2d/CCParticleSystem.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 024d82eb3f..685f57b795 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1119,6 +1119,11 @@ void ParticleSystem::setAnimationDescriptor(unsigned short indexOfDescriptor, desc.reverseIndices = reverse; } +void ParticleSystem::resetEmissionShapes() +{ + _emissionShapes.clear(); +} + void ParticleSystem::addEmissionShapePoint(Vec2 pos) { EmissionShape shape{}; diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 56b32535d2..f95e3c0dac 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -1133,6 +1133,9 @@ public: void setEmissionShapes(bool enabled) { _isEmissionShapes = enabled; } bool isEmissionShapes() { return _isEmissionShapes; } + /** Resets the count of emission shapes to 0 and empties the emission shapes array */ + void resetEmissionShapes(); + /** Adds an emission shape of type point to the system. */ void addEmissionShapePoint(Vec2 pos); From 4ab0fbd62de0782b837362eb1410f291dc140db4 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 2 Jun 2022 22:26:26 +0300 Subject: [PATCH 42/61] Fix rectangular torus stretched dimensions. --- core/2d/CCParticleSystem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 685f57b795..f5876dedfc 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -802,10 +802,10 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } case EmissionShapeType::RECTTORUS: { - float width = (shape.outerWidth - shape.innerWidth) * abs(RANDOM_KISS()) + shape.outerWidth; - float height = (shape.outerHeight - shape.innerHeight) * abs(RANDOM_KISS()) + shape.outerHeight; - width = RANDOM_KISS() < 0.0F ? (width - ((shape.outerWidth - shape.innerWidth) / 2)) * -1 : width - ((shape.outerWidth - shape.innerWidth) / 2); - height = RANDOM_KISS() < 0.0F ? (height - ((shape.outerHeight - shape.innerHeight) / 2)) * -1 : height - ((shape.outerHeight - shape.innerHeight) / 2); + float width = (shape.outerWidth - shape.innerWidth) * abs(RANDOM_KISS()) + shape.innerWidth; + float height = (shape.outerHeight - shape.innerHeight) * abs(RANDOM_KISS()) + shape.innerHeight; + width = RANDOM_KISS() < 0.0F ? width * -1 : width; + height = RANDOM_KISS() < 0.0F ? height * -1 : height; float prob = RANDOM_KISS(); _particleData.posx[i] = _sourcePosition.x + shape.x + width / 2 * (prob >= 0.0F ? 1.0F : RANDOM_KISS()); _particleData.posy[i] = _sourcePosition.y + shape.y + height / 2 * (prob < 0.0F ? 1.0F : RANDOM_KISS()); From c54b142e5845a7d6b9a5931dd5c6e7461c48df02 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Mon, 6 Jun 2022 19:23:11 +0300 Subject: [PATCH 43/61] Fix indentations. --- core/2d/CCParticleSystem.cpp | 111 ++++++++-------- core/2d/CCParticleSystem.h | 220 +++++++++++++++++-------------- core/2d/CCParticleSystemQuad.cpp | 43 +++--- 3 files changed, 203 insertions(+), 171 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index f5876dedfc..c444c24c36 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -126,27 +126,27 @@ bool ParticleData::init(int count) { maxCount = count; - posx = (float*)malloc(count * sizeof(float)); - posy = (float*)malloc(count * sizeof(float)); - startPosX = (float*)malloc(count * sizeof(float)); - startPosY = (float*)malloc(count * sizeof(float)); - colorR = (float*)malloc(count * sizeof(float)); - colorG = (float*)malloc(count * sizeof(float)); - colorB = (float*)malloc(count * sizeof(float)); - colorA = (float*)malloc(count * sizeof(float)); - deltaColorR = (float*)malloc(count * sizeof(float)); - deltaColorG = (float*)malloc(count * sizeof(float)); - deltaColorB = (float*)malloc(count * sizeof(float)); - deltaColorA = (float*)malloc(count * sizeof(float)); + posx = (float*)malloc(count * sizeof(float)); + posy = (float*)malloc(count * sizeof(float)); + startPosX = (float*)malloc(count * sizeof(float)); + startPosY = (float*)malloc(count * sizeof(float)); + colorR = (float*)malloc(count * sizeof(float)); + colorG = (float*)malloc(count * sizeof(float)); + colorB = (float*)malloc(count * sizeof(float)); + colorA = (float*)malloc(count * sizeof(float)); + deltaColorR = (float*)malloc(count * sizeof(float)); + deltaColorG = (float*)malloc(count * sizeof(float)); + deltaColorB = (float*)malloc(count * sizeof(float)); + deltaColorA = (float*)malloc(count * sizeof(float)); - size = (float*)malloc(count * sizeof(float)); - deltaSize = (float*)malloc(count * sizeof(float)); - rotation = (float*)malloc(count * sizeof(float)); - staticRotation = (float*)malloc(count * sizeof(float)); - deltaRotation = (float*)malloc(count * sizeof(float)); - totalTimeToLive = (float*)malloc(count * sizeof(float)); - timeToLive = (float*)malloc(count * sizeof(float)); - atlasIndex = (unsigned int*)malloc(count * sizeof(unsigned int)); + size = (float*)malloc(count * sizeof(float)); + deltaSize = (float*)malloc(count * sizeof(float)); + rotation = (float*)malloc(count * sizeof(float)); + staticRotation = (float*)malloc(count * sizeof(float)); + deltaRotation = (float*)malloc(count * sizeof(float)); + totalTimeToLive = (float*)malloc(count * sizeof(float)); + timeToLive = (float*)malloc(count * sizeof(float)); + atlasIndex = (unsigned int*)malloc(count * sizeof(unsigned int)); modeA.dirX = (float*)malloc(count * sizeof(float)); modeA.dirY = (float*)malloc(count * sizeof(float)); @@ -262,7 +262,7 @@ ParticleSystem::ParticleSystem() , _isLoopAnimated(false) , _animIndexCount(0) , _isAnimationReversed(false) - , _undefinedIndexRect({0,0,0,0}) + , _undefinedIndexRect({0, 0, 0, 0}) , _animationTimescaleInd(false) , _yCoordFlipped(1) , _positionType(PositionType::FREE) @@ -325,11 +325,11 @@ bool ParticleSystem::allocAnimationMem() if (!_isAnimAllocated) { _particleData.animTimeLength = (float*)malloc(_totalParticles * sizeof(float)); - _particleData.animTimeDelta = (float*)malloc(_totalParticles * sizeof(float)); - _particleData.animIndex = (unsigned short*)malloc(_totalParticles * sizeof(unsigned short)); - _particleData.animCellIndex = (unsigned short*)malloc(_totalParticles * sizeof(unsigned short)); - if (_particleData.animTimeLength && _particleData.animTimeDelta && - _particleData.animIndex && _particleData.animCellIndex) + _particleData.animTimeDelta = (float*)malloc(_totalParticles * sizeof(float)); + _particleData.animIndex = (unsigned short*)malloc(_totalParticles * sizeof(unsigned short)); + _particleData.animCellIndex = (unsigned short*)malloc(_totalParticles * sizeof(unsigned short)); + if (_particleData.animTimeLength && _particleData.animTimeDelta && _particleData.animIndex && + _particleData.animCellIndex) return _isAnimAllocated = true; else // If any of the above allocations fail, then we safely deallocate the ones that succeeded. @@ -782,7 +782,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } auto randElem = abs(RANDOM_KISS()); - auto& shape = _emissionShapes[MIN(randElem * _emissionShapes.size(), _emissionShapes.size() - 1)]; + auto& shape = _emissionShapes[MIN(randElem * _emissionShapes.size(), _emissionShapes.size() - 1)]; switch (shape.type) { @@ -802,11 +802,11 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } case EmissionShapeType::RECTTORUS: { - float width = (shape.outerWidth - shape.innerWidth) * abs(RANDOM_KISS()) + shape.innerWidth; - float height = (shape.outerHeight - shape.innerHeight) * abs(RANDOM_KISS()) + shape.innerHeight; - width = RANDOM_KISS() < 0.0F ? width * -1 : width; - height = RANDOM_KISS() < 0.0F ? height * -1 : height; - float prob = RANDOM_KISS(); + float width = (shape.outerWidth - shape.innerWidth) * abs(RANDOM_KISS()) + shape.innerWidth; + float height = (shape.outerHeight - shape.innerHeight) * abs(RANDOM_KISS()) + shape.innerHeight; + width = RANDOM_KISS() < 0.0F ? width * -1 : width; + height = RANDOM_KISS() < 0.0F ? height * -1 : height; + float prob = RANDOM_KISS(); _particleData.posx[i] = _sourcePosition.x + shape.x + width / 2 * (prob >= 0.0F ? 1.0F : RANDOM_KISS()); _particleData.posy[i] = _sourcePosition.y + shape.y + height / 2 * (prob < 0.0F ? 1.0F : RANDOM_KISS()); @@ -827,9 +827,10 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { auto val = abs(RANDOM_KISS()) * shape.outerRadius / shape.outerRadius; val = powf(val, 1 / shape.edgeElasticity); - auto point = Vec2(((val * (shape.outerRadius - shape.innerRadius) + shape.outerRadius) - + auto point = Vec2(((val * (shape.outerRadius - shape.innerRadius) + shape.outerRadius) - (shape.outerRadius - shape.innerRadius)) * - (RANDOM_KISS() < 0.0F ? 1.0F : -1.0F), 0.0F); + (RANDOM_KISS() < 0.0F ? 1.0F : -1.0F), + 0.0F); point = point.rotateByAngle(Vec2::ZERO, CC_DEGREES_TO_RADIANS(RANDOM_KISS() * 360)); _particleData.posx[i] = _sourcePosition.x + shape.x + point.x / 2; _particleData.posy[i] = _sourcePosition.y + shape.y + point.y / 2; @@ -907,9 +908,9 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } // color -#define SET_COLOR(c, b, v) \ - for (int i = start; i < _particleCount; ++i) \ - { \ +#define SET_COLOR(c, b, v) \ + for (int i = start; i < _particleCount; ++i) \ + { \ c[i] = clampf(b + v * RANDOM_KISS(), 0, 1); \ } @@ -1035,8 +1036,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe // tangential accel for (int i = start; i < _particleCount; ++i) { - _particleData.modeA.tangentialAccel[i] = - modeA.tangentialAccel + modeA.tangentialAccelVar * RANDOM_KISS(); + _particleData.modeA.tangentialAccel[i] = modeA.tangentialAccel + modeA.tangentialAccelVar * RANDOM_KISS(); } // rotation is dir @@ -1105,7 +1105,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe void ParticleSystem::setAnimationDescriptor(unsigned short indexOfDescriptor, float time, float timeVariance, - const std::vector &indices, + const std::vector& indices, bool reverse) { auto iter = _animations.find(indexOfDescriptor); @@ -1266,16 +1266,14 @@ void ParticleSystem::setAnimationIndicesAtlas() // VERTICAL if (_texture->getPixelsHigh() > _texture->getPixelsWide()) { - setAnimationIndicesAtlas(_texture->getPixelsWide(), - ParticleSystem::TexAnimDir::VERTICAL); + setAnimationIndicesAtlas(_texture->getPixelsWide(), ParticleSystem::TexAnimDir::VERTICAL); return; } // HORIZONTAL if (_texture->getPixelsWide() > _texture->getPixelsHigh()) { - setAnimationIndicesAtlas(_texture->getPixelsHigh(), - ParticleSystem::TexAnimDir::HORIZONTAL); + setAnimationIndicesAtlas(_texture->getPixelsHigh(), ParticleSystem::TexAnimDir::HORIZONTAL); return; } @@ -1287,7 +1285,7 @@ void ParticleSystem::setAnimationIndicesAtlas(unsigned int unifiedCellSize, TexA CCASSERT(unifiedCellSize > 0, "A cell cannot have a size of zero."); resetAnimationIndices(); - + auto texWidth = _texture->getPixelsWide(); auto texHeight = _texture->getPixelsHigh(); @@ -1319,8 +1317,8 @@ void ParticleSystem::setAnimationIndicesAtlas(unsigned int unifiedCellSize, TexA frame.origin.x = unifiedCellSize * i; frame.origin.y = 0; - frame.size.x = unifiedCellSize; - frame.size.y = texHeight; + frame.size.x = unifiedCellSize; + frame.size.y = texHeight; addAnimationIndex(_animIndexCount, frame); } @@ -1342,7 +1340,7 @@ bool ParticleSystem::addAnimationIndex(unsigned short index, std::string_view fr if (frame) return addAnimationIndex(index, frame); return false; - } +} bool ParticleSystem::addAnimationIndex(cocos2d::SpriteFrame* frame) { @@ -1374,12 +1372,12 @@ bool ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::Rect rect, void ParticleSystem::simulate(float seconds, float frameRate) { auto l_updatePaused = _updatePaused; - _updatePaused = false; - seconds = seconds == SIMULATION_USE_PARTICLE_LIFETIME ? - getLife() + getLifeVar() : seconds; - frameRate = frameRate == SIMULATION_USE_GAME_ANIMATION_INTERVAL ? - 1.0F / Director::getInstance()->getAnimationInterval() : frameRate; - auto delta = 1.0F / frameRate; + _updatePaused = false; + seconds = seconds == SIMULATION_USE_PARTICLE_LIFETIME ? getLife() + getLifeVar() : seconds; + frameRate = frameRate == SIMULATION_USE_GAME_ANIMATION_INTERVAL + ? 1.0F / Director::getInstance()->getAnimationInterval() + : frameRate; + auto delta = 1.0F / frameRate; if (seconds > delta) { while (seconds > 0.0F) @@ -1459,8 +1457,11 @@ void ParticleSystem::update(float dt) { _fixedFPSDelta += dt; if (_fixedFPSDelta < 1.0F / _fixedFPS) + { + CC_PROFILER_STOP_CATEGORY(kProfilerCategoryParticles, "CCParticleSystem - update"); return; - dt = _fixedFPSDelta; + } + dt = _fixedFPSDelta; _fixedFPSDelta = 0.0F; } diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index f95e3c0dac..f05a057ad6 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -66,6 +66,8 @@ enum class EmissionShapeType RECTTORUS, CIRCLE, TORUS, + CONE, + CONETORUS, }; /** @@ -95,10 +97,10 @@ Structure that contains animation description */ struct ParticleAnimationDescriptor { - float animationSpeed; - float animationSpeedVariance; + float animationSpeed; + float animationSpeedVariance; std::vector animationIndices; - bool reverseIndices; + bool reverseIndices; }; /** @struct ParticleFrameDescriptor @@ -107,7 +109,7 @@ Structure that contains frame description struct ParticleFrameDescriptor { cocos2d::Rect rect; - bool isRotated; + bool isRotated; }; class CC_DLL ParticleData @@ -228,7 +230,7 @@ public: animCellIndex[p1] = animCellIndex[p2]; } - atlasIndex[p1] = atlasIndex[p2]; + atlasIndex[p1] = atlasIndex[p2]; modeA.dirX[p1] = modeA.dirX[p2]; modeA.dirY[p1] = modeA.dirY[p2]; @@ -318,9 +320,9 @@ public: }; - /** TexAnimDir - Texture animation direction for the particles. - */ + /** TexAnimDir + Texture animation direction for the particles. + */ enum class TexAnimDir { VERTICAL, /** texture coordinates are read top to bottom within the texture */ @@ -913,53 +915,61 @@ public: void setSpawnAngleVar(float angle) { _spawnAngleVar = angle; } /** Gets the spawn opacity fade in time of each particle. - * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a specified time. + * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a + specified time. * @return The spawn opacity fade in time in seconds. */ float getSpawnFadeIn() { return _spawnFadeIn; } /** Sets the spawn opacity fade in time of each particle when it's created. - * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a specified time. - * + * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a + * specified time. + * * @param time The spawn opacity fade in time in seconds. */ void setSpawnFadeIn(float time); /** Gets the spawn opacity fade in time variance of each particle. - * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a specified time. - * + * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a + * specified time. + * * @return The spawn opacity fade in time variance in seconds. */ float getSpawnFadeInVar() { return _spawnFadeInVar; } /** Sets the spawn opacity fade in time variance of each particle when it's created. - * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a specified time. - * + * Particles have the ability to spawn while having 0 opacity and gradually start going to 255 opacity with a + * specified time. + * * @param time The spawn opacity fade in time variance in seconds. */ void setSpawnFadeInVar(float time); /** Gets the spawn opacity fade in time of each particle. - * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified time. - * + * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified + * time. + * * @return The spawn opacity fade in time in seconds. */ float getSpawnScaleIn() { return _spawnScaleIn; } /** Sets the spawn opacity fade in time of each particle when it's created. - * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified time. - * + * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified + * time. + * * @param time The spawn opacity fade in time in seconds. */ void setSpawnScaleIn(float time); /** Gets the spawn opacity fade in time variance of each particle. - * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified time. - * + * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified + * time. + * * @return The spawn opacity fade in time variance in seconds. */ float getSpawnScaleInVar() { return _spawnScaleInVar; } /** Sets the spawn opacity fade in time variance of each particle when it's created. - * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified time. - * + * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified + * time. + * * @param time The spawn opacity fade in time variance in seconds. */ void setSpawnScaleInVar(float time); @@ -996,17 +1006,18 @@ public: /** Enables or disables tex coord animations that are set by the emitter randomly when a particle is emitted. */ void setEmitterAnimation(bool enabled); - /** Enables or disables tex coord animations that are used to make particles play a sequence forever until they die */ + /** Enables or disables tex coord animations that are used to make particles play a sequence forever until they die + */ void setLoopAnimation(bool enabled); bool isLifeAnimated() { return _isLifeAnimated; } bool isEmitterAnimated() { return _isEmitterAnimated; } bool isLoopAnimated() { return _isLoopAnimated; } - /** Gets the total number of indices. - * - * @return The size of the list holding animation indices. - */ + /** Gets the total number of indices. + * + * @return The size of the list holding animation indices. + */ int getTotalAnimationIndices() { return _animIndexCount; } /** Sets wether to start from first cell and go forwards (normal) or last cell and go backwards (reversed) */ @@ -1020,96 +1031,100 @@ public: void resetAnimationDescriptors(); /** Choose what animation descriptors are to be selected at random for particles. - * This function should be called after you've inserted/overwritten any animation descriptors. - * - * @param animations Array of specific indices of animations to play at random - */ - void setMultiAnimationRandomSpecific(const std::vector &animations) { _randomAnimations = animations; }; + * This function should be called after you've inserted/overwritten any animation descriptors. + * + * @param animations Array of specific indices of animations to play at random + */ + void setMultiAnimationRandomSpecific(const std::vector& animations) + { + _randomAnimations = animations; + }; /** Choose ALL animation descriptors to be selected at random for particles. - * This function should be called after you've inserted/overwritten any animation descriptors. - */ + * This function should be called after you've inserted/overwritten any animation descriptors. + */ void setMultiAnimationRandom(); /** Add all particle animation indices based on cells size and direction spicified using a texture atlas. - * will erase the array and add new indices from the atlas. - * This function will automatically figure out your atlas cell size and direction for you! thank her later :) */ + * will erase the array and add new indices from the atlas. + * This function will automatically figure out your atlas cell size and direction for you! thank her later :) */ void setAnimationIndicesAtlas(); - /** Add all particle animation indices based on cell size and direction spicified if the method of rendering preferred is texture atlas. - * will erase the array and add new indices from the atlas. - * - * @param unifiedCellSize The size of cell unified. - * @param direction What direction is the atlas - */ + /** Add all particle animation indices based on cell size and direction spicified if the method of rendering + * preferred is texture atlas. will erase the array and add new indices from the atlas. + * + * @param unifiedCellSize The size of cell unified. + * @param direction What direction is the atlas + */ void setAnimationIndicesAtlas(unsigned int unifiedCellSize, TexAnimDir direction = TexAnimDir::HORIZONTAL); /** Add a particle animation index based on tex coords spicified using a sprite frame. - * The index is automatically incremented on each addition. - * - * @param frameName SpriteFrame name to search for - * - * @return Returns true of the index was successfully found and added. Otherwise, false - */ + * The index is automatically incremented on each addition. + * + * @param frameName SpriteFrame name to search for + * + * @return Returns true of the index was successfully found and added. Otherwise, false + */ bool addAnimationIndex(std::string_view frameName); /** Add a particle animation index based on tex coords spicified using a sprite frame. - * - * @param index Index id to add the frame to or override it with the new frame - * @param frameName SpriteFrame name to search for - * - * @return Returns true of the index was successfully found and added. Otherwise, false - */ + * + * @param index Index id to add the frame to or override it with the new frame + * @param frameName SpriteFrame name to search for + * + * @return Returns true of the index was successfully found and added. Otherwise, false + */ bool addAnimationIndex(unsigned short index, std::string_view frameName); /** Add a particle animation index based on tex coords spicified using a sprite frame. - * The index is automatically incremented on each addition. - * - * @param frame SpriteFrame containting data about tex coords - * - * @return Returns true of the index was successfully found and added. Otherwise, false - */ + * The index is automatically incremented on each addition. + * + * @param frame SpriteFrame containting data about tex coords + * + * @return Returns true of the index was successfully found and added. Otherwise, false + */ bool addAnimationIndex(cocos2d::SpriteFrame* frame); /** Add a particle animation index based on tex coords spicified using a sprite frame. - * you can specify which index you want to override in this function - * - * @param index Index id to add the frame to or override it with the new frame - * @param frame SpriteFrame containting data about tex coords - * - * @return Returns true of the index was successfully found and added. Otherwise, false - */ + * you can specify which index you want to override in this function + * + * @param index Index id to add the frame to or override it with the new frame + * @param frame SpriteFrame containting data about tex coords + * + * @return Returns true of the index was successfully found and added. Otherwise, false + */ bool addAnimationIndex(unsigned short index, cocos2d::SpriteFrame* frame); /** Add a particle animation index based on tex coords spicified. - * you can specify which index you want to override in this function - * - * @param index Index id to add the frame to or override it with the new rect - * @param rect Rect containting data about tex coords in pixels - * @param rotated Not implemented. - * - * @return Returns true of the index was successfully found and added. Otherwise, false - */ + * you can specify which index you want to override in this function + * + * @param index Index id to add the frame to or override it with the new rect + * @param rect Rect containting data about tex coords in pixels + * @param rotated Not implemented. + * + * @return Returns true of the index was successfully found and added. Otherwise, false + */ bool addAnimationIndex(unsigned short index, cocos2d::Rect rect, bool rotated = false); /** You can specify what rect is used if an index in an animation descriptor wasn't found. - * - * @param rect Rect containting data about tex coords in pixels - */ + * + * @param rect Rect containting data about tex coords in pixels + */ void setRectForUndefinedIndices(cocos2d::Rect rect) { _undefinedIndexRect = rect; }; /** Add a particle animation descriptor with an index. - * - * @param indexOfDescriptor Index of the animation to be added, adding to the same index will just override the pervious animation descriptor - * @param time length of the animation in seconds - * @param timeVariance Time randomly selected for each different particle added on the animation length - * @param indices An array of the indicies - * @param reverse Should the animation indicies be played backwards? (default: false) - */ + * + * @param indexOfDescriptor Index of the animation to be added, adding to the same index will just override the + * pervious animation descriptor + * @param time length of the animation in seconds + * @param timeVariance Time randomly selected for each different particle added on the animation length + * @param indices An array of the indicies + * @param reverse Should the animation indicies be played backwards? (default: false) + */ void setAnimationDescriptor(unsigned short indexOfDescriptor, float time, float timeVariance, - const std::vector &indices, + const std::vector& indices, bool reverse = false); /** Add a particle animation descriptor with the index 0. @@ -1117,7 +1132,7 @@ public: * @param indices An array of the indicies * @param reverse Should the animation indicies be played backwards? (default: false) */ - void setAnimationDescriptor(const std::vector &indices, bool reverse = false) + void setAnimationDescriptor(const std::vector& indices, bool reverse = false) { setAnimationDescriptor(0, 0, 0, indices, reverse); }; @@ -1146,13 +1161,19 @@ public: void addEmissionShapeRectTorus(Vec2 pos, Size innerSize, Size outerSize); /** Adds an emission shape of type Circle to the system. - * @param edgeElasticity If the value is greater than 1.0 then particles will bias towards the edge of the circle more often the greater the value is; If the value is lower than 1.0 then particles will bias towards the center of the circle more often the closer the value is to 0.0; If the value is exactly 1.0 then there will be no bias behaviour. - */ + * @param edgeElasticity If the value is greater than 1.0 then particles will bias towards the edge of the circle + * more often the greater the value is; If the value is lower than 1.0 then particles will bias towards the center + * of the circle more often the closer the value is to 0.0; If the value is exactly 1.0 then there will be no bias + * behaviour. + */ void addEmissionShapeCircle(Vec2 pos, float radius, float edgeElasticity = 1.0F); /** Adds an emission shape of type Torus to the system. - * @param edgeElasticity If the value is greater than 1.0 then particles will bias towards the edge of the torus more often the greater the value is; If the value is lower than 1.0 then particles will bias towards the inner radius of the torus more often the closer the value is to 0.0; If the value is exactly 1.0 then there will be no bias behaviour. - */ + * @param edgeElasticity If the value is greater than 1.0 then particles will bias towards the edge of the torus + * more often the greater the value is; If the value is lower than 1.0 then particles will bias towards the inner + * radius of the torus more often the closer the value is to 0.0; If the value is exactly 1.0 then there will be no + * bias behaviour. + */ void addEmissionShapeTorus(Vec2 pos, float innerRadius, float outerRadius, float edgeElasticity = 1.0F); /** Gets the particles movement type: Free or Grouped. @@ -1171,16 +1192,20 @@ public: /** Advance the particle system and make it seem like it ran for this many seconds. * * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) - * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more accurate the simulation will be at the cost of performance. value of -1 means (SIMULATION_USE_GAME_ANIMATION_INTERVAL) + * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more + * accurate the simulation will be at the cost of performance. value of -1 means + * (SIMULATION_USE_GAME_ANIMATION_INTERVAL) */ - void simulate(float seconds = SIMULATION_USE_PARTICLE_LIFETIME, - float frameRate = SIMULATION_USE_GAME_ANIMATION_INTERVAL); + void simulate(float seconds = SIMULATION_USE_PARTICLE_LIFETIME, + float frameRate = SIMULATION_USE_GAME_ANIMATION_INTERVAL); /** Resets the particle system and then advances the particle system and make it seem like it ran for this many * seconds. The frame rate used for simulation accuracy is the screens refresh rate. * * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) - * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more accurate the simulation will be at the cost of performance. value of -1 means (SIMULATION_USE_GAME_ANIMATION_INTERVAL) + * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more + * accurate the simulation will be at the cost of performance. value of -1 means + * (SIMULATION_USE_GAME_ANIMATION_INTERVAL) */ void resimulate(float seconds = SIMULATION_USE_PARTICLE_LIFETIME, float frameRate = SIMULATION_USE_GAME_ANIMATION_INTERVAL); @@ -1279,6 +1304,9 @@ public: virtual float getFixedFPS(); /** Sets the fixed frame rate count of the particle system. + * i.e. if the framerate is set to 30.0 while the refresh rate is greater than 30.0 then the particle system will + wait until it hits the 30.0 FPS mark. + * This is usefull for increasing performance or for creating old-school effects with it. @param Fixed frame rate count of the particle system. (default: 0.0) */ virtual void setFixedFPS(float frameRate = 0.0F); diff --git a/core/2d/CCParticleSystemQuad.cpp b/core/2d/CCParticleSystemQuad.cpp index 8f7a71d005..9326319b8d 100644 --- a/core/2d/CCParticleSystemQuad.cpp +++ b/core/2d/CCParticleSystemQuad.cpp @@ -106,7 +106,9 @@ ParticleSystemQuad* ParticleSystemQuad::create(std::string_view filename) ParticleSystemQuad* ParticleSystemQuad::createWithTotalParticles(int numberOfParticles) { - CCASSERT(numberOfParticles <= 10000, "Adding more than 10000 particles will crash the renderer, the mesh generated has an index format of U_SHORT (uint16_t)"); + CCASSERT(numberOfParticles <= 10000, + "Adding more than 10000 particles will crash the renderer, the mesh generated has an index format of " + "U_SHORT (uint16_t)"); ParticleSystemQuad* ret = new ParticleSystemQuad(); if (ret->initWithTotalParticles(numberOfParticles)) @@ -293,7 +295,7 @@ inline void updatePosWithParticle(V3F_C4B_T2F_Quad* quad, float x = newPosition.x; float y = newPosition.y; - float r = (float)-CC_DEGREES_TO_RADIANS(rotation + staticRotation); + float r = (float)-CC_DEGREES_TO_RADIANS(rotation + staticRotation); float cr = cosf(r); float sr = sinf(r); float ax = x1 * cr - y1 * sr + x; @@ -359,19 +361,20 @@ void ParticleSystemQuad::updateParticleQuads() worldToNodeTM.transformPoint(&p1); Vec3 p2; Vec2 newPos; - float* startX = _particleData.startPosX; - float* startY = _particleData.startPosY; - float* x = _particleData.posx; - float* y = _particleData.posy; - float* s = _particleData.size; - float* r = _particleData.rotation; - float* sr = _particleData.staticRotation; - float* sid = _particleData.scaleInDelta; - float* sil = _particleData.scaleInLength; + float* startX = _particleData.startPosX; + float* startY = _particleData.startPosY; + float* x = _particleData.posx; + float* y = _particleData.posy; + float* s = _particleData.size; + float* r = _particleData.rotation; + float* sr = _particleData.staticRotation; + float* sid = _particleData.scaleInDelta; + float* sil = _particleData.scaleInLength; V3F_C4B_T2F_Quad* quadStart = startQuad; if (_isScaleInAllocated) { - for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr, ++sid, ++sil) + for (int i = 0; i < _particleCount; + ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr, ++sid, ++sil) { p2.set(*startX, *startY, 0); worldToNodeTM.transformPoint(&p2); @@ -411,7 +414,8 @@ void ParticleSystemQuad::updateParticleQuads() V3F_C4B_T2F_Quad* quadStart = startQuad; if (_isScaleInAllocated) { - for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr, ++sid, ++sil) + for (int i = 0; i < _particleCount; + ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr, ++sid, ++sil) { newPos.set(*x, *y); newPos.x = *x - (currentPosition.x - *startX); @@ -447,7 +451,8 @@ void ParticleSystemQuad::updateParticleQuads() V3F_C4B_T2F_Quad* quadStart = startQuad; if (_isScaleInAllocated) { - for (int i = 0; i < _particleCount; ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr, ++sid, ++sil) + for (int i = 0; i < _particleCount; + ++i, ++startX, ++startY, ++x, ++y, ++quadStart, ++s, ++r, ++sr, ++sid, ++sil) { newPos.set(*x + pos.x, *y + pos.y); updatePosWithParticle(quadStart, newPos, *s, tweenfunc::expoEaseOut(*sid / *sil), *r, *sr); @@ -573,8 +578,7 @@ void ParticleSystemQuad::updateParticleQuads() if (_opacityModifyRGB) { auto hsv = HSV(); - for (int i = 0; i < _particleCount; - ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) { float colorR = *r; float colorG = *g; @@ -582,8 +586,8 @@ void ParticleSystemQuad::updateParticleQuads() float colorA = *a; hsv.set(colorR, colorG, colorB, colorA); hsv.h += *hue; - hsv.s = abs(*sat); - hsv.v = abs(*val); + hsv.s = abs(*sat); + hsv.v = abs(*val); auto colF = hsv.toColor4F(); quad->bl.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, colF.a * 255.0F); @@ -598,8 +602,7 @@ void ParticleSystemQuad::updateParticleQuads() else { auto hsv = HSV(); - for (int i = 0; i < _particleCount; - ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) + for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++hue, ++sat, ++val) { float colorR = *r; float colorG = *g; From 6b133b68a22659ca75f0857e520aab7a01617bff Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sat, 11 Jun 2022 18:06:43 +0300 Subject: [PATCH 44/61] Improve emission shapes workflow. `ps->addEmissionShape(ParticleSystem::createConeShape({ -2000, 0 }, 400, 90, 50));` is the new workflow. --- core/2d/CCParticleSystem.cpp | 109 +++++++++++++++++++++++++------ core/2d/CCParticleSystem.h | 123 ++++++++++++++++++++++++++++------- 2 files changed, 187 insertions(+), 45 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index c444c24c36..43fcc4c20e 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -265,6 +265,8 @@ ParticleSystem::ParticleSystem() , _undefinedIndexRect({0, 0, 0, 0}) , _animationTimescaleInd(false) , _yCoordFlipped(1) + , _isEmissionShapes(false) + , _emissionShapeIndex(0) , _positionType(PositionType::FREE) , _paused(false) , _updatePaused(false) @@ -816,8 +818,8 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { auto val = abs(RANDOM_KISS()) * shape.innerRadius / shape.innerRadius; val = powf(val, 1 / shape.edgeElasticity); - auto point = Vec2(val * shape.innerRadius * (RANDOM_KISS() < 0.0F ? 1.0F : -1.0F), 0.0F); - point = point.rotateByAngle(Vec2::ZERO, CC_DEGREES_TO_RADIANS(RANDOM_KISS() * 360)); + auto point = Vec2(0.0F, val * shape.innerRadius); + point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(shape.coneOffset + shape.coneAngle / 2 * RANDOM_KISS())); _particleData.posx[i] = _sourcePosition.x + shape.x + point.x / 2; _particleData.posy[i] = _sourcePosition.y + shape.y + point.y / 2; @@ -827,11 +829,8 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { auto val = abs(RANDOM_KISS()) * shape.outerRadius / shape.outerRadius; val = powf(val, 1 / shape.edgeElasticity); - auto point = Vec2(((val * (shape.outerRadius - shape.innerRadius) + shape.outerRadius) - - (shape.outerRadius - shape.innerRadius)) * - (RANDOM_KISS() < 0.0F ? 1.0F : -1.0F), - 0.0F); - point = point.rotateByAngle(Vec2::ZERO, CC_DEGREES_TO_RADIANS(RANDOM_KISS() * 360)); + auto point = Vec2(0.0F, ((val * (shape.outerRadius - shape.innerRadius) + shape.outerRadius) - (shape.outerRadius - shape.innerRadius))); + point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(shape.coneOffset + shape.coneAngle / 2 * RANDOM_KISS())); _particleData.posx[i] = _sourcePosition.x + shape.x + point.x / 2; _particleData.posy[i] = _sourcePosition.y + shape.y + point.y / 2; @@ -1121,10 +1120,28 @@ void ParticleSystem::setAnimationDescriptor(unsigned short indexOfDescriptor, void ParticleSystem::resetEmissionShapes() { + _emissionShapeIndex = 0; _emissionShapes.clear(); } -void ParticleSystem::addEmissionShapePoint(Vec2 pos) +void ParticleSystem::addEmissionShape(EmissionShape shape) +{ + setEmissionShape(_emissionShapeIndex, shape); +} + +void ParticleSystem::setEmissionShape(unsigned short index, EmissionShape shape) +{ + auto iter = _emissionShapes.find(index); + if (iter == _emissionShapes.end()) + { + iter = _emissionShapes.emplace(index, EmissionShape{}).first; + _emissionShapeIndex++; + } + + iter->second = shape; +} + +EmissionShape ParticleSystem::createPointShape(Vec2 pos) { EmissionShape shape{}; @@ -1133,10 +1150,10 @@ void ParticleSystem::addEmissionShapePoint(Vec2 pos) shape.x = pos.x; shape.y = pos.y; - _emissionShapes.push_back(shape); + return shape; } -void ParticleSystem::addEmissionShapeRect(Vec2 pos, Size size) +EmissionShape ParticleSystem::createRectShape(Vec2 pos, Size size) { EmissionShape shape{}; @@ -1148,10 +1165,10 @@ void ParticleSystem::addEmissionShapeRect(Vec2 pos, Size size) shape.innerWidth = size.x; shape.innerHeight = size.y; - _emissionShapes.push_back(shape); + return shape; } -void ParticleSystem::addEmissionShapeRectTorus(Vec2 pos, Size innerSize, Size outerSize) +EmissionShape ParticleSystem::createRectTorusShape(Vec2 pos, Size innerSize, Size outerSize) { EmissionShape shape{}; @@ -1166,10 +1183,10 @@ void ParticleSystem::addEmissionShapeRectTorus(Vec2 pos, Size innerSize, Size ou shape.outerWidth = outerSize.x; shape.outerHeight = outerSize.y; - _emissionShapes.push_back(shape); + return shape; } -void ParticleSystem::addEmissionShapeCircle(Vec2 pos, float radius, float edgeElasticity) +EmissionShape ParticleSystem::createCircleShape(Vec2 pos, float radius, float edgeElasticity) { EmissionShape shape{}; @@ -1180,12 +1197,34 @@ void ParticleSystem::addEmissionShapeCircle(Vec2 pos, float radius, float edgeEl shape.innerRadius = radius; + shape.coneOffset = 0; + shape.coneAngle = 360; + shape.edgeElasticity = edgeElasticity; - _emissionShapes.push_back(shape); + return shape; } -void ParticleSystem::addEmissionShapeTorus(Vec2 pos, float innerRadius, float outerRadius, float edgeElasticity) +EmissionShape ParticleSystem::createConeShape(Vec2 pos, float radius, float offset, float angle, float edgeBias) +{ + EmissionShape shape{}; + + shape.type = EmissionShapeType::CIRCLE; + + shape.x = pos.x; + shape.y = pos.y; + + shape.innerRadius = radius; + + shape.coneOffset = offset; + shape.coneAngle = angle; + + shape.edgeElasticity = edgeBias; + + return shape; +} + +EmissionShape ParticleSystem::createTorusShape(Vec2 pos, float innerRadius, float outerRadius, float edgeBias) { EmissionShape shape{}; @@ -1195,12 +1234,39 @@ void ParticleSystem::addEmissionShapeTorus(Vec2 pos, float innerRadius, float ou shape.y = pos.y; shape.innerRadius = innerRadius; - shape.outerRadius = outerRadius; - shape.edgeElasticity = edgeElasticity; + shape.coneOffset = 0; + shape.coneAngle = 360; - _emissionShapes.push_back(shape); + shape.edgeElasticity = edgeBias; + + return shape; +} + +EmissionShape ParticleSystem::createConeTorusShape(Vec2 pos, + float innerRadius, + float outerRadius, + float offset, + float angle, + float edgeBias) +{ + EmissionShape shape{}; + + shape.type = EmissionShapeType::TORUS; + + shape.x = pos.x; + shape.y = pos.y; + + shape.innerRadius = innerRadius; + shape.outerRadius = outerRadius; + + shape.coneOffset = offset; + shape.coneAngle = angle; + + shape.edgeElasticity = edgeBias; + + return shape; } void ParticleSystem::setLifeAnimation(bool enabled) @@ -1358,14 +1424,15 @@ bool ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::Rect rect, { auto iter = _animationIndices.find(index); if (iter == _animationIndices.end()) + { iter = _animationIndices.emplace(index, ParticleFrameDescriptor{}).first; + _animIndexCount++; + } auto& desc = iter->second; desc.rect = rect; desc.isRotated = rotated; - ++_animIndexCount; - return true; } diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index f05a057ad6..7a26cb6e93 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -65,14 +65,12 @@ enum class EmissionShapeType RECT, RECTTORUS, CIRCLE, - TORUS, - CONE, - CONETORUS, + TORUS }; /** * Particle emission shapes. - * Current supported shapes are Point, Rectangle, RectangularTorus, Circle, Torus + * Current supported shapes are Point, Rectangle, RectangularTorus, Circle, Torus, Cone, Cone Torus * @since adxe-1.0.0b7 */ struct EmissionShape @@ -89,6 +87,8 @@ struct EmissionShape float innerRadius; float outerRadius; + float coneOffset; + float coneAngle; float edgeElasticity; }; @@ -1151,30 +1151,103 @@ public: /** Resets the count of emission shapes to 0 and empties the emission shapes array */ void resetEmissionShapes(); - /** Adds an emission shape of type point to the system. */ - void addEmissionShapePoint(Vec2 pos); + /** Adds an emission shape to the system. + * The index is automatically incremented on each addition. + * + * @param shape Shape descriptor object. + */ + void addEmissionShape(EmissionShape shape); - /** Adds an emission shape of type Rectangle to the system. */ - void addEmissionShapeRect(Vec2 pos, Size size); + /** Updates an existing emission shape or adds it. + * @param index index of the shape descriptor. + * @param shape Shape descriptor object. + */ + void setEmissionShape(unsigned short index, EmissionShape shape); - /** Adds an emission shape of type Rectangular Torus to the system. */ - void addEmissionShapeRectTorus(Vec2 pos, Size innerSize, Size outerSize); + /** Adds an emission shape of type point to the system. + * @param pos Position of the emission shape in local space. + */ + static EmissionShape createPointShape(Vec2 pos); + + /** Adds an emission shape of type Rectangle to the system. + * @param pos Position of the emission shape in local space. + * @param size Size of the rectangle. + */ + static EmissionShape createRectShape(Vec2 pos, Size size); + + /** Adds an emission shape of type Rectangular Torus to the system. + * @param pos Position of the emission shape in local space. + * @param innerSize Inner size offset of the rectangle. + * @param outerSize Outer size of the rectangle. + */ + static EmissionShape createRectTorusShape(Vec2 pos, Size innerSize, Size outerSize); /** Adds an emission shape of type Circle to the system. - * @param edgeElasticity If the value is greater than 1.0 then particles will bias towards the edge of the circle - * more often the greater the value is; If the value is lower than 1.0 then particles will bias towards the center - * of the circle more often the closer the value is to 0.0; If the value is exactly 1.0 then there will be no bias - * behaviour. + * + * The default angle offset of the circle is 0 and the default angle of the circle is 360 + * + * @param pos Position of the emission shape in local space. + * @param radius Radius of the circle. + * @param edgeBias circle edge center bias value, If the value is greater than 1.0 then particles will bias + * towards the edge of the circle more often the greater the value is; If the value is lower than 1.0 then particles + * will bias towards the center of the circle more often the closer the value is to 0.0; If the value is exactly 1.0 + * then there will be no bias behaviour. */ - void addEmissionShapeCircle(Vec2 pos, float radius, float edgeElasticity = 1.0F); + static EmissionShape createCircleShape(Vec2 pos, float radius, float edgeBias = 1.0F); + + /** Adds an emission shape of type Cone to the system. + * + * The default angle offset of the circle is 0 and the default angle of the circle is 360 + * + * @param pos Position of the emission shape in local space. + * @param radius Radius of the circle. + * @param offset Cone offset angle in degrees. + * @param angle Cone angle in degrees. + * @param edgeBias circle edge center bias value, If the value is greater than 1.0 then particles will bias + * towards the edge of the circle more often the greater the value is; If the value is lower than 1.0 then particles + * will bias towards the center of the circle more often the closer the value is to 0.0; If the value is exactly 1.0 + * then there will be no bias behaviour. + */ + static EmissionShape createConeShape(Vec2 pos, + float radius, + float offset, + float angle, + float edgeBias = 1.0F); /** Adds an emission shape of type Torus to the system. - * @param edgeElasticity If the value is greater than 1.0 then particles will bias towards the edge of the torus - * more often the greater the value is; If the value is lower than 1.0 then particles will bias towards the inner - * radius of the torus more often the closer the value is to 0.0; If the value is exactly 1.0 then there will be no - * bias behaviour. + * + * The default angle offset of the torus is 0 and the default angle of the torus is 360 + * + * @param pos Position of the emission shape in local space. + * @param innerRadius Inner radius offset of the torus. + * @param outerRadius Outer radius of the torus. + * @param edgeBias torus edge center bias value, If the value is greater than 1.0 then particles will bias + * towards the edge of the torus more often the greater the value is; If the value is lower than 1.0 then particles + * will bias towards the center of the torus more often the closer the value is to 0.0; If the value is exactly 1.0 + * then there will be no bias behaviour. */ - void addEmissionShapeTorus(Vec2 pos, float innerRadius, float outerRadius, float edgeElasticity = 1.0F); + static EmissionShape createTorusShape(Vec2 pos, float innerRadius, float outerRadius, float edgeBias = 1.0F); + + /** Adds an emission shape of type Torus to the system. + * + * The default angle offset of the torus is 0 and the default angle of the torus is 360 + * + * @param pos Position of the emission shape in local space. + * @param innerRadius Inner radius offset of the torus. + * @param outerRadius Outer radius of the torus. + * @param offset Cone offset angle in degrees. + * @param angle Cone angle in degrees. + * @param edgeBias torus edge center bias value, If the value is greater than 1.0 then particles will bias + * towards the edge of the torus more often the greater the value is; If the value is lower than 1.0 then particles + * will bias towards the center of the torus more often the closer the value is to 0.0; If the value is exactly 1.0 + * then there will be no bias behaviour. + */ + static EmissionShape createConeTorusShape(Vec2 pos, + float innerRadius, + float outerRadius, + float offset, + float angle, + float edgeBias = 1.0F); /** Gets the particles movement type: Free or Grouped. @since v0.8 @@ -1200,7 +1273,7 @@ public: float frameRate = SIMULATION_USE_GAME_ANIMATION_INTERVAL); /** Resets the particle system and then advances the particle system and make it seem like it ran for this many - * seconds. The frame rate used for simulation accuracy is the screens refresh rate. + * seconds. The frame rate used for simulation accuracy is the director's animation interval. * * @param seconds Seconds to advance. value of -1 means (SIMULATION_USE_PARTICLE_LIFETIME) * @param frameRate Frame rate to run the simulation with (preferred: 30.0) The higher this value is the more @@ -1288,7 +1361,7 @@ public: virtual void resumeEmissions(); /** Is system update paused - @return True if the emissions are paused, else false + @return True if system update is paused, else false */ virtual bool isUpdatePaused() const; @@ -1520,8 +1593,10 @@ protected: /** Wether to use emission shapes for this particle system or not */ bool _isEmissionShapes; - /** A vector that contains emission shapes. */ - std::vector _emissionShapes; + /** variable keeping count of emission shapes added */ + int _emissionShapeIndex; + /** A map that stores emission shapes that are choosen at random */ + std::unordered_map _emissionShapes; /** particles movement type: Free or Grouped @since v0.8 From 4d3f7ab92dd50656460f0db21ca062f84f69d2e4 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 12 Jun 2022 00:58:01 +0300 Subject: [PATCH 45/61] Add emission mask support. --- core/2d/CCParticleSystem.cpp | 135 ++++++++++++++++++++++++++++++++++- core/2d/CCParticleSystem.h | 46 +++++++++++- 2 files changed, 178 insertions(+), 3 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 43fcc4c20e..15ae448c11 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -836,6 +836,30 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe break; } + case EmissionShapeType::ALPHA_MASK: + { + Vec2 pos = {shape.x, shape.y}; + Vec2 size = shape.mask.size; + Vec2 overrideSize = {shape.innerWidth, shape.innerHeight}; + Vec2 scale = {shape.outerWidth, shape.outerHeight}; + float angle = shape.coneOffset; + + if (overrideSize.isZero()) + overrideSize = shape.mask.size; + + Vec2 point = {0, 0}; + + { + int rand0 = abs(RANDOM_KISS() * shape.mask.points.size()); + int index = MIN(rand0, shape.mask.points.size() - 1); + point = shape.mask.points[index]; + } + + _particleData.posx[i] = _sourcePosition.x + shape.x + point.x; + _particleData.posy[i] = _sourcePosition.y + shape.y + point.y; + + break; + } } } } @@ -1141,6 +1165,32 @@ void ParticleSystem::setEmissionShape(unsigned short index, EmissionShape shape) iter->second = shape; } +EmissionShape ParticleSystem::createMaskShape(std::string_view maskName, + Vec2 pos, + Vec2 overrideSize, + Vec2 scale, + float angle) +{ + EmissionShape shape{}; + + shape.type = EmissionShapeType::ALPHA_MASK; + + shape.mask = ParticleEmissionMaskCache::getInstance()->getEmissionMask(maskName); + + shape.x = pos.x; + shape.y = pos.y; + + shape.innerWidth = overrideSize.x; + shape.innerHeight = overrideSize.y; + + shape.outerWidth = scale.x; + shape.outerHeight = scale.y; + + shape.coneOffset = angle; + + return shape; +} + EmissionShape ParticleSystem::createPointShape(Vec2 pos) { EmissionShape shape{}; @@ -2232,4 +2282,87 @@ void ParticleSystem::setTimeScale(float scale) _timeScale = scale; } -NS_CC_END +static ParticleEmissionMaskCache* emissionMaskCache; + +ParticleEmissionMaskCache* ParticleEmissionMaskCache::getInstance() +{ + if (emissionMaskCache == nullptr) + { + emissionMaskCache = new ParticleEmissionMaskCache(); + return emissionMaskCache; + } + return emissionMaskCache; +} + +void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, + std::string_view texturePath, + float alphaThreshold, + bool inverted) +{ + auto img = new Image(); + img->Image::initWithImageFile(texturePath); + img->autorelease(); + + CCASSERT(img, "image texture was nullptr."); + bakeEmissionMask(maskName, img, alphaThreshold, inverted); +} + +void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, + Image* imageTexture, + float alphaThreshold, + bool inverted) +{ + auto img = imageTexture; + CCASSERT(img, "image texture was nullptr."); + CCASSERT(img->hasAlpha(), "image data should contain an alpha channel."); + + vector points; + + auto data = img->getData(); + auto w = img->getWidth(); + auto h = img->getHeight(); + + for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) + { + float a = data[(y * w + x) * 4 + 3] / 255.0F; + if (a >= alphaThreshold && !inverted) + points.push_back({float(x), float(h - y)}); + if (a < alphaThreshold && inverted) + points.push_back({float(x), float(h - y)}); + } + + auto iter = this->masks.find(maskName); + if (iter == this->masks.end()) + iter = this->masks.emplace(maskName, ParticleEmissionMaskDescriptor{}).first; + + ParticleEmissionMaskDescriptor desc; + desc.size = {float(w), float(h)}; + desc.points = std::move(points); + + iter->second = desc; +} + +const ParticleEmissionMaskDescriptor& ParticleEmissionMaskCache::getEmissionMask(std::string_view maskName) +{ + auto iter = this->masks.find(maskName); + if (iter == this->masks.end()) + { + ParticleEmissionMaskDescriptor desc; + desc.size = {float(1), float(1)}; + desc.points = {{0, 0}}; + return desc; + } + return iter->second; +} + +void ParticleEmissionMaskCache::releaseMaskFromMemory(std::string_view maskName) +{ + this->masks.erase(maskName); +} + +void ParticleEmissionMaskCache::releaseAllMasksFromMemory() +{ + this->masks.clear(); +} + +NS_CC_END \ No newline at end of file diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 7a26cb6e93..1d0fd0ce5f 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -65,7 +65,14 @@ enum class EmissionShapeType RECT, RECTTORUS, CIRCLE, - TORUS + TORUS, + ALPHA_MASK +}; + +struct ParticleEmissionMaskDescriptor +{ + Vec2 size; + std::vector points; }; /** @@ -90,6 +97,8 @@ struct EmissionShape float coneOffset; float coneAngle; float edgeElasticity; + + ParticleEmissionMaskDescriptor mask; }; /** @struct ParticleAnimationDescriptor @@ -244,6 +253,29 @@ public: } }; +class CC_DLL ParticleEmissionMaskCache : public cocos2d::Ref +{ +public: + static ParticleEmissionMaskCache* getInstance(); + + void bakeEmissionMask(std::string_view maskName, + std::string_view texturePath, + float alphaThreshold = 0.5F, + bool inverted = false); + + void bakeEmissionMask(std::string_view maskName, + Image* imageTexture, + float alphaThreshold = 0.5F, + bool inverted = false); + + const ParticleEmissionMaskDescriptor& getEmissionMask(std::string_view maskName); + + void releaseMaskFromMemory(std::string_view maskName); + void releaseAllMasksFromMemory(); + + hlookup::string_map masks; +}; + // typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tParticle*, Vec2); class Texture2D; @@ -292,7 +324,6 @@ emitter.startSpin = 0; @endcode */ - class CC_DLL ParticleSystem : public Node, public TextureProtocol, public PlayableProtocol { public: @@ -1164,6 +1195,17 @@ public: */ void setEmissionShape(unsigned short index, EmissionShape shape); + /** Adds an emission shape of type mask to the system. + * The mask should be added using the ParticleEmissionMaskCache class. + * + * @param maskName Name of the emission mask. + * @param pos Position of the emission shape in local space. + * @param overrideSize Size of the emission mask in pixels, leave ZERO to use texture size. + * @param scale Scale of the emission mask, the size will be multiplied by the specified scale. + * @param angle Angle of the sampled points to be rotated in degrees. + */ + static EmissionShape createMaskShape(std::string_view maskName, Vec2 pos = Vec2::ZERO, Vec2 overrideSize = Vec2::ZERO, Vec2 scale = Vec2::ONE, float angle = 0.0F); + /** Adds an emission shape of type point to the system. * @param pos Position of the emission shape in local space. */ From d28e92aa45a8d0811c5a964b69a9528a578d655d Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 12 Jun 2022 01:32:29 +0300 Subject: [PATCH 46/61] Fix RANDOM_KISS 1.0F Bias --- core/2d/CCParticleSystem.cpp | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 15ae448c11..94c68ec973 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -100,8 +100,10 @@ inline void normalize_point(float x, float y, particle_point* out) /** A more effective random number generator function that fixes strafing for position variance, made by kiss rng. KEEP IT SIMPLE STUPID (KISS) rng example: https://gist.github.com/3ki5tj/7b1d51e96d1f9bfb89bc + + Generates a random number between 0.0 to 1.0 INCLUSIVE. */ -inline static float RANDOM_KISS(void) +inline static float RANDOM_KISS_ABS(void) { #define kiss_znew(z) (z = 36969 * (z & 65535) + (z >> 16)) #define kiss_wnew(w) (w = 18000 * (w & 65535) + (w >> 16)) @@ -111,10 +113,18 @@ inline static float RANDOM_KISS(void) #define kiss_KISS(z, w, jc, jsr) ((kiss_MWC(z, w) ^ kiss_CONG(jc)) + kiss_SHR3(jsr)) static unsigned kiss_z = rand(), kiss_w = rand(), kiss_jsr = rand(), kiss_jcong = rand(); - // Generate two random floats and add them to get a total of 2.0 and then subtract 1.0 - // to get a random number between -1.0 and 1.0 INCLUSIVE. - return -1.0F + ((kiss_KISS(kiss_z, kiss_w, kiss_jcong, kiss_jsr) / 4294967296.0) + - (kiss_KISS(kiss_z, kiss_w, kiss_jcong, kiss_jsr) / 4294967296.0)); + return kiss_KISS(kiss_z, kiss_w, kiss_jcong, kiss_jsr) / 4294967296.0; +} + +/** + A more effective random number generator function that fixes strafing for position variance, made by kiss rng. + KEEP IT SIMPLE STUPID (KISS) rng example: https://gist.github.com/3ki5tj/7b1d51e96d1f9bfb89bc + + Generates a random number between -1.0 and 1.0 INCLUSIVE. + */ +inline static float RANDOM_KISS(void) +{ + return -1.0F + RANDOM_KISS_ABS() + RANDOM_KISS_ABS(); } ParticleData::ParticleData() @@ -783,7 +793,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe continue; } - auto randElem = abs(RANDOM_KISS()); + auto randElem = RANDOM_KISS_ABS(); auto& shape = _emissionShapes[MIN(randElem * _emissionShapes.size(), _emissionShapes.size() - 1)]; switch (shape.type) @@ -804,8 +814,8 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } case EmissionShapeType::RECTTORUS: { - float width = (shape.outerWidth - shape.innerWidth) * abs(RANDOM_KISS()) + shape.innerWidth; - float height = (shape.outerHeight - shape.innerHeight) * abs(RANDOM_KISS()) + shape.innerHeight; + float width = (shape.outerWidth - shape.innerWidth) * RANDOM_KISS_ABS() + shape.innerWidth; + float height = (shape.outerHeight - shape.innerHeight) * RANDOM_KISS_ABS() + shape.innerHeight; width = RANDOM_KISS() < 0.0F ? width * -1 : width; height = RANDOM_KISS() < 0.0F ? height * -1 : height; float prob = RANDOM_KISS(); @@ -816,7 +826,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } case EmissionShapeType::CIRCLE: { - auto val = abs(RANDOM_KISS()) * shape.innerRadius / shape.innerRadius; + auto val = RANDOM_KISS_ABS() * shape.innerRadius / shape.innerRadius; val = powf(val, 1 / shape.edgeElasticity); auto point = Vec2(0.0F, val * shape.innerRadius); point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(shape.coneOffset + shape.coneAngle / 2 * RANDOM_KISS())); @@ -827,7 +837,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } case EmissionShapeType::TORUS: { - auto val = abs(RANDOM_KISS()) * shape.outerRadius / shape.outerRadius; + auto val = RANDOM_KISS_ABS() * shape.outerRadius / shape.outerRadius; val = powf(val, 1 / shape.edgeElasticity); auto point = Vec2(0.0F, ((val * (shape.outerRadius - shape.innerRadius) + shape.outerRadius) - (shape.outerRadius - shape.innerRadius))); point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(shape.coneOffset + shape.coneAngle / 2 * RANDOM_KISS())); @@ -1651,7 +1661,7 @@ void ParticleSystem::update(float dt) if (_particleData.animTimeDelta[i] > _particleData.animTimeLength[i]) { auto& anim = _animations.at(_particleData.animIndex[i]); - float percent = abs(RANDOM_KISS()); + float percent = RANDOM_KISS_ABS(); percent = anim.reverseIndices ? 1.0F - percent : percent; _particleData.animCellIndex[i] = anim.animationIndices[MIN( From 224bcf3397cee2f8cfa9b3eeeda0485c853c33a0 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 12 Jun 2022 01:39:11 +0300 Subject: [PATCH 47/61] Fix RANDOM_KISS rounding crash. --- core/2d/CCParticleSystem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 94c68ec973..47488f5ac9 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -860,7 +860,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe Vec2 point = {0, 0}; { - int rand0 = abs(RANDOM_KISS() * shape.mask.points.size()); + int rand0 = RANDOM_KISS_ABS() * shape.mask.points.size(); int index = MIN(rand0, shape.mask.points.size() - 1); point = shape.mask.points[index]; } @@ -915,7 +915,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { for (int i = start; i < _particleCount; ++i) { - int rand0 = abs(RANDOM_KISS() * _animIndexCount); + int rand0 = RANDOM_KISS_ABS() * _animIndexCount; _particleData.animCellIndex[i] = MIN(rand0, _animIndexCount - 1); } } @@ -927,7 +927,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe for (int i = start; i < _particleCount; ++i) { - int rand0 = abs(RANDOM_KISS() * _randomAnimations.size()); + int rand0 = RANDOM_KISS_ABS() * _randomAnimations.size(); int index = MIN(rand0, _randomAnimations.size() - 1); _particleData.animIndex[i] = _randomAnimations[index]; auto& descriptor = _animations.at(_particleData.animIndex[i]); From 503a5bb1e443602add978bf39c068795c556f1a5 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 12 Jun 2022 02:16:15 +0300 Subject: [PATCH 48/61] Add emission mask transformations support. --- core/2d/CCParticleSystem.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 47488f5ac9..494ce5437f 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -865,6 +865,13 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe point = shape.mask.points[index]; } + point -= size / 2; + + point.x = point.x / size.x * overrideSize.x * scale.x; + point.y = point.y / size.y * overrideSize.y * scale.y; + + point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(angle)); + _particleData.posx[i] = _sourcePosition.x + shape.x + point.x; _particleData.posy[i] = _sourcePosition.y + shape.y + point.y; From b670066756b658e55a74d3ff3c8cec4660a0b3e9 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 12 Jun 2022 02:44:27 +0300 Subject: [PATCH 49/61] Avoid unnecessary copying of memory. --- core/2d/CCParticleSystem.cpp | 14 ++++++++------ core/2d/CCParticleSystem.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 494ce5437f..14ba8b1907 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -848,21 +848,23 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } case EmissionShapeType::ALPHA_MASK: { + auto& mask = ParticleEmissionMaskCache::getInstance()->getEmissionMask(shape.maskName); + Vec2 pos = {shape.x, shape.y}; - Vec2 size = shape.mask.size; + Vec2 size = mask.size; Vec2 overrideSize = {shape.innerWidth, shape.innerHeight}; Vec2 scale = {shape.outerWidth, shape.outerHeight}; float angle = shape.coneOffset; if (overrideSize.isZero()) - overrideSize = shape.mask.size; + overrideSize = mask.size; Vec2 point = {0, 0}; { - int rand0 = RANDOM_KISS_ABS() * shape.mask.points.size(); - int index = MIN(rand0, shape.mask.points.size() - 1); - point = shape.mask.points[index]; + int rand0 = RANDOM_KISS_ABS() * mask.points.size(); + int index = MIN(rand0, mask.points.size() - 1); + point = mask.points[index]; } point -= size / 2; @@ -1192,7 +1194,7 @@ EmissionShape ParticleSystem::createMaskShape(std::string_view maskName, shape.type = EmissionShapeType::ALPHA_MASK; - shape.mask = ParticleEmissionMaskCache::getInstance()->getEmissionMask(maskName); + shape.maskName = maskName; shape.x = pos.x; shape.y = pos.y; diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 1d0fd0ce5f..efeec1ee1e 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -98,7 +98,7 @@ struct EmissionShape float coneAngle; float edgeElasticity; - ParticleEmissionMaskDescriptor mask; + std::string_view maskName; }; /** @struct ParticleAnimationDescriptor From 2836420fb30b8bdec319d8cf9ef25c0542acf73e Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 12 Jun 2022 03:10:49 +0300 Subject: [PATCH 50/61] what? [skip ci] --- core/2d/CCParticleSystem.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 14ba8b1907..693ea1af90 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -861,11 +861,9 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe Vec2 point = {0, 0}; - { - int rand0 = RANDOM_KISS_ABS() * mask.points.size(); - int index = MIN(rand0, mask.points.size() - 1); - point = mask.points[index]; - } + int rand0 = RANDOM_KISS_ABS() * mask.points.size(); + int index = MIN(rand0, mask.points.size() - 1); + point = mask.points[index]; point -= size / 2; From 61b3de52a3f7c53c49e6d986f0831f3e76003fa2 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 12 Jun 2022 03:40:13 +0300 Subject: [PATCH 51/61] Cleanup and add performance logs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Somehow not using const reference takes 30 fps away πŸ˜• --- core/2d/CCParticleSystem.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 693ea1af90..0669bb5dec 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -2357,6 +2357,9 @@ void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, desc.points = std::move(points); iter->second = desc; + + CCLOG("Particle emission mask '%s' baked (%dx%d), %d samples generated taking %.2fmb of memory.", + std::string(maskName).c_str(), w, h, desc.points.size(), desc.points.size() * 8 / 1e+6); } const ParticleEmissionMaskDescriptor& ParticleEmissionMaskCache::getEmissionMask(std::string_view maskName) @@ -2364,7 +2367,7 @@ const ParticleEmissionMaskDescriptor& ParticleEmissionMaskCache::getEmissionMask auto iter = this->masks.find(maskName); if (iter == this->masks.end()) { - ParticleEmissionMaskDescriptor desc; + ParticleEmissionMaskDescriptor desc{}; desc.size = {float(1), float(1)}; desc.points = {{0, 0}}; return desc; From 83f801c5440eaddd63f486917965ee2c0aa141d0 Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 12 Jun 2022 15:15:51 +0300 Subject: [PATCH 52/61] Emplace dummy masks to container. --- core/2d/CCParticleSystem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 0669bb5dec..2e85deba7a 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -2367,10 +2367,10 @@ const ParticleEmissionMaskDescriptor& ParticleEmissionMaskCache::getEmissionMask auto iter = this->masks.find(maskName); if (iter == this->masks.end()) { - ParticleEmissionMaskDescriptor desc{}; - desc.size = {float(1), float(1)}; - desc.points = {{0, 0}}; - return desc; + iter = this->masks.emplace(maskName, ParticleEmissionMaskDescriptor{}).first; + iter->second.size = {float(1), float(1)}; + iter->second.points = {{0, 0}}; + return iter->second; } return iter->second; } From eff782dfb47a9137300586212e3148a2f072146d Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 12 Jun 2022 15:36:25 +0300 Subject: [PATCH 53/61] Add comments. --- core/2d/CCParticleSystem.cpp | 4 ++-- core/2d/CCParticleSystem.h | 44 ++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 2e85deba7a..a73a8ebdc2 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -2375,12 +2375,12 @@ const ParticleEmissionMaskDescriptor& ParticleEmissionMaskCache::getEmissionMask return iter->second; } -void ParticleEmissionMaskCache::releaseMaskFromMemory(std::string_view maskName) +void ParticleEmissionMaskCache::deleteMask(std::string_view maskName) { this->masks.erase(maskName); } -void ParticleEmissionMaskCache::releaseAllMasksFromMemory() +void ParticleEmissionMaskCache::deleteAllMasks() { this->masks.clear(); } diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index efeec1ee1e..4c531d0ead 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -69,6 +69,10 @@ enum class EmissionShapeType ALPHA_MASK }; +/** + * Particle emission mask descriptor. + * @since adxe-1.0.0b7 + */ struct ParticleEmissionMaskDescriptor { Vec2 size; @@ -253,27 +257,63 @@ public: } }; +/** + * Particle emission mask cache. + * @since adxe-1.0.0b7 + */ class CC_DLL ParticleEmissionMaskCache : public cocos2d::Ref { public: static ParticleEmissionMaskCache* getInstance(); + /** Bakes a particle emission mask from texture data on cpu and stores it in memory by it's name. + * If the mask already exists then it will be overwritten. + * + * @param maskName The name that identifies the mask. + * @param texturePath Path of the texture that holds alpha data. + * @param alphaThreshold The threshold at which pixels are picked, If a pixel's alpha channel is greater than + * alphaThreshold then it will be picked. + * @param inverted Inverts the pick condition so that If a pixel's alpha channel is lower than alphaThreshold then + * it will be picked. + */ void bakeEmissionMask(std::string_view maskName, std::string_view texturePath, float alphaThreshold = 0.5F, bool inverted = false); + /** Bakes a particle emission mask from texture data on cpu and stores it in memory by it's name. + * If the mask already exists then it will be overwritten. + * + * @param maskName The name that identifies the mask. + * @param imageTexture Image object containing texture data with alpha channel. + * @param alphaThreshold The threshold at which pixels are picked, If a pixel's alpha channel is greater than + * alphaThreshold then it will be picked. + * @param inverted Inverts the pick condition so that If a pixel's alpha channel is lower than alphaThreshold then + * it will be picked. + */ void bakeEmissionMask(std::string_view maskName, Image* imageTexture, float alphaThreshold = 0.5F, bool inverted = false); + /** Returns a baked mask with the specified name if it exists. otherwise, it will return a dummy mask. + * + * @param maskName The name that identifies the mask. + */ const ParticleEmissionMaskDescriptor& getEmissionMask(std::string_view maskName); - void releaseMaskFromMemory(std::string_view maskName); - void releaseAllMasksFromMemory(); + /** Deletes a baked mask and releases the data from memory with the specified name if it exists. + * + * @param maskName The name that identifies the mask. + */ + void deleteMask(std::string_view maskName); + /** Deletes all baked masks and releases their data from memory. */ + void deleteAllMasks(); + +private: hlookup::string_map masks; + }; // typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tParticle*, Vec2); From a2bea19e1ebc56774c372b65b0782313b3c14d5c Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Sun, 12 Jun 2022 15:57:11 +0300 Subject: [PATCH 54/61] Add in-between fill in. --- core/2d/CCParticleSystem.cpp | 42 +++++++++++++++++++++++++----------- core/2d/CCParticleSystem.h | 18 ++++++++++------ 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index a73a8ebdc2..9c9b27251b 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -2314,20 +2314,22 @@ ParticleEmissionMaskCache* ParticleEmissionMaskCache::getInstance() void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, std::string_view texturePath, float alphaThreshold, - bool inverted) + bool inverted, + int inbetweenSamples) { auto img = new Image(); img->Image::initWithImageFile(texturePath); img->autorelease(); CCASSERT(img, "image texture was nullptr."); - bakeEmissionMask(maskName, img, alphaThreshold, inverted); + bakeEmissionMask(maskName, img, alphaThreshold, inverted, inbetweenSamples); } void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, Image* imageTexture, float alphaThreshold, - bool inverted) + bool inverted, + int inbetweenSamples) { auto img = imageTexture; CCASSERT(img, "image texture was nullptr."); @@ -2339,14 +2341,28 @@ void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, auto w = img->getWidth(); auto h = img->getHeight(); - for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) - { - float a = data[(y * w + x) * 4 + 3] / 255.0F; - if (a >= alphaThreshold && !inverted) - points.push_back({float(x), float(h - y)}); - if (a < alphaThreshold && inverted) - points.push_back({float(x), float(h - y)}); - } + for (int y = 0; y < h; y++) + for (int x = 0; x < w; x++) + { + if (inbetweenSamples > 1) + { + float a = data[(y * w + x) * 4 + 3] / 255.0F; + if (a >= alphaThreshold && !inverted) + for (float i = 0; i < 1.0F; i += 1.0F / inbetweenSamples) + points.push_back({float(x + i), float(h - y + i)}); + if (a < alphaThreshold && inverted) + for (float i = 0; i < 1.0F; i += 1.0F / inbetweenSamples) + points.push_back({float(x + i), float(h - y + i)}); + } + else + { + float a = data[(y * w + x) * 4 + 3] / 255.0F; + if (a >= alphaThreshold && !inverted) + points.push_back({float(x), float(h - y)}); + if (a < alphaThreshold && inverted) + points.push_back({float(x), float(h - y)}); + } + } auto iter = this->masks.find(maskName); if (iter == this->masks.end()) @@ -2375,12 +2391,12 @@ const ParticleEmissionMaskDescriptor& ParticleEmissionMaskCache::getEmissionMask return iter->second; } -void ParticleEmissionMaskCache::deleteMask(std::string_view maskName) +void ParticleEmissionMaskCache::removeMask(std::string_view maskName) { this->masks.erase(maskName); } -void ParticleEmissionMaskCache::deleteAllMasks() +void ParticleEmissionMaskCache::removeAllMasks() { this->masks.clear(); } diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 4c531d0ead..db8a2f6b2b 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -275,11 +275,14 @@ public: * alphaThreshold then it will be picked. * @param inverted Inverts the pick condition so that If a pixel's alpha channel is lower than alphaThreshold then * it will be picked. + * @param inbetweenSamples How many times should pixels be filled inbetween, this value should be increased If + * you're planning to scale the emission shape up. WARNING: it will use more memory. */ void bakeEmissionMask(std::string_view maskName, std::string_view texturePath, float alphaThreshold = 0.5F, - bool inverted = false); + bool inverted = false, + int inbetweenSamples = 1); /** Bakes a particle emission mask from texture data on cpu and stores it in memory by it's name. * If the mask already exists then it will be overwritten. @@ -290,11 +293,14 @@ public: * alphaThreshold then it will be picked. * @param inverted Inverts the pick condition so that If a pixel's alpha channel is lower than alphaThreshold then * it will be picked. + * @param inbetweenSamples How many times should pixels be filled inbetween, this value should be increased If + * you're planning to scale the emission shape up. WARNING: it will use more memory. */ void bakeEmissionMask(std::string_view maskName, Image* imageTexture, float alphaThreshold = 0.5F, - bool inverted = false); + bool inverted = false, + int inbetweenSamples = 1); /** Returns a baked mask with the specified name if it exists. otherwise, it will return a dummy mask. * @@ -302,14 +308,14 @@ public: */ const ParticleEmissionMaskDescriptor& getEmissionMask(std::string_view maskName); - /** Deletes a baked mask and releases the data from memory with the specified name if it exists. + /** Removes a baked mask and releases the data from memory with the specified name if it exists. * * @param maskName The name that identifies the mask. */ - void deleteMask(std::string_view maskName); + void removeMask(std::string_view maskName); - /** Deletes all baked masks and releases their data from memory. */ - void deleteAllMasks(); + /** Remove all baked masks and releases their data from memory. */ + void removeAllMasks(); private: hlookup::string_map masks; From 0a1fe16b337e54fbc53c10288fd59307ac5a38ea Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Wed, 15 Jun 2022 23:04:08 +0300 Subject: [PATCH 55/61] Add cpp_tests and improve system. --- core/2d/CCParticleSystem.cpp | 150 +-- core/2d/CCParticleSystem.h | 114 +- .../Classes/ParticleTest/ParticleTest.cpp | 1148 +++++++++++++++++ .../Classes/ParticleTest/ParticleTest.h | 219 ++++ .../Resources/Particles/animation_1.plist | 176 +++ .../Resources/Particles/animation_1.png | Bin 0 -> 8121 bytes tests/cpp-tests/Resources/Particles/mask.png | Bin 0 -> 17534 bytes 7 files changed, 1688 insertions(+), 119 deletions(-) create mode 100644 tests/cpp-tests/Resources/Particles/animation_1.plist create mode 100644 tests/cpp-tests/Resources/Particles/animation_1.png create mode 100644 tests/cpp-tests/Resources/Particles/mask.png diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 9c9b27251b..992af5538e 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -97,36 +97,6 @@ inline void normalize_point(float x, float y, particle_point* out) out->y = y * n; } -/** - A more effective random number generator function that fixes strafing for position variance, made by kiss rng. - KEEP IT SIMPLE STUPID (KISS) rng example: https://gist.github.com/3ki5tj/7b1d51e96d1f9bfb89bc - - Generates a random number between 0.0 to 1.0 INCLUSIVE. - */ -inline static float RANDOM_KISS_ABS(void) -{ -#define kiss_znew(z) (z = 36969 * (z & 65535) + (z >> 16)) -#define kiss_wnew(w) (w = 18000 * (w & 65535) + (w >> 16)) -#define kiss_MWC(z, w) ((kiss_znew(z) << 16) + kiss_wnew(w)) -#define kiss_SHR3(jsr) (jsr ^= (jsr << 17), jsr ^= (jsr >> 13), jsr ^= (jsr << 5)) -#define kiss_CONG(jc) (jc = 69069 * jc + 1234567) -#define kiss_KISS(z, w, jc, jsr) ((kiss_MWC(z, w) ^ kiss_CONG(jc)) + kiss_SHR3(jsr)) - - static unsigned kiss_z = rand(), kiss_w = rand(), kiss_jsr = rand(), kiss_jcong = rand(); - return kiss_KISS(kiss_z, kiss_w, kiss_jcong, kiss_jsr) / 4294967296.0; -} - -/** - A more effective random number generator function that fixes strafing for position variance, made by kiss rng. - KEEP IT SIMPLE STUPID (KISS) rng example: https://gist.github.com/3ki5tj/7b1d51e96d1f9bfb89bc - - Generates a random number between -1.0 and 1.0 INCLUSIVE. - */ -inline static float RANDOM_KISS(void) -{ - return -1.0F + RANDOM_KISS_ABS() + RANDOM_KISS_ABS(); -} - ParticleData::ParticleData() { memset(this, 0, sizeof(ParticleData)); @@ -279,7 +249,6 @@ ParticleSystem::ParticleSystem() , _emissionShapeIndex(0) , _positionType(PositionType::FREE) , _paused(false) - , _updatePaused(false) , _timeScale(1) , _fixedFPS(0) , _fixedFPSDelta(0) @@ -777,7 +746,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe // life for (int i = start; i < _particleCount; ++i) { - float particleLife = _life + _lifeVar * RANDOM_KISS(); + float particleLife = _life + _lifeVar * _rng.rangef(); _particleData.totalTimeToLive[i] = MAX(0, particleLife); _particleData.timeToLive[i] = MAX(0, particleLife); } @@ -788,12 +757,12 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { if (_emissionShapes.empty()) { - _particleData.posx[i] = _sourcePosition.x + _posVar.x * RANDOM_KISS(); - _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_KISS(); + _particleData.posx[i] = _sourcePosition.x + _posVar.x * _rng.rangef(); + _particleData.posy[i] = _sourcePosition.y + _posVar.y * _rng.rangef(); continue; } - auto randElem = RANDOM_KISS_ABS(); + auto randElem = _rng.float01(); auto& shape = _emissionShapes[MIN(randElem * _emissionShapes.size(), _emissionShapes.size() - 1)]; switch (shape.type) @@ -807,29 +776,29 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } case EmissionShapeType::RECT: { - _particleData.posx[i] = _sourcePosition.x + shape.x + shape.innerWidth / 2 * RANDOM_KISS(); - _particleData.posy[i] = _sourcePosition.y + shape.y + shape.innerHeight / 2 * RANDOM_KISS(); + _particleData.posx[i] = _sourcePosition.x + shape.x + shape.innerWidth / 2 * _rng.rangef(); + _particleData.posy[i] = _sourcePosition.y + shape.y + shape.innerHeight / 2 * _rng.rangef(); break; } case EmissionShapeType::RECTTORUS: { - float width = (shape.outerWidth - shape.innerWidth) * RANDOM_KISS_ABS() + shape.innerWidth; - float height = (shape.outerHeight - shape.innerHeight) * RANDOM_KISS_ABS() + shape.innerHeight; - width = RANDOM_KISS() < 0.0F ? width * -1 : width; - height = RANDOM_KISS() < 0.0F ? height * -1 : height; - float prob = RANDOM_KISS(); - _particleData.posx[i] = _sourcePosition.x + shape.x + width / 2 * (prob >= 0.0F ? 1.0F : RANDOM_KISS()); - _particleData.posy[i] = _sourcePosition.y + shape.y + height / 2 * (prob < 0.0F ? 1.0F : RANDOM_KISS()); + float width = (shape.outerWidth - shape.innerWidth) * _rng.float01() + shape.innerWidth; + float height = (shape.outerHeight - shape.innerHeight) * _rng.float01() + shape.innerHeight; + width = _rng.rangef() < 0.0F ? width * -1 : width; + height = _rng.rangef() < 0.0F ? height * -1 : height; + float prob = _rng.rangef(); + _particleData.posx[i] = _sourcePosition.x + shape.x + width / 2 * (prob >= 0.0F ? 1.0F : _rng.rangef()); + _particleData.posy[i] = _sourcePosition.y + shape.y + height / 2 * (prob < 0.0F ? 1.0F : _rng.rangef()); break; } case EmissionShapeType::CIRCLE: { - auto val = RANDOM_KISS_ABS() * shape.innerRadius / shape.innerRadius; + auto val = _rng.float01() * shape.innerRadius / shape.innerRadius; val = powf(val, 1 / shape.edgeElasticity); auto point = Vec2(0.0F, val * shape.innerRadius); - point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(shape.coneOffset + shape.coneAngle / 2 * RANDOM_KISS())); + point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(shape.coneOffset + shape.coneAngle / 2 * _rng.rangef())); _particleData.posx[i] = _sourcePosition.x + shape.x + point.x / 2; _particleData.posy[i] = _sourcePosition.y + shape.y + point.y / 2; @@ -837,10 +806,10 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } case EmissionShapeType::TORUS: { - auto val = RANDOM_KISS_ABS() * shape.outerRadius / shape.outerRadius; + auto val = _rng.float01() * shape.outerRadius / shape.outerRadius; val = powf(val, 1 / shape.edgeElasticity); auto point = Vec2(0.0F, ((val * (shape.outerRadius - shape.innerRadius) + shape.outerRadius) - (shape.outerRadius - shape.innerRadius))); - point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(shape.coneOffset + shape.coneAngle / 2 * RANDOM_KISS())); + point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(shape.coneOffset + shape.coneAngle / 2 * _rng.rangef())); _particleData.posx[i] = _sourcePosition.x + shape.x + point.x / 2; _particleData.posy[i] = _sourcePosition.y + shape.y + point.y / 2; @@ -861,7 +830,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe Vec2 point = {0, 0}; - int rand0 = RANDOM_KISS_ABS() * mask.points.size(); + int rand0 = _rng.float01() * mask.points.size(); int index = MIN(rand0, mask.points.size() - 1); point = mask.points[index]; @@ -885,12 +854,12 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe // position for (int i = start; i < _particleCount; ++i) { - _particleData.posx[i] = _sourcePosition.x + _posVar.x * RANDOM_KISS(); + _particleData.posx[i] = _sourcePosition.x + _posVar.x * _rng.rangef(); } for (int i = start; i < _particleCount; ++i) { - _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_KISS(); + _particleData.posy[i] = _sourcePosition.y + _posVar.y * _rng.rangef(); } } @@ -911,7 +880,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe _particleData.animIndex[i] = animationIndex; auto& descriptor = _animations.at(animationIndex); _particleData.animTimeLength[i] = - descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_KISS(); + descriptor.animationSpeed + descriptor.animationSpeedVariance * _rng.rangef(); } } } @@ -922,7 +891,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { for (int i = start; i < _particleCount; ++i) { - int rand0 = RANDOM_KISS_ABS() * _animIndexCount; + int rand0 = _rng.float01() * _animIndexCount; _particleData.animCellIndex[i] = MIN(rand0, _animIndexCount - 1); } } @@ -934,12 +903,12 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe for (int i = start; i < _particleCount; ++i) { - int rand0 = RANDOM_KISS_ABS() * _randomAnimations.size(); + int rand0 = _rng.float01() * _randomAnimations.size(); int index = MIN(rand0, _randomAnimations.size() - 1); _particleData.animIndex[i] = _randomAnimations[index]; auto& descriptor = _animations.at(_particleData.animIndex[i]); _particleData.animTimeLength[i] = - descriptor.animationSpeed + descriptor.animationSpeedVariance * RANDOM_KISS(); + descriptor.animationSpeed + descriptor.animationSpeedVariance * _rng.rangef(); } } @@ -951,7 +920,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe #define SET_COLOR(c, b, v) \ for (int i = start; i < _particleCount; ++i) \ { \ - c[i] = clampf(b + v * RANDOM_KISS(), 0, 1); \ + c[i] = clampf(b + v * _rng.rangef(), 0, 1); \ } SET_COLOR(_particleData.colorR, _startColor.r, _startColorVar.r); @@ -980,7 +949,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { for (int i = start; i < _particleCount; ++i) { - _particleData.opacityFadeInLength[i] = _spawnFadeIn + _spawnFadeInVar * RANDOM_KISS(); + _particleData.opacityFadeInLength[i] = _spawnFadeIn + _spawnFadeInVar * _rng.rangef(); } std::fill_n(_particleData.opacityFadeInDelta + start, _particleCount - start, 0.0F); } @@ -990,7 +959,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { for (int i = start; i < _particleCount; ++i) { - _particleData.scaleInLength[i] = _spawnScaleIn + _spawnScaleInVar * RANDOM_KISS(); + _particleData.scaleInLength[i] = _spawnScaleIn + _spawnScaleInVar * _rng.rangef(); } std::fill_n(_particleData.scaleInDelta + start, _particleCount - start, 0.0F); } @@ -1000,24 +969,24 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { for (int i = start; i < _particleCount; ++i) { - _particleData.hue[i] = _hsv.h + _hsvVar.h * RANDOM_KISS(); + _particleData.hue[i] = _hsv.h + _hsvVar.h * _rng.rangef(); } for (int i = start; i < _particleCount; ++i) { - _particleData.sat[i] = _hsv.s + _hsvVar.s * RANDOM_KISS(); + _particleData.sat[i] = _hsv.s + _hsvVar.s * _rng.rangef(); } for (int i = start; i < _particleCount; ++i) { - _particleData.val[i] = _hsv.v + _hsvVar.v * RANDOM_KISS(); + _particleData.val[i] = _hsv.v + _hsvVar.v * _rng.rangef(); } } // size for (int i = start; i < _particleCount; ++i) { - _particleData.size[i] = _startSize + _startSizeVar * RANDOM_KISS(); + _particleData.size[i] = _startSize + _startSizeVar * _rng.rangef(); _particleData.size[i] = MAX(0, _particleData.size[i]); } @@ -1025,7 +994,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { for (int i = start; i < _particleCount; ++i) { - float endSize = _endSize + _endSizeVar * RANDOM_KISS(); + float endSize = _endSize + _endSizeVar * _rng.rangef(); endSize = MAX(0, endSize); _particleData.deltaSize[i] = (endSize - _particleData.size[i]) / _particleData.timeToLive[i]; } @@ -1036,18 +1005,18 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe // rotation for (int i = start; i < _particleCount; ++i) { - _particleData.rotation[i] = _startSpin + _startSpinVar * RANDOM_KISS(); + _particleData.rotation[i] = _startSpin + _startSpinVar * _rng.rangef(); } for (int i = start; i < _particleCount; ++i) { - float endA = _endSpin + _endSpinVar * RANDOM_KISS(); + float endA = _endSpin + _endSpinVar * _rng.rangef(); _particleData.deltaRotation[i] = (endA - _particleData.rotation[i]) / _particleData.timeToLive[i]; } // static rotation for (int i = start; i < _particleCount; ++i) { - _particleData.staticRotation[i] = _spawnAngle + _spawnAngleVar * RANDOM_KISS(); + _particleData.staticRotation[i] = _spawnAngle + _spawnAngleVar * _rng.rangef(); } // position @@ -1070,13 +1039,13 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe // radial accel for (int i = start; i < _particleCount; ++i) { - _particleData.modeA.radialAccel[i] = modeA.radialAccel + modeA.radialAccelVar * RANDOM_KISS(); + _particleData.modeA.radialAccel[i] = modeA.radialAccel + modeA.radialAccelVar * _rng.rangef(); } // tangential accel for (int i = start; i < _particleCount; ++i) { - _particleData.modeA.tangentialAccel[i] = modeA.tangentialAccel + modeA.tangentialAccelVar * RANDOM_KISS(); + _particleData.modeA.tangentialAccel[i] = modeA.tangentialAccel + modeA.tangentialAccelVar * _rng.rangef(); } // rotation is dir @@ -1084,9 +1053,9 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { for (int i = start; i < _particleCount; ++i) { - float a = CC_DEGREES_TO_RADIANS(_angle + _angleVar * RANDOM_KISS()); + float a = CC_DEGREES_TO_RADIANS(_angle + _angleVar * _rng.rangef()); Vec2 v(cosf(a), sinf(a)); - float s = modeA.speed + modeA.speedVar * RANDOM_KISS(); + float s = modeA.speed + modeA.speedVar * _rng.rangef(); Vec2 dir = v * s; _particleData.modeA.dirX[i] = dir.x; // v * s ; _particleData.modeA.dirY[i] = dir.y; @@ -1097,9 +1066,9 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { for (int i = start; i < _particleCount; ++i) { - float a = CC_DEGREES_TO_RADIANS(_angle + _angleVar * RANDOM_KISS()); + float a = CC_DEGREES_TO_RADIANS(_angle + _angleVar * _rng.rangef()); Vec2 v(cosf(a), sinf(a)); - float s = modeA.speed + modeA.speedVar * RANDOM_KISS(); + float s = modeA.speed + modeA.speedVar * _rng.rangef(); Vec2 dir = v * s; _particleData.modeA.dirX[i] = dir.x; // v * s ; _particleData.modeA.dirY[i] = dir.y; @@ -1114,18 +1083,18 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe // Set the default diameter of the particle from the source position for (int i = start; i < _particleCount; ++i) { - _particleData.modeB.radius[i] = modeB.startRadius + modeB.startRadiusVar * RANDOM_KISS(); + _particleData.modeB.radius[i] = modeB.startRadius + modeB.startRadiusVar * _rng.rangef(); } for (int i = start; i < _particleCount; ++i) { - _particleData.modeB.angle[i] = CC_DEGREES_TO_RADIANS(_angle + _angleVar * RANDOM_KISS()); + _particleData.modeB.angle[i] = CC_DEGREES_TO_RADIANS(_angle + _angleVar * _rng.rangef()); } for (int i = start; i < _particleCount; ++i) { _particleData.modeB.degreesPerSecond[i] = - CC_DEGREES_TO_RADIANS(modeB.rotatePerSecond + modeB.rotatePerSecondVar * RANDOM_KISS()); + CC_DEGREES_TO_RADIANS(modeB.rotatePerSecond + modeB.rotatePerSecondVar * _rng.rangef()); } if (modeB.endRadius == START_RADIUS_EQUAL_TO_END_RADIUS) @@ -1134,7 +1103,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe { for (int i = start; i < _particleCount; ++i) { - float endRadius = modeB.endRadius + modeB.endRadiusVar * RANDOM_KISS(); + float endRadius = modeB.endRadius + modeB.endRadiusVar * _rng.rangef(); _particleData.modeB.deltaRadius[i] = (endRadius - _particleData.modeB.radius[i]) / _particleData.timeToLive[i]; } @@ -1483,7 +1452,12 @@ bool ParticleSystem::addAnimationIndex(cocos2d::SpriteFrame* frame) bool ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::SpriteFrame* frame) { if (frame) - return addAnimationIndex(index, frame->getRect(), frame->isRotated()); + { + auto rect = frame->getRectInPixels(); + rect.size.x = frame->getOriginalSizeInPixels().x; + rect.size.y = frame->getOriginalSizeInPixels().y; + return addAnimationIndex(index, rect, frame->isRotated()); + } return false; } @@ -1505,8 +1479,6 @@ bool ParticleSystem::addAnimationIndex(unsigned short index, cocos2d::Rect rect, void ParticleSystem::simulate(float seconds, float frameRate) { - auto l_updatePaused = _updatePaused; - _updatePaused = false; seconds = seconds == SIMULATION_USE_PARTICLE_LIFETIME ? getLife() + getLifeVar() : seconds; frameRate = frameRate == SIMULATION_USE_GAME_ANIMATION_INTERVAL ? 1.0F / Director::getInstance()->getAnimationInterval() @@ -1523,7 +1495,6 @@ void ParticleSystem::simulate(float seconds, float frameRate) } else this->update(seconds); - _updatePaused = l_updatePaused; } void ParticleSystem::resimulate(float seconds, float frameRate) @@ -1577,7 +1548,7 @@ bool ParticleSystem::isFull() void ParticleSystem::update(float dt) { // don't process particles nor update gl buffer when this node is invisible. - if (!_visible || _updatePaused) + if (!_visible) return; CC_PROFILER_START_CATEGORY(kProfilerCategoryParticles, "CCParticleSystem - update"); @@ -1668,7 +1639,7 @@ void ParticleSystem::update(float dt) if (_particleData.animTimeDelta[i] > _particleData.animTimeLength[i]) { auto& anim = _animations.at(_particleData.animIndex[i]); - float percent = RANDOM_KISS_ABS(); + float percent = _rng.float01(); percent = anim.reverseIndices ? 1.0F - percent : percent; _particleData.animCellIndex[i] = anim.animationIndices[MIN( @@ -2264,21 +2235,6 @@ void ParticleSystem::resumeEmissions() _paused = false; } -bool ParticleSystem::isUpdatePaused() const -{ - return _updatePaused; -} - -void ParticleSystem::pauseUpdate() -{ - _updatePaused = true; -} - -void ParticleSystem::resumeUpdate() -{ - _updatePaused = false; -} - float ParticleSystem::getFixedFPS() { return _fixedFPS; diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index db8a2f6b2b..65c9018563 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -54,10 +54,92 @@ struct particle_point float y; }; +//* A more effective random number generator function that fixes strafing for position variance, made by kiss rng. +struct RngSeed +{ + const unsigned long RNG_RAND_MAX = 4294967295; + const unsigned long RNG_RAND_MAX_SIGNED = 2147483647; + unsigned long _x = 1; + unsigned long _y = 2; + unsigned long _z = 4; + unsigned long _w = 8; + unsigned long _carry = 0; + unsigned long _k; + unsigned long _m; + unsigned long _seed; + + RngSeed() { seed_rand(time(NULL)); } + + // initialize this object with seed + void seed_rand(unsigned long seed) + { + _seed = seed; + _x = seed | 1; + _y = seed | 2; + _z = seed | 4; + _w = seed | 8; + _carry = 0; + } + + // returns an unsigned long random value + unsigned long rand() + { + _x = _x * 69069 + 1; + _y ^= _y << 13; + _y ^= _y >> 17; + _y ^= _y << 5; + _k = (_z >> 2) + (_w >> 3) + (_carry >> 2); + _m = _w + _w + _z + _carry; + _z = _w; + _w = _m; + _carry = _k >> 30; + return _x + _y + _w; + } + + // returns a random integer from min to max + int range(int min, int max) + { + return floor(min + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - min)))); + } + + // returns a random unsigned integer from min to max + unsigned int rangeu(unsigned int min, unsigned int max) + { + return floor(min + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - min)))); + } + + // returns a random float from min to max + float rangef(float min = -1.0F, float max = 1.0F) + { + return min + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - min))); + } + + // returns a random integer from 0 to max + int max(int max = INT_MAX) + { + return floor(0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - 0)))); + } + + // returns a random unsigned integer from 0 to max + unsigned int maxu(unsigned int max = UINT_MAX) + { + return floor(0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - 0)))); + } + + // returns a random float from 0.0 to max + float maxf(float max) { return 0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - 0))); } + + // returns a random float from 0.0 to 1.0 + float float01() { return 0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (1 - 0))); } + + // returns either false or true randomly + bool bool01() { return (bool)floor(0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (2 - 0)))); } +}; + /** * Particle emission shapes. * Current supported shapes are Point, Rectangle, RectangularTorus, Circle, Torus - * @since adxe-1.0.0b7 + * @since adxe-1.0.0b8 */ enum class EmissionShapeType { @@ -71,7 +153,7 @@ enum class EmissionShapeType /** * Particle emission mask descriptor. - * @since adxe-1.0.0b7 + * @since adxe-1.0.0b8 */ struct ParticleEmissionMaskDescriptor { @@ -82,7 +164,7 @@ struct ParticleEmissionMaskDescriptor /** * Particle emission shapes. * Current supported shapes are Point, Rectangle, RectangularTorus, Circle, Torus, Cone, Cone Torus - * @since adxe-1.0.0b7 + * @since adxe-1.0.0b8 */ struct EmissionShape { @@ -259,7 +341,7 @@ public: /** * Particle emission mask cache. - * @since adxe-1.0.0b7 + * @since adxe-1.0.0b8 */ class CC_DLL ParticleEmissionMaskCache : public cocos2d::Ref { @@ -1021,14 +1103,14 @@ public: */ void setSpawnFadeInVar(float time); - /** Gets the spawn opacity fade in time of each particle. + /** Gets the spawn scale fade in time of each particle. * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified * time. * * @return The spawn opacity fade in time in seconds. */ float getSpawnScaleIn() { return _spawnScaleIn; } - /** Sets the spawn opacity fade in time of each particle when it's created. + /** Sets the spawn scale fade in time of each particle when it's created. * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified * time. * @@ -1036,14 +1118,14 @@ public: */ void setSpawnScaleIn(float time); - /** Gets the spawn opacity fade in time variance of each particle. + /** Gets the spawn scale fade in time variance of each particle. * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified * time. * * @return The spawn opacity fade in time variance in seconds. */ float getSpawnScaleInVar() { return _spawnScaleInVar; } - /** Sets the spawn opacity fade in time variance of each particle when it's created. + /** Sets the spawn scale fade in time variance of each particle when it's created. * Particles have the ability to spawn while having 0.0 size and gradually start going to 1.0 size with a specified * time. * @@ -1448,17 +1530,6 @@ public: /* Unpause the emissions */ virtual void resumeEmissions(); - /** Is system update paused - @return True if system update is paused, else false - */ - virtual bool isUpdatePaused() const; - - /* Pause the particles from being updated */ - virtual void pauseUpdate(); - - /* Unpause the particles from being updated */ - virtual void resumeUpdate(); - /** Gets the fixed frame rate count of the particle system. @return Fixed frame rate count of the particle system. */ @@ -1694,9 +1765,6 @@ protected: /** is the emitter paused */ bool _paused; - /** is particle system update paused */ - bool _updatePaused; - /** time scale of the particle system */ float _timeScale; @@ -1711,6 +1779,8 @@ protected: static Vector __allInstances; + RngSeed _rng; + private: CC_DISALLOW_COPY_AND_ASSIGN(ParticleSystem); }; diff --git a/tests/cpp-tests/Classes/ParticleTest/ParticleTest.cpp b/tests/cpp-tests/Classes/ParticleTest/ParticleTest.cpp index 425c830dc7..39c1749c7f 100644 --- a/tests/cpp-tests/Classes/ParticleTest/ParticleTest.cpp +++ b/tests/cpp-tests/Classes/ParticleTest/ParticleTest.cpp @@ -678,6 +678,1123 @@ std::string ParallaxParticle::subtitle() const return "Parallax + Particles"; } +//------------------------------------------------------------------ +// +// DemoFixedFPS +// +//------------------------------------------------------------------ +void DemoFixedFPS::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setFixedFPS(15.0F); + + setEmitterPosition(); +} + +std::string DemoFixedFPS::subtitle() const +{ + return "Particle Fixed FPS set to 15"; +} + +//------------------------------------------------------------------ +// +// DemoTimeScale +// +//------------------------------------------------------------------ +void DemoTimeScale::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setTag(2); + + setEmitterPosition(); +} + +std::string DemoTimeScale::subtitle() const +{ + return "Particle system timescale should alternate between 1.0 and 0.0"; +} + +void DemoTimeScale::update(float dt) +{ + auto p = (ParticleSystem*)getChildByTag(2); + + elapsedTime += Director::getInstance()->getDeltaTime(); + auto scale = (sin(elapsedTime * 4) + 1.0F) / 2.0F; + p->setTimeScale(scale); +} + +//------------------------------------------------------------------ +// +// DemoSpawnFadeIn +// +//------------------------------------------------------------------ +void DemoSpawnFadeIn::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setSpawnFadeIn(0.5F); + _emitter->setSpawnFadeInVar(0); + + setEmitterPosition(); +} + +std::string DemoSpawnFadeIn::subtitle() const +{ + return "Particle spawn fade in set to 0.5 seconds"; +} + +//------------------------------------------------------------------ +// +// DemoScaleFadeIn +// +//------------------------------------------------------------------ +void DemoScaleFadeIn::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setSpawnScaleIn(1); + _emitter->setSpawnScaleInVar(0); + + setEmitterPosition(); +} + +std::string DemoScaleFadeIn::subtitle() const +{ + return "Particle spawn scale in set to 1.0 seconds"; +} + +//------------------------------------------------------------------ +// +// DemoSimulation +// +//------------------------------------------------------------------ +void DemoSimulation::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->simulate(3.0F); + + setEmitterPosition(); +} + +std::string DemoSimulation::subtitle() const +{ + return "Particle simulation, particle system should advance 3 seconds in at the start"; +} + +//------------------------------------------------------------------ +// +// DemoSpawnRotation +// +//------------------------------------------------------------------ +void DemoSpawnRotation::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setSpawnAngle(0); + _emitter->setSpawnAngleVar(180); + + setEmitterPosition(); +} + +std::string DemoSpawnRotation::subtitle() const +{ + return "Particle rotation, particles should spawn with a random rotation"; +} + +//------------------------------------------------------------------ +// +// DemoHSV +// +//------------------------------------------------------------------ +void DemoHSV::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setStartColor({1, 1, 1, 1}); + _emitter->setStartColorVar({0, 0, 0, 0}); + _emitter->setEndColor({1, 1, 1, 1}); + _emitter->setEndColorVar({0, 0, 0, 0}); + + _emitter->useHSV(true); + _emitter->setHSV(HSV(90, 0.5F, 0.5F)); + _emitter->setHSVVar(HSV(90, 0.5F, 0.5F)); + + setEmitterPosition(); +} + +std::string DemoHSV::subtitle() const +{ + return "Particle HSV color system"; +} + +//------------------------------------------------------------------ +// +// DemoLifeAnimation +// +//------------------------------------------------------------------ +void DemoLifeAnimation::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + SpriteFrameCache::getInstance()->addSpriteFramesWithFile("Particles/animation_1.plist"); + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage("Particles/animation_1.png")); + + _emitter->setBlendAdditive(true); + + _emitter->setStartColor({1, 1, 1, 1}); + _emitter->setStartColorVar({0, 0, 0, 0}); + _emitter->setEndColor({1, 1, 1, 1}); + _emitter->setEndColorVar({0, 0, 0, 0}); + + _emitter->setEmissionRate(10); + + _emitter->setStartSize(16); + _emitter->setEndSize(16); + + _emitter->setLifeAnimation(true); + + _emitter->resetAnimationIndices(); + + _emitter->addAnimationIndex("particle_anim_0.png"); + _emitter->addAnimationIndex("particle_anim_1.png"); + _emitter->addAnimationIndex("particle_anim_2.png"); + _emitter->addAnimationIndex("particle_anim_3.png"); + _emitter->addAnimationIndex("particle_anim_4.png"); + _emitter->addAnimationIndex("particle_anim_5.png"); + _emitter->addAnimationIndex("particle_anim_6.png"); + _emitter->addAnimationIndex("particle_anim_7.png"); + _emitter->addAnimationIndex("particle_anim_8.png"); + _emitter->addAnimationIndex("particle_anim_9.png"); + + setEmitterPosition(); +} + +std::string DemoLifeAnimation::subtitle() const +{ + return "Particle life animation, particles should show 0 to 9 based on their life"; +} + +//------------------------------------------------------------------ +// +// DemoLifeAnimationAtlas +// +//------------------------------------------------------------------ +void DemoLifeAnimationAtlas::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage("Particles/animation_1.png")); + + _emitter->setBlendAdditive(true); + + _emitter->setStartColor({1, 1, 1, 1}); + _emitter->setStartColorVar({0, 0, 0, 0}); + _emitter->setEndColor({1, 1, 1, 1}); + _emitter->setEndColorVar({0, 0, 0, 0}); + + _emitter->setEmissionRate(10); + + _emitter->setStartSize(16); + _emitter->setEndSize(16); + + _emitter->setLifeAnimation(true); + + _emitter->resetAnimationIndices(); + + _emitter->setAnimationIndicesAtlas(); + + setEmitterPosition(); +} + +std::string DemoLifeAnimationAtlas::subtitle() const +{ + return "Particle life animation, particles should show 0 to 9 based on their life based on a texture atlas NOT using sprite frames"; +} + +//------------------------------------------------------------------ +// +// DemoLifeAnimation +// +//------------------------------------------------------------------ +void DemoLifeAnimationReversed::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + SpriteFrameCache::getInstance()->addSpriteFramesWithFile("Particles/animation_1.plist"); + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage("Particles/animation_1.png")); + + _emitter->setBlendAdditive(true); + + _emitter->setStartColor({1, 1, 1, 1}); + _emitter->setStartColorVar({0, 0, 0, 0}); + _emitter->setEndColor({1, 1, 1, 1}); + _emitter->setEndColorVar({0, 0, 0, 0}); + + _emitter->setEmissionRate(10); + + _emitter->setStartSize(16); + _emitter->setEndSize(16); + + _emitter->setLifeAnimation(true); + + _emitter->resetAnimationIndices(); + + _emitter->addAnimationIndex("particle_anim_0.png"); + _emitter->addAnimationIndex("particle_anim_1.png"); + _emitter->addAnimationIndex("particle_anim_2.png"); + _emitter->addAnimationIndex("particle_anim_3.png"); + _emitter->addAnimationIndex("particle_anim_4.png"); + _emitter->addAnimationIndex("particle_anim_5.png"); + _emitter->addAnimationIndex("particle_anim_6.png"); + _emitter->addAnimationIndex("particle_anim_7.png"); + _emitter->addAnimationIndex("particle_anim_8.png"); + _emitter->addAnimationIndex("particle_anim_9.png"); + + _emitter->setAnimationReverse(true); + + setEmitterPosition(); +} + +std::string DemoLifeAnimationReversed::subtitle() const +{ + return "Particle life animation, particles should show 9 to 0 reversed based on their life"; +} + +//------------------------------------------------------------------ +// +// DemoLoopAnimation +// +//------------------------------------------------------------------ +void DemoLoopAnimation::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + SpriteFrameCache::getInstance()->addSpriteFramesWithFile("Particles/animation_1.plist"); + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage("Particles/animation_1.png")); + + _emitter->setBlendAdditive(true); + + _emitter->setStartColor({1, 1, 1, 1}); + _emitter->setStartColorVar({0, 0, 0, 0}); + _emitter->setEndColor({1, 1, 1, 1}); + _emitter->setEndColorVar({0, 0, 0, 0}); + + _emitter->setEmissionRate(10); + + _emitter->setStartSize(16); + _emitter->setEndSize(16); + + _emitter->setLoopAnimation(true); + + _emitter->resetAnimationIndices(); + _emitter->resetAnimationDescriptors(); + + _emitter->addAnimationIndex("particle_anim_0.png"); + _emitter->addAnimationIndex("particle_anim_1.png"); + _emitter->addAnimationIndex("particle_anim_2.png"); + _emitter->addAnimationIndex("particle_anim_3.png"); + _emitter->addAnimationIndex("particle_anim_4.png"); + _emitter->addAnimationIndex("particle_anim_5.png"); + _emitter->addAnimationIndex("particle_anim_6.png"); + _emitter->addAnimationIndex("particle_anim_7.png"); + _emitter->addAnimationIndex("particle_anim_8.png"); + _emitter->addAnimationIndex("particle_anim_9.png"); + + _emitter->setAnimationDescriptor(0, 1, 0.5F, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + + setEmitterPosition(); +} + +std::string DemoLoopAnimation::subtitle() const +{ + return "Particle loop animation, particles should loop from 0 to 9 in one second with 0.5 variance"; +} + +//------------------------------------------------------------------ +// +// DemoLoopAnimationReversed +// +//------------------------------------------------------------------ +void DemoLoopAnimationReversed::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + SpriteFrameCache::getInstance()->addSpriteFramesWithFile("Particles/animation_1.plist"); + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage("Particles/animation_1.png")); + + _emitter->setBlendAdditive(true); + + _emitter->setStartColor({1, 1, 1, 1}); + _emitter->setStartColorVar({0, 0, 0, 0}); + _emitter->setEndColor({1, 1, 1, 1}); + _emitter->setEndColorVar({0, 0, 0, 0}); + + _emitter->setEmissionRate(10); + + _emitter->setStartSize(16); + _emitter->setEndSize(16); + + _emitter->setLoopAnimation(true); + + _emitter->resetAnimationIndices(); + _emitter->resetAnimationDescriptors(); + + _emitter->addAnimationIndex("particle_anim_0.png"); + _emitter->addAnimationIndex("particle_anim_1.png"); + _emitter->addAnimationIndex("particle_anim_2.png"); + _emitter->addAnimationIndex("particle_anim_3.png"); + _emitter->addAnimationIndex("particle_anim_4.png"); + _emitter->addAnimationIndex("particle_anim_5.png"); + _emitter->addAnimationIndex("particle_anim_6.png"); + _emitter->addAnimationIndex("particle_anim_7.png"); + _emitter->addAnimationIndex("particle_anim_8.png"); + _emitter->addAnimationIndex("particle_anim_9.png"); + + _emitter->setAnimationDescriptor(0, 1, 0.5F, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, true /* Reversed animation descriptor */); + + setEmitterPosition(); +} + +std::string DemoLoopAnimationReversed::subtitle() const +{ + return "Particle loop animation, particles should loop from 9 to 0 reversed in one second with 0.5 variance"; +} + +//------------------------------------------------------------------ +// +// DemoEmitterAnimation +// +//------------------------------------------------------------------ +void DemoEmitterAnimation::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + SpriteFrameCache::getInstance()->addSpriteFramesWithFile("Particles/animation_1.plist"); + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage("Particles/animation_1.png")); + + _emitter->setBlendAdditive(true); + + _emitter->setStartColor({1, 1, 1, 1}); + _emitter->setStartColorVar({0, 0, 0, 0}); + _emitter->setEndColor({1, 1, 1, 1}); + _emitter->setEndColorVar({0, 0, 0, 0}); + + _emitter->setEmissionRate(10); + + _emitter->setStartSize(16); + _emitter->setEndSize(16); + + _emitter->setEmitterAnimation(true); + + _emitter->resetAnimationIndices(); + + _emitter->addAnimationIndex("particle_anim_0.png"); + _emitter->addAnimationIndex("particle_anim_1.png"); + _emitter->addAnimationIndex("particle_anim_2.png"); + _emitter->addAnimationIndex("particle_anim_3.png"); + _emitter->addAnimationIndex("particle_anim_4.png"); + _emitter->addAnimationIndex("particle_anim_5.png"); + _emitter->addAnimationIndex("particle_anim_6.png"); + _emitter->addAnimationIndex("particle_anim_7.png"); + _emitter->addAnimationIndex("particle_anim_8.png"); + _emitter->addAnimationIndex("particle_anim_9.png"); + + setEmitterPosition(); +} + +std::string DemoEmitterAnimation::subtitle() const +{ + return "Particle emitter animation, particles should spawn with a fixed random index"; +} + +//------------------------------------------------------------------ +// +// DemoEmitterAnimation +// +//------------------------------------------------------------------ +void DemoEmitterAnimationDescriptor::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + SpriteFrameCache::getInstance()->addSpriteFramesWithFile("Particles/animation_1.plist"); + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage("Particles/animation_1.png")); + + _emitter->setBlendAdditive(true); + + _emitter->setStartColor({1, 1, 1, 1}); + _emitter->setStartColorVar({0, 0, 0, 0}); + _emitter->setEndColor({1, 1, 1, 1}); + _emitter->setEndColorVar({0, 0, 0, 0}); + + _emitter->setEmissionRate(10); + + _emitter->setStartSize(16); + _emitter->setEndSize(16); + + _emitter->setEmitterAnimation(true); + + _emitter->resetAnimationIndices(); + _emitter->resetAnimationDescriptors(); + + _emitter->addAnimationIndex("particle_anim_0.png"); + _emitter->addAnimationIndex("particle_anim_1.png"); + _emitter->addAnimationIndex("particle_anim_2.png"); + _emitter->addAnimationIndex("particle_anim_3.png"); + _emitter->addAnimationIndex("particle_anim_4.png"); + _emitter->addAnimationIndex("particle_anim_5.png"); + _emitter->addAnimationIndex("particle_anim_6.png"); + _emitter->addAnimationIndex("particle_anim_7.png"); + _emitter->addAnimationIndex("particle_anim_8.png"); + _emitter->addAnimationIndex("particle_anim_9.png"); + + _emitter->setAnimationDescriptor(0, 1, 0.5F, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + + setEmitterPosition(); +} + +std::string DemoEmitterAnimationDescriptor::subtitle() const +{ + return "Particle emitter animation, particles should have their index changed every one second with a 0.5 variance to a random index"; +} + +//------------------------------------------------------------------ +// +// DemoLoopAnimationMultiDescriptor +// +//------------------------------------------------------------------ +void DemoLoopAnimationMultiDescriptor::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + SpriteFrameCache::getInstance()->addSpriteFramesWithFile("Particles/animation_1.plist"); + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage("Particles/animation_1.png")); + + _emitter->setBlendAdditive(true); + + _emitter->setStartColor({1, 1, 1, 1}); + _emitter->setStartColorVar({0, 0, 0, 0}); + _emitter->setEndColor({1, 1, 1, 1}); + _emitter->setEndColorVar({0, 0, 0, 0}); + + _emitter->setEmissionRate(10); + + _emitter->setStartSize(16); + _emitter->setEndSize(16); + + _emitter->setLoopAnimation(true); + + _emitter->resetAnimationIndices(); + _emitter->resetAnimationDescriptors(); + + _emitter->addAnimationIndex("particle_anim_0.png"); + _emitter->addAnimationIndex("particle_anim_1.png"); + _emitter->addAnimationIndex("particle_anim_2.png"); + _emitter->addAnimationIndex("particle_anim_3.png"); + _emitter->addAnimationIndex("particle_anim_4.png"); + _emitter->addAnimationIndex("particle_anim_5.png"); + _emitter->addAnimationIndex("particle_anim_6.png"); + _emitter->addAnimationIndex("particle_anim_7.png"); + _emitter->addAnimationIndex("particle_anim_8.png"); + _emitter->addAnimationIndex("particle_anim_9.png"); + + _emitter->setAnimationDescriptor(0, 1, 0.5F, {1, 3, 8}); + _emitter->setAnimationDescriptor(1, 1, 0.5F, {0, 1}); + _emitter->setAnimationDescriptor(2, 1, 0.5F, {0, 9}); + + setEmitterPosition(); +} + +std::string DemoLoopAnimationMultiDescriptor::subtitle() const +{ + return "Particle loop animation, particles should play a random animation descriptor"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapePoint +// +//------------------------------------------------------------------ +void DemoEmissionShapePoint::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createPointShape({40, 40})); + _emitter->addEmissionShape(ParticleSystem::createPointShape({-40, 40})); + _emitter->addEmissionShape(ParticleSystem::createPointShape({40, -40})); + _emitter->addEmissionShape(ParticleSystem::createPointShape({-40, -40})); + + setEmitterPosition(); +} + +std::string DemoEmissionShapePoint::subtitle() const +{ + return "Particle emission shape point"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeRect +// +//------------------------------------------------------------------ +void DemoEmissionShapeRect::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createRectShape({0, 0}, {300, 100})); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeRect::subtitle() const +{ + return "Particle emission shape rectangle"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeRectTorus +// +//------------------------------------------------------------------ +void DemoEmissionShapeRectTorus::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createRectTorusShape({0, 0}, {100, 100}, {150, 150})); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeRectTorus::subtitle() const +{ + return "Particle emission shape rectangular torus"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeCircle +// +//------------------------------------------------------------------ +void DemoEmissionShapeCircle::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createCircleShape({0, 0}, 150, 2.0F /* Bias */)); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeCircle::subtitle() const +{ + return "Particle emission shape circle"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeCircleBias +// +//------------------------------------------------------------------ +void DemoEmissionShapeCircleBiasEdge::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createCircleShape({0, 0}, 150, 6.0F /* Bias */)); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeCircleBiasEdge::subtitle() const +{ + return "Particle emission shape circle with bias towards the edge"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeCircleBiasCenter +// +//------------------------------------------------------------------ +void DemoEmissionShapeCircleBiasCenter::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createCircleShape({0, 0}, 150, 0.75F /* Bias */)); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeCircleBiasCenter::subtitle() const +{ + return "Particle emission shape circle with bias towards the center"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeTorus +// +//------------------------------------------------------------------ +void DemoEmissionShapeTorus::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createTorusShape({0, 0}, 80, 150, 2.0F /* Bias */)); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeTorus::subtitle() const +{ + return "Particle emission shape torus"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeTorusBiasEdge +// +//------------------------------------------------------------------ +void DemoEmissionShapeTorusBiasEdge::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createTorusShape({0, 0}, 80, 150, 6.0F /* Bias */)); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeTorusBiasEdge::subtitle() const +{ + return "Particle emission shape torus with bias towards the edge"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeTorusBiasInner +// +//------------------------------------------------------------------ +void DemoEmissionShapeTorusBiasInner::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createTorusShape({0, 0}, 80, 150, 0.5F /* Bias */)); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeTorusBiasInner::subtitle() const +{ + return "Particle emission shape torus with bias towards the inner radius"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeCone +// +//------------------------------------------------------------------ +void DemoEmissionShapeCone::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createConeShape({0, 0}, 150, 0, 90, 2.0F /* Bias */)); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeCone::subtitle() const +{ + return "Particle emission shape cone"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeCone +// +//------------------------------------------------------------------ +void DemoEmissionShapeConeTorus::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::create(); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(10); + + _emitter->setEmissionShapes(true); + + _emitter->addEmissionShape(ParticleSystem::createConeTorusShape({0, 0}, 100, 150, 0, 120, 2.0F /* Bias */)); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeConeTorus::subtitle() const +{ + return "Particle emission shape cone torus"; +} + +//------------------------------------------------------------------ +// +// DemoEmissionShapeAlphaMask +// +//------------------------------------------------------------------ +void DemoEmissionShapeAlphaMask::onEnter() +{ + ParticleDemo::onEnter(); + + _color->setColor(Color3B::BLACK); + removeChild(_background, true); + _background = nullptr; + + _emitter = ParticleFireworks::createWithTotalParticles(8000); + _emitter->retain(); + addChild(_emitter, 10); + _emitter->setTexture(Director::getInstance()->getTextureCache()->addImage(s_stars1)); + + _emitter->setBlendAdditive(true); + + _emitter->setGravity({0, 0}); + _emitter->setSpeed(0); + _emitter->setSpeedVar(0); + + _emitter->setStartSize(3); + _emitter->setEndSize(3); + + _emitter->setEmissionRate(1000); + + _emitter->setEmissionShapes(true); + + auto cache = ParticleEmissionMaskCache::getInstance(); + cache->bakeEmissionMask("mask_001"sv, "Particles/mask.png", 0.5F, false, 1); + + _emitter->addEmissionShape(ParticleSystem::createMaskShape("mask_001"sv, {0, 0}, {400, 200}, Vec2::ONE)); + + setEmitterPosition(); +} + +std::string DemoEmissionShapeAlphaMask::subtitle() const +{ + return "Particle emission shape alpha mask texture"; +} + //------------------------------------------------------------------ // // RadiusMode1 @@ -1051,6 +2168,37 @@ ParticleTests::ParticleTests() addTestCase("ButterFlyYFlipped", []() { return DemoParticleFromFile::create("ButterFlyYFlipped"); }); ADD_TEST_CASE(RadiusMode1); ADD_TEST_CASE(RadiusMode2); + + ADD_TEST_CASE(DemoFixedFPS); + ADD_TEST_CASE(DemoTimeScale); + ADD_TEST_CASE(DemoSimulation); + ADD_TEST_CASE(DemoSpawnFadeIn); + ADD_TEST_CASE(DemoScaleFadeIn); + ADD_TEST_CASE(DemoSpawnRotation); + ADD_TEST_CASE(DemoHSV); + + ADD_TEST_CASE(DemoLifeAnimation); + ADD_TEST_CASE(DemoLifeAnimationAtlas); + ADD_TEST_CASE(DemoLifeAnimationReversed); + ADD_TEST_CASE(DemoLoopAnimation); + ADD_TEST_CASE(DemoLoopAnimationReversed); + ADD_TEST_CASE(DemoLoopAnimationMultiDescriptor); + ADD_TEST_CASE(DemoEmitterAnimation); + ADD_TEST_CASE(DemoEmitterAnimationDescriptor); + + ADD_TEST_CASE(DemoEmissionShapePoint); + ADD_TEST_CASE(DemoEmissionShapeRect); + ADD_TEST_CASE(DemoEmissionShapeRectTorus); + ADD_TEST_CASE(DemoEmissionShapeCircle); + ADD_TEST_CASE(DemoEmissionShapeCircleBiasEdge); + ADD_TEST_CASE(DemoEmissionShapeCircleBiasCenter); + ADD_TEST_CASE(DemoEmissionShapeTorus); + ADD_TEST_CASE(DemoEmissionShapeTorusBiasEdge); + ADD_TEST_CASE(DemoEmissionShapeTorusBiasInner); + ADD_TEST_CASE(DemoEmissionShapeCone); + ADD_TEST_CASE(DemoEmissionShapeConeTorus); + ADD_TEST_CASE(DemoEmissionShapeAlphaMask); + ADD_TEST_CASE(Issue704); ADD_TEST_CASE(Issue870); ADD_TEST_CASE(Issue1201); diff --git a/tests/cpp-tests/Classes/ParticleTest/ParticleTest.h b/tests/cpp-tests/Classes/ParticleTest/ParticleTest.h index 42ec6f9ee0..3d8b780b53 100644 --- a/tests/cpp-tests/Classes/ParticleTest/ParticleTest.h +++ b/tests/cpp-tests/Classes/ParticleTest/ParticleTest.h @@ -182,6 +182,225 @@ public: virtual std::string subtitle() const override; }; +class DemoFixedFPS : public ParticleDemo +{ +public: + CREATE_FUNC(DemoFixedFPS); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoTimeScale: public ParticleDemo +{ +public: + CREATE_FUNC(DemoTimeScale); + virtual void onEnter() override; + virtual std::string subtitle() const override; + virtual void update(float dt) override; + + float elapsedTime = 0.0F; +}; + +class DemoSpawnFadeIn : public ParticleDemo +{ +public: + CREATE_FUNC(DemoSpawnFadeIn); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoScaleFadeIn : public ParticleDemo +{ +public: + CREATE_FUNC(DemoScaleFadeIn); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoSimulation : public ParticleDemo +{ +public: + CREATE_FUNC(DemoSimulation); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoSpawnRotation : public ParticleDemo +{ +public: + CREATE_FUNC(DemoSpawnRotation); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoHSV : public ParticleDemo +{ +public: + CREATE_FUNC(DemoHSV); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoLifeAnimation : public ParticleDemo +{ +public: + CREATE_FUNC(DemoLifeAnimation); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoLifeAnimationAtlas : public ParticleDemo +{ +public: + CREATE_FUNC(DemoLifeAnimationAtlas); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoLifeAnimationReversed : public ParticleDemo +{ +public: + CREATE_FUNC(DemoLifeAnimationReversed); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoLoopAnimation : public ParticleDemo +{ +public: + CREATE_FUNC(DemoLoopAnimation); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoLoopAnimationReversed : public ParticleDemo +{ +public: + CREATE_FUNC(DemoLoopAnimationReversed); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmitterAnimation : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmitterAnimation); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmitterAnimationDescriptor : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmitterAnimationDescriptor); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoLoopAnimationMultiDescriptor : public ParticleDemo +{ +public: + CREATE_FUNC(DemoLoopAnimationMultiDescriptor); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapePoint : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapePoint); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeRect : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeRect); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeRectTorus : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeRectTorus); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeCircle : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeCircle); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeCircleBiasEdge : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeCircleBiasEdge); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeCircleBiasCenter : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeCircleBiasCenter); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeTorus : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeTorus); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeTorusBiasEdge : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeTorusBiasEdge); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeTorusBiasInner : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeTorusBiasInner); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeCone : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeCone); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeConeTorus : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeConeTorus); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + +class DemoEmissionShapeAlphaMask : public ParticleDemo +{ +public: + CREATE_FUNC(DemoEmissionShapeAlphaMask); + virtual void onEnter() override; + virtual std::string subtitle() const override; +}; + class DemoParticleFromFile : public ParticleDemo { public: diff --git a/tests/cpp-tests/Resources/Particles/animation_1.plist b/tests/cpp-tests/Resources/Particles/animation_1.plist new file mode 100644 index 0000000000..4e951331a7 --- /dev/null +++ b/tests/cpp-tests/Resources/Particles/animation_1.plist @@ -0,0 +1,176 @@ + + + + + frames + + particle_anim_0.png + + aliases + + spriteOffset + {0,0} + spriteSize + {64,64} + spriteSourceSize + {64,64} + textureRect + {{0,0},{64,64}} + textureRotated + + + particle_anim_1.png + + aliases + + spriteOffset + {0,0} + spriteSize + {64,64} + spriteSourceSize + {64,64} + textureRect + {{64,0},{64,64}} + textureRotated + + + particle_anim_2.png + + aliases + + spriteOffset + {0,0} + spriteSize + {64,64} + spriteSourceSize + {64,64} + textureRect + {{128,0},{64,64}} + textureRotated + + + particle_anim_3.png + + aliases + + spriteOffset + {0,0} + spriteSize + {64,64} + spriteSourceSize + {64,64} + textureRect + {{192,0},{64,64}} + textureRotated + + + particle_anim_4.png + + aliases + + spriteOffset + {0,0} + spriteSize + {64,64} + spriteSourceSize + {64,64} + textureRect + {{256,0},{64,64}} + textureRotated + + + particle_anim_5.png + + aliases + + spriteOffset + {0,0} + spriteSize + {64,64} + spriteSourceSize + {64,64} + textureRect + {{320,0},{64,64}} + textureRotated + + + particle_anim_6.png + + aliases + + spriteOffset + {0,0} + spriteSize + {64,64} + spriteSourceSize + {64,64} + textureRect + {{384,0},{64,64}} + textureRotated + + + particle_anim_7.png + + aliases + + spriteOffset + {0,0} + spriteSize + {64,64} + spriteSourceSize + {64,64} + textureRect + {{448,0},{64,64}} + textureRotated + + + particle_anim_8.png + + aliases + + spriteOffset + {0,0} + spriteSize + {64,64} + spriteSourceSize + {64,64} + textureRect + {{512,0},{64,64}} + textureRotated + + + particle_anim_9.png + + aliases + + spriteOffset + {0,0} + spriteSize + {64,64} + spriteSourceSize + {64,64} + textureRect + {{576,0},{64,64}} + textureRotated + + + + metadata + + format + 3 + pixelFormat + RGBA8888 + premultiplyAlpha + + realTextureFileName + animation_1.png + size + {640,64} + smartupdate + $TexturePacker:SmartUpdate:da26e187ca02fabf90d1710d32ee7cf9:36c91cc4cb0ce67ab1706c682518e467:5e97592c8b58bf2d3aec7c12fc605c76$ + textureFileName + animation_1.png + + + diff --git a/tests/cpp-tests/Resources/Particles/animation_1.png b/tests/cpp-tests/Resources/Particles/animation_1.png new file mode 100644 index 0000000000000000000000000000000000000000..8e057a23bdd0fded46f648ff24add43110a8e63a GIT binary patch literal 8121 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 L+YnNzX&?1J9cT7k literal 0 HcmV?d00001 diff --git a/tests/cpp-tests/Resources/Particles/mask.png b/tests/cpp-tests/Resources/Particles/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..292b6279fe4e8f35a899aee0293d85cb965d5d33 GIT binary patch literal 17534 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 Date: Thu, 16 Jun 2022 03:55:46 +0300 Subject: [PATCH 56/61] Fix fixed fps move stuttering. --- core/2d/CCParticleSystem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 992af5538e..1319459651 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -1563,6 +1563,8 @@ void ParticleSystem::update(float dt) _fixedFPSDelta += dt; if (_fixedFPSDelta < 1.0F / _fixedFPS) { + updateParticleQuads(); + _transformSystemDirty = false; CC_PROFILER_STOP_CATEGORY(kProfilerCategoryParticles, "CCParticleSystem - update"); return; } From ca4d1524dc17556502d69e7c3f67715f944717df Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 16 Jun 2022 05:01:35 +0300 Subject: [PATCH 57/61] extract RngSeed struct to it's separate class. [skip ci] --- core/2d/CCParticleSystem.h | 83 +---------------------------------- core/math/RngSeed.hpp | 88 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 82 deletions(-) create mode 100644 core/math/RngSeed.hpp diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 65c9018563..5da0aef4fd 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -34,6 +34,7 @@ THE SOFTWARE. #include "base/CCValue.h" #include "2d/CCSpriteFrame.h" #include "2d/CCSpriteFrameCache.h" +#include "math/RngSeed.hpp" NS_CC_BEGIN @@ -54,88 +55,6 @@ struct particle_point float y; }; -//* A more effective random number generator function that fixes strafing for position variance, made by kiss rng. -struct RngSeed -{ - const unsigned long RNG_RAND_MAX = 4294967295; - const unsigned long RNG_RAND_MAX_SIGNED = 2147483647; - unsigned long _x = 1; - unsigned long _y = 2; - unsigned long _z = 4; - unsigned long _w = 8; - unsigned long _carry = 0; - unsigned long _k; - unsigned long _m; - unsigned long _seed; - - RngSeed() { seed_rand(time(NULL)); } - - // initialize this object with seed - void seed_rand(unsigned long seed) - { - _seed = seed; - _x = seed | 1; - _y = seed | 2; - _z = seed | 4; - _w = seed | 8; - _carry = 0; - } - - // returns an unsigned long random value - unsigned long rand() - { - _x = _x * 69069 + 1; - _y ^= _y << 13; - _y ^= _y >> 17; - _y ^= _y << 5; - _k = (_z >> 2) + (_w >> 3) + (_carry >> 2); - _m = _w + _w + _z + _carry; - _z = _w; - _w = _m; - _carry = _k >> 30; - return _x + _y + _w; - } - - // returns a random integer from min to max - int range(int min, int max) - { - return floor(min + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - min)))); - } - - // returns a random unsigned integer from min to max - unsigned int rangeu(unsigned int min, unsigned int max) - { - return floor(min + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - min)))); - } - - // returns a random float from min to max - float rangef(float min = -1.0F, float max = 1.0F) - { - return min + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - min))); - } - - // returns a random integer from 0 to max - int max(int max = INT_MAX) - { - return floor(0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - 0)))); - } - - // returns a random unsigned integer from 0 to max - unsigned int maxu(unsigned int max = UINT_MAX) - { - return floor(0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - 0)))); - } - - // returns a random float from 0.0 to max - float maxf(float max) { return 0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - 0))); } - - // returns a random float from 0.0 to 1.0 - float float01() { return 0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (1 - 0))); } - - // returns either false or true randomly - bool bool01() { return (bool)floor(0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (2 - 0)))); } -}; - /** * Particle emission shapes. * Current supported shapes are Point, Rectangle, RectangularTorus, Circle, Torus diff --git a/core/math/RngSeed.hpp b/core/math/RngSeed.hpp new file mode 100644 index 0000000000..3c7f5390b5 --- /dev/null +++ b/core/math/RngSeed.hpp @@ -0,0 +1,88 @@ +#ifndef RNGSEED_H_ +#define RNGSEED_H_ + +/** A more effective seeded random number generator struct, made by kiss rng. + * @since adxe-1.0.0b8 + */ +struct RngSeed +{ + const unsigned long RNG_RAND_MAX = 4294967295; + const unsigned long RNG_RAND_MAX_SIGNED = 2147483647; + unsigned long _x = 1; + unsigned long _y = 2; + unsigned long _z = 4; + unsigned long _w = 8; + unsigned long _carry = 0; + unsigned long _k; + unsigned long _m; + unsigned long _seed; + + RngSeed() { seed_rand(time(NULL)); } + + // initialize this object with seed + void seed_rand(unsigned long seed) + { + _seed = seed; + _x = seed | 1; + _y = seed | 2; + _z = seed | 4; + _w = seed | 8; + _carry = 0; + } + + // returns an unsigned long random value + unsigned long rand() + { + _x = _x * 69069 + 1; + _y ^= _y << 13; + _y ^= _y >> 17; + _y ^= _y << 5; + _k = (_z >> 2) + (_w >> 3) + (_carry >> 2); + _m = _w + _w + _z + _carry; + _z = _w; + _w = _m; + _carry = _k >> 30; + return _x + _y + _w; + } + + // returns a random integer from min to max + int range(int min, int max) + { + return floor(min + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - min)))); + } + + // returns a random unsigned integer from min to max + unsigned int rangeu(unsigned int min, unsigned int max) + { + return floor(min + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - min)))); + } + + // returns a random float from min to max + float rangef(float min = -1.0F, float max = 1.0F) + { + return min + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - min))); + } + + // returns a random integer from 0 to max + int max(int max = INT_MAX) + { + return floor(0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - 0)))); + } + + // returns a random unsigned integer from 0 to max + unsigned int maxu(unsigned int max = UINT_MAX) + { + return floor(0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - 0)))); + } + + // returns a random float from 0.0 to max + float maxf(float max) { return 0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (max - 0))); } + + // returns a random float from 0.0 to 1.0 + float float01() { return 0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (1 - 0))); } + + // returns either false or true randomly + bool bool01() { return (bool)floor(0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (2 - 0)))); } +}; + +#endif \ No newline at end of file From ce77ba4c72068b2e3de3282dae95a702761ea579 Mon Sep 17 00:00:00 2001 From: Turky Mohammed <45469625+DelinWorks@users.noreply.github.com> Date: Thu, 16 Jun 2022 05:14:56 +0300 Subject: [PATCH 58/61] Update RngSeed.hpp [skip ci] --- core/math/RngSeed.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/math/RngSeed.hpp b/core/math/RngSeed.hpp index 3c7f5390b5..ae93615d39 100644 --- a/core/math/RngSeed.hpp +++ b/core/math/RngSeed.hpp @@ -17,7 +17,7 @@ struct RngSeed unsigned long _m; unsigned long _seed; - RngSeed() { seed_rand(time(NULL)); } + RngSeed() : _k(0) { seed_rand(time(NULL)); } // initialize this object with seed void seed_rand(unsigned long seed) @@ -85,4 +85,4 @@ struct RngSeed bool bool01() { return (bool)floor(0 + static_cast(rand()) / (static_cast(RNG_RAND_MAX / (2 - 0)))); } }; -#endif \ No newline at end of file +#endif From 26013c7d1528a6a03f2eca00ad2a3a34c0f7c723 Mon Sep 17 00:00:00 2001 From: Turky Mohammed <45469625+DelinWorks@users.noreply.github.com> Date: Thu, 16 Jun 2022 05:23:41 +0300 Subject: [PATCH 59/61] Update RngSeed.hpp [skip ci] --- core/math/RngSeed.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/math/RngSeed.hpp b/core/math/RngSeed.hpp index ae93615d39..d983bf1bfc 100644 --- a/core/math/RngSeed.hpp +++ b/core/math/RngSeed.hpp @@ -13,11 +13,11 @@ struct RngSeed unsigned long _z = 4; unsigned long _w = 8; unsigned long _carry = 0; - unsigned long _k; - unsigned long _m; - unsigned long _seed; + unsigned long _k = 0; + unsigned long _m = 0; + unsigned long _seed = 0; - RngSeed() : _k(0) { seed_rand(time(NULL)); } + RngSeed() { seed_rand(time(NULL)); } // initialize this object with seed void seed_rand(unsigned long seed) From 2b2a57e8ce9deea907f5d1e572f67a31306463e3 Mon Sep 17 00:00:00 2001 From: Turky Mohammed <45469625+DelinWorks@users.noreply.github.com> Date: Thu, 16 Jun 2022 14:13:59 +0300 Subject: [PATCH 60/61] Update CCParticleSystem.h --- core/2d/CCParticleSystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index 5da0aef4fd..fcc6b4b77a 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -103,7 +103,7 @@ struct EmissionShape float coneAngle; float edgeElasticity; - std::string_view maskName; + std::string maskName; }; /** @struct ParticleAnimationDescriptor From 2e3c84252d6d621c716d577f9882a0d9aac8269d Mon Sep 17 00:00:00 2001 From: DelinWorks Date: Thu, 16 Jun 2022 15:25:43 +0300 Subject: [PATCH 61/61] Add fourccId support. --- core/2d/CCParticleSystem.cpp | 61 ++++++++++++------- core/2d/CCParticleSystem.h | 32 ++++++---- core/base/ccUtils.cpp | 9 +++ core/base/ccUtils.h | 2 + .../Classes/ParticleTest/ParticleTest.cpp | 4 +- 5 files changed, 71 insertions(+), 37 deletions(-) diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index 1319459651..ff7c74a3ac 100644 --- a/core/2d/CCParticleSystem.cpp +++ b/core/2d/CCParticleSystem.cpp @@ -796,7 +796,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe case EmissionShapeType::CIRCLE: { auto val = _rng.float01() * shape.innerRadius / shape.innerRadius; - val = powf(val, 1 / shape.edgeElasticity); + val = powf(val, 1 / shape.edgeBias); auto point = Vec2(0.0F, val * shape.innerRadius); point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(shape.coneOffset + shape.coneAngle / 2 * _rng.rangef())); _particleData.posx[i] = _sourcePosition.x + shape.x + point.x / 2; @@ -807,7 +807,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe case EmissionShapeType::TORUS: { auto val = _rng.float01() * shape.outerRadius / shape.outerRadius; - val = powf(val, 1 / shape.edgeElasticity); + val = powf(val, 1 / shape.edgeBias); auto point = Vec2(0.0F, ((val * (shape.outerRadius - shape.innerRadius) + shape.outerRadius) - (shape.outerRadius - shape.innerRadius))); point = point.rotateByAngle(Vec2::ZERO, -CC_DEGREES_TO_RADIANS(shape.coneOffset + shape.coneAngle / 2 * _rng.rangef())); _particleData.posx[i] = _sourcePosition.x + shape.x + point.x / 2; @@ -817,7 +817,7 @@ void ParticleSystem::addParticles(int count, int animationIndex, int animationCe } case EmissionShapeType::ALPHA_MASK: { - auto& mask = ParticleEmissionMaskCache::getInstance()->getEmissionMask(shape.maskName); + auto& mask = ParticleEmissionMaskCache::getInstance()->getEmissionMask(shape.fourccId); Vec2 pos = {shape.x, shape.y}; Vec2 size = mask.size; @@ -1151,7 +1151,7 @@ void ParticleSystem::setEmissionShape(unsigned short index, EmissionShape shape) iter->second = shape; } -EmissionShape ParticleSystem::createMaskShape(std::string_view maskName, +EmissionShape ParticleSystem::createMaskShape(std::string_view maskId, Vec2 pos, Vec2 overrideSize, Vec2 scale, @@ -1161,7 +1161,7 @@ EmissionShape ParticleSystem::createMaskShape(std::string_view maskName, shape.type = EmissionShapeType::ALPHA_MASK; - shape.maskName = maskName; + shape.fourccId = utils::fourccValue(maskId); shape.x = pos.x; shape.y = pos.y; @@ -1222,7 +1222,7 @@ EmissionShape ParticleSystem::createRectTorusShape(Vec2 pos, Size innerSize, Siz return shape; } -EmissionShape ParticleSystem::createCircleShape(Vec2 pos, float radius, float edgeElasticity) +EmissionShape ParticleSystem::createCircleShape(Vec2 pos, float radius, float edgeBias) { EmissionShape shape{}; @@ -1236,7 +1236,7 @@ EmissionShape ParticleSystem::createCircleShape(Vec2 pos, float radius, float ed shape.coneOffset = 0; shape.coneAngle = 360; - shape.edgeElasticity = edgeElasticity; + shape.edgeBias = edgeBias; return shape; } @@ -1255,7 +1255,7 @@ EmissionShape ParticleSystem::createConeShape(Vec2 pos, float radius, float offs shape.coneOffset = offset; shape.coneAngle = angle; - shape.edgeElasticity = edgeBias; + shape.edgeBias = edgeBias; return shape; } @@ -1275,7 +1275,7 @@ EmissionShape ParticleSystem::createTorusShape(Vec2 pos, float innerRadius, floa shape.coneOffset = 0; shape.coneAngle = 360; - shape.edgeElasticity = edgeBias; + shape.edgeBias = edgeBias; return shape; } @@ -1300,7 +1300,7 @@ EmissionShape ParticleSystem::createConeTorusShape(Vec2 pos, shape.coneOffset = offset; shape.coneAngle = angle; - shape.edgeElasticity = edgeBias; + shape.edgeBias = edgeBias; return shape; } @@ -2269,7 +2269,7 @@ ParticleEmissionMaskCache* ParticleEmissionMaskCache::getInstance() return emissionMaskCache; } -void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, +void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskId, std::string_view texturePath, float alphaThreshold, bool inverted, @@ -2280,10 +2280,10 @@ void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, img->autorelease(); CCASSERT(img, "image texture was nullptr."); - bakeEmissionMask(maskName, img, alphaThreshold, inverted, inbetweenSamples); + bakeEmissionMask(maskId, img, alphaThreshold, inverted, inbetweenSamples); } -void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, +void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskId, Image* imageTexture, float alphaThreshold, bool inverted, @@ -2322,9 +2322,11 @@ void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, } } - auto iter = this->masks.find(maskName); + auto fourccId = utils::fourccValue(maskId); + + auto iter = this->masks.find(fourccId); if (iter == this->masks.end()) - iter = this->masks.emplace(maskName, ParticleEmissionMaskDescriptor{}).first; + iter = this->masks.emplace(fourccId, ParticleEmissionMaskDescriptor{}).first; ParticleEmissionMaskDescriptor desc; desc.size = {float(w), float(h)}; @@ -2332,16 +2334,16 @@ void ParticleEmissionMaskCache::bakeEmissionMask(std::string_view maskName, iter->second = desc; - CCLOG("Particle emission mask '%s' baked (%dx%d), %d samples generated taking %.2fmb of memory.", - std::string(maskName).c_str(), w, h, desc.points.size(), desc.points.size() * 8 / 1e+6); + CCLOG("Particle emission mask '%lu' baked (%dx%d), %d samples generated taking %.2fmb of memory.", + (unsigned long)htonl(fourccId), w, h, desc.points.size(), desc.points.size() * 8 / 1e+6); } -const ParticleEmissionMaskDescriptor& ParticleEmissionMaskCache::getEmissionMask(std::string_view maskName) +const ParticleEmissionMaskDescriptor& ParticleEmissionMaskCache::getEmissionMask(uint32_t fourccId) { - auto iter = this->masks.find(maskName); + auto iter = this->masks.find(fourccId); if (iter == this->masks.end()) { - iter = this->masks.emplace(maskName, ParticleEmissionMaskDescriptor{}).first; + iter = this->masks.emplace(fourccId, ParticleEmissionMaskDescriptor{}).first; iter->second.size = {float(1), float(1)}; iter->second.points = {{0, 0}}; return iter->second; @@ -2349,9 +2351,24 @@ const ParticleEmissionMaskDescriptor& ParticleEmissionMaskCache::getEmissionMask return iter->second; } -void ParticleEmissionMaskCache::removeMask(std::string_view maskName) +const ParticleEmissionMaskDescriptor& ParticleEmissionMaskCache::getEmissionMask(std::string_view maskId) { - this->masks.erase(maskName); + auto fourccId = utils::fourccValue(maskId); + + auto iter = this->masks.find(fourccId); + if (iter == this->masks.end()) + { + iter = this->masks.emplace(fourccId, ParticleEmissionMaskDescriptor{}).first; + iter->second.size = {float(1), float(1)}; + iter->second.points = {{0, 0}}; + return iter->second; + } + return iter->second; +} + +void ParticleEmissionMaskCache::removeMask(std::string_view maskId) +{ + this->masks.erase(utils::fourccValue(maskId)); } void ParticleEmissionMaskCache::removeAllMasks() diff --git a/core/2d/CCParticleSystem.h b/core/2d/CCParticleSystem.h index fcc6b4b77a..f8f94820cc 100644 --- a/core/2d/CCParticleSystem.h +++ b/core/2d/CCParticleSystem.h @@ -101,9 +101,9 @@ struct EmissionShape float outerRadius; float coneOffset; float coneAngle; - float edgeElasticity; + float edgeBias; - std::string maskName; + uint32_t fourccId; }; /** @struct ParticleAnimationDescriptor @@ -270,7 +270,7 @@ public: /** Bakes a particle emission mask from texture data on cpu and stores it in memory by it's name. * If the mask already exists then it will be overwritten. * - * @param maskName The name that identifies the mask. + * @param maskId The id of the mask, FOURCC starts with '#', such as "#abcd" * @param texturePath Path of the texture that holds alpha data. * @param alphaThreshold The threshold at which pixels are picked, If a pixel's alpha channel is greater than * alphaThreshold then it will be picked. @@ -279,7 +279,7 @@ public: * @param inbetweenSamples How many times should pixels be filled inbetween, this value should be increased If * you're planning to scale the emission shape up. WARNING: it will use more memory. */ - void bakeEmissionMask(std::string_view maskName, + void bakeEmissionMask(std::string_view maskId, std::string_view texturePath, float alphaThreshold = 0.5F, bool inverted = false, @@ -288,7 +288,7 @@ public: /** Bakes a particle emission mask from texture data on cpu and stores it in memory by it's name. * If the mask already exists then it will be overwritten. * - * @param maskName The name that identifies the mask. + * @param maskId The id of the mask, FOURCC starts with '#', such as "#abcd" * @param imageTexture Image object containing texture data with alpha channel. * @param alphaThreshold The threshold at which pixels are picked, If a pixel's alpha channel is greater than * alphaThreshold then it will be picked. @@ -297,7 +297,7 @@ public: * @param inbetweenSamples How many times should pixels be filled inbetween, this value should be increased If * you're planning to scale the emission shape up. WARNING: it will use more memory. */ - void bakeEmissionMask(std::string_view maskName, + void bakeEmissionMask(std::string_view maskId, Image* imageTexture, float alphaThreshold = 0.5F, bool inverted = false, @@ -305,21 +305,27 @@ public: /** Returns a baked mask with the specified name if it exists. otherwise, it will return a dummy mask. * - * @param maskName The name that identifies the mask. + * @param fourccId The unsigned integer id of the mask. */ - const ParticleEmissionMaskDescriptor& getEmissionMask(std::string_view maskName); + const ParticleEmissionMaskDescriptor& getEmissionMask(uint32_t fourccId); + + /** Returns a baked mask with the specified name if it exists. otherwise, it will return a dummy mask. + * + * @param maskId The id of the mask, FOURCC starts with '#', such as "#abcd" + */ + const ParticleEmissionMaskDescriptor& getEmissionMask(std::string_view maskId); /** Removes a baked mask and releases the data from memory with the specified name if it exists. * - * @param maskName The name that identifies the mask. + * @param maskId The id of the mask, FOURCC starts with '#', such as "#abcd" */ - void removeMask(std::string_view maskName); + void removeMask(std::string_view maskId); /** Remove all baked masks and releases their data from memory. */ void removeAllMasks(); private: - hlookup::string_map masks; + std::unordered_map masks; }; @@ -1245,13 +1251,13 @@ public: /** Adds an emission shape of type mask to the system. * The mask should be added using the ParticleEmissionMaskCache class. * - * @param maskName Name of the emission mask. + * @param maskId The id of the mask, FOURCC starts with '#', such as "#abcd" * @param pos Position of the emission shape in local space. * @param overrideSize Size of the emission mask in pixels, leave ZERO to use texture size. * @param scale Scale of the emission mask, the size will be multiplied by the specified scale. * @param angle Angle of the sampled points to be rotated in degrees. */ - static EmissionShape createMaskShape(std::string_view maskName, Vec2 pos = Vec2::ZERO, Vec2 overrideSize = Vec2::ZERO, Vec2 scale = Vec2::ONE, float angle = 0.0F); + static EmissionShape createMaskShape(std::string_view maskId, Vec2 pos = Vec2::ZERO, Vec2 overrideSize = Vec2::ZERO, Vec2 scale = Vec2::ONE, float angle = 0.0F); /** Adds an emission shape of type point to the system. * @param pos Position of the emission shape in local space. diff --git a/core/base/ccUtils.cpp b/core/base/ccUtils.cpp index 9d43f9ee6b..8c64385aa1 100644 --- a/core/base/ccUtils.cpp +++ b/core/base/ccUtils.cpp @@ -780,6 +780,15 @@ std::string urlDecode(std::string_view st) return decoded; } +CC_DLL uint32_t fourccValue(std::string_view str) +{ + if (str.empty() || str[0] != '#') + return (uint32_t)-1; + uint32_t value = 0; + memcpy(&value, str.data() + 1, std::min(sizeof(value), str.size() - 1)); + return value; +} + } // namespace utils NS_CC_END diff --git a/core/base/ccUtils.h b/core/base/ccUtils.h index 70a0a1498e..29546d40a4 100644 --- a/core/base/ccUtils.h +++ b/core/base/ccUtils.h @@ -404,6 +404,8 @@ inline char* char2hex(char* p, unsigned char c, unsigned char a = 'a') CC_DLL std::string urlEncode(std::string_view s); CC_DLL std::string urlDecode(std::string_view st); + +CC_DLL uint32_t fourccValue(std::string_view str); } // namespace utils NS_CC_END diff --git a/tests/cpp-tests/Classes/ParticleTest/ParticleTest.cpp b/tests/cpp-tests/Classes/ParticleTest/ParticleTest.cpp index 39c1749c7f..ee20f23fb7 100644 --- a/tests/cpp-tests/Classes/ParticleTest/ParticleTest.cpp +++ b/tests/cpp-tests/Classes/ParticleTest/ParticleTest.cpp @@ -1783,9 +1783,9 @@ void DemoEmissionShapeAlphaMask::onEnter() _emitter->setEmissionShapes(true); auto cache = ParticleEmissionMaskCache::getInstance(); - cache->bakeEmissionMask("mask_001"sv, "Particles/mask.png", 0.5F, false, 1); + cache->bakeEmissionMask("#msk1"sv, "Particles/mask.png", 0.5F, false, 1); - _emitter->addEmissionShape(ParticleSystem::createMaskShape("mask_001"sv, {0, 0}, {400, 200}, Vec2::ONE)); + _emitter->addEmissionShape(ParticleSystem::createMaskShape("#msk1"sv, {0, 0}, {400, 200}, Vec2::ONE)); setEmitterPosition(); }