diff --git a/core/2d/CCParticleSystem.cpp b/core/2d/CCParticleSystem.cpp index daaf7e9928..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) { @@ -121,24 +122,30 @@ 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)); + staticRotation = (float*)malloc(count * sizeof(float)); + 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)); + 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)); modeA.dirY = (float*)malloc(count * sizeof(float)); @@ -151,9 +158,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() @@ -173,8 +180,14 @@ 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); CC_SAFE_FREE(modeA.dirX); @@ -223,6 +236,15 @@ ParticleSystem::ParticleSystem() , _texture(nullptr) , _blendFunc(BlendFunc::ALPHA_PREMULTIPLIED) , _opacityModifyRGB(false) + , _isLifeAnimated(false) + , _isEmitterAnimated(false) + , _isLoopAnimated(false) + , _isAnimationAtlas(false) + , _animDir(TexAnimDir::VERTICAL) + , _animUnifiedSize(1) + , _animIndexCount(0) + , _isLifeAnimationReversed(false) + , _isAnimationMulti(false) , _yCoordFlipped(1) , _positionType(PositionType::FREE) , _paused(false) @@ -604,23 +626,31 @@ 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) +void ParticleSystem::addParticles(int count, int animationCellIndex, int animationIndex) { if (_paused) return; uint32_t RANDSEED = rand(); + if (_isAnimationAtlas) + { + animationCellIndex = MIN(animationCellIndex, getTotalAnimationCells() - 1); + animationIndex = MIN(animationIndex, _animIndexCount - 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 +664,63 @@ 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; + } + } + + 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) \ @@ -697,6 +784,12 @@ void ParticleSystem::addParticles(int count) _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) @@ -801,6 +894,84 @@ void ParticleSystem::addParticles(int count) } } +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(); @@ -880,6 +1051,37 @@ void ParticleSystem::update(float dt) for (int i = 0; i < _particleCount; ++i) { _particleData.timeToLive[i] -= 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 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 4cd110e10f..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,8 +87,14 @@ public: float* size; float* deltaSize; float* rotation; + float* staticRotation; float* deltaRotation; + float* totalTimeToLive; float* timeToLive; + float* animTimeDelta; + float* animTimeLength; + unsigned short* animIndex; + unsigned short* animCellIndex; unsigned int* atlasIndex; //! Mode A: gravity, direction, radial accel, tangential accel @@ -124,8 +144,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 +224,7 @@ public: }; /** PositionType - Possible types of particle positions. + Types of particle positioning. * @js cc.ParticleSystem.TYPE_FREE */ enum class PositionType @@ -216,6 +238,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 +285,7 @@ public: static Vector& getAllParticleSystems(); public: - void addParticles(int count); + void addParticles(int count, int animationCellIndex = -1, int animationIndex = -1); void stopSystem(); /** Kill all living particles. @@ -702,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. @@ -728,6 +772,141 @@ 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; + _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; + _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; } + + /** 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; } + + /** 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 * @@ -965,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 */ @@ -975,6 +1158,31 @@ 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; + /** 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 */ + 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 cab5a697f2..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,24 +403,75 @@ 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); } } + auto setTexCoords = [this](V3F_C4B_T2F_Quad* quad, unsigned short* cellIndex) { + + float left = 0.0F, bottom = 0.0F, top = 1.0F, right = 1.0F; + + if (_isAnimationAtlas) + { + 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; + 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 short* 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 +481,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 || _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; + 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) + 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 +505,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 || _isLoopAnimated) + setTexCoords(quad, cellIndex); } } } diff --git a/core/audio/AudioEngineImpl.cpp b/core/audio/AudioEngineImpl.cpp index 007afa5e60..5d0306d936 100644 --- a/core/audio/AudioEngineImpl.cpp +++ b/core/audio/AudioEngineImpl.cpp @@ -310,6 +310,7 @@ bool AudioEngineImpl::init() if (s_ALDevice) { + alGetError(); s_ALContext = alcCreateContext(s_ALDevice, nullptr); alcMakeContextCurrent(s_ALContext); diff --git a/core/network/Uri.cpp b/core/network/Uri.cpp index 695fe6e26f..835738740b 100644 --- a/core/network/Uri.cpp +++ b/core/network/Uri.cpp @@ -71,7 +71,7 @@ NS_CC_BEGIN namespace network { -Uri::Uri() : _isValid(false), _isSecure(false), _hasAuthority(false), _isCustomPort(false), _port(0) {} +Uri::Uri() : _isValid(false), _isSecure(false), _hasAuthority(false), _port(0) {} Uri::Uri(const Uri& o) { @@ -95,7 +95,6 @@ Uri& Uri::operator=(const Uri& o) _host = o._host; _hostName = o._hostName; _hasAuthority = o._hasAuthority; - _isCustomPort = o._isCustomPort; _port = o._port; _authority = o._authority; _pathEtc = o._pathEtc; @@ -122,9 +121,7 @@ Uri& Uri::operator=(Uri&& o) _host = std::move(o._host); _hostName = std::move(o._hostName); _hasAuthority = o._hasAuthority; - _isCustomPort = o._isCustomPort; o._hasAuthority = false; - o._isCustomPort = false; _port = o._port; o._port = 0; _authority = std::move(o._authority); @@ -293,26 +290,18 @@ bool Uri::doParse(std::string_view str) _isSecure = true; if (_port == 0) _port = 443; - - _isCustomPort = _port != 443; } else if (_scheme == "http" || _scheme == "ws") { if (_port == 0) _port = 80; - - _isCustomPort = _port != 80; } else if (_scheme == "ftp") { if (_port == 0) _port = 21; - - _isCustomPort = _port != 21; } } - else - _isCustomPort = _port != 0; if (_path.empty()) _path.push_back('/'); @@ -330,7 +319,6 @@ void Uri::clear() _host.clear(); _hostName.clear(); _hasAuthority = false; - _isCustomPort = false; _port = 0; _authority.clear(); _pathEtc.clear(); @@ -383,7 +371,7 @@ std::string Uri::toString() const ss << _username << "@"; } ss << _host; - if (_isCustomPort) + if (_port != 0) { ss << ":" << _port; } diff --git a/core/network/Uri.h b/core/network/Uri.h index 4fa604607f..de32b98921 100644 --- a/core/network/Uri.h +++ b/core/network/Uri.h @@ -130,8 +130,6 @@ public: */ std::string_view getAuthority() const { return _authority; } - bool isCustomPort() const { return _isCustomPort; } - /** Gets a string representation of the URI. */ std::string toString() const; @@ -165,14 +163,13 @@ private: bool _isValid; bool _isSecure; - bool _hasAuthority; - bool _isCustomPort; - uint16_t _port; std::string _scheme; std::string _username; std::string _password; std::string _host; std::string _hostName; + bool _hasAuthority; + uint16_t _port; std::string _authority; std::string _pathEtc; std::string _path; diff --git a/extensions/assets-manager/AssetsManagerEx.cpp b/extensions/assets-manager/AssetsManagerEx.cpp index 9802781ab4..16f42474c7 100644 --- a/extensions/assets-manager/AssetsManagerEx.cpp +++ b/extensions/assets-manager/AssetsManagerEx.cpp @@ -625,7 +625,7 @@ void AssetsManagerEx::downloadVersion() { _updateState = State::DOWNLOADING_VERSION; // Download version file asynchronously - _downloader->createDownloadFileTask(versionUrl, _tempVersionPath, VERSION_ID); + _downloader->createDownloadFileTask(versionUrl, _tempVersionPath, "", VERSION_ID); } // No version file found else @@ -696,7 +696,7 @@ void AssetsManagerEx::downloadManifest() { _updateState = State::DOWNLOADING_MANIFEST; // Download version file asynchronously - _downloader->createDownloadFileTask(manifestUrl, _tempManifestPath, MANIFEST_ID); + _downloader->createDownloadFileTask(manifestUrl, _tempManifestPath, "", MANIFEST_ID); } // No manifest file found else @@ -1258,7 +1258,7 @@ void AssetsManagerEx::queueDowload() _currConcurrentTask++; DownloadUnit& unit = _downloadUnits[key]; _fileUtils->createDirectory(basename(unit.storagePath)); - _downloader->createDownloadFileTask(unit.srcUrl, unit.storagePath, unit.customId); + _downloader->createDownloadFileTask(unit.srcUrl, unit.storagePath, "", unit.customId); _tempManifest->setAssetDownloadState(key, Manifest::DownloadState::DOWNLOADING); } diff --git a/tests/cpp-tests/Classes/UnitTest/UnitTest.cpp b/tests/cpp-tests/Classes/UnitTest/UnitTest.cpp index 3cd50aa857..aea3cc09da 100644 --- a/tests/cpp-tests/Classes/UnitTest/UnitTest.cpp +++ b/tests/cpp-tests/Classes/UnitTest/UnitTest.cpp @@ -1204,7 +1204,7 @@ void ParseUriTest::onEnter() EXPECT_EQ("ws", v.getScheme()); EXPECT_EQ("localhost", v.getHost()); EXPECT_EQ("localhost", v.getHostName()); - EXPECT_EQ("/", v.getPath()); + EXPECT_EQ("", v.getPath()); EXPECT_EQ(90, v.getPort()); EXPECT_EQ("", v.getFragment()); EXPECT_EQ("key1=foo=bar&key2=foobar&", v.getQuery()); @@ -1219,7 +1219,7 @@ void ParseUriTest::onEnter() EXPECT_EQ("ws", v.getScheme()); EXPECT_EQ("localhost", v.getHost()); EXPECT_EQ("localhost", v.getHostName()); - EXPECT_EQ("/", v.getPath()); + EXPECT_EQ("", v.getPath()); EXPECT_EQ(90, v.getPort()); EXPECT_EQ("", v.getFragment()); EXPECT_EQ("key1=foo=bar&key2=foobar&", v.getQuery()); @@ -1246,7 +1246,7 @@ void ParseUriTest::onEnter() EXPECT_EQ("ws", v.getScheme()); EXPECT_EQ("localhost", v.getHost()); EXPECT_EQ("localhost", v.getHostName()); - EXPECT_EQ("/", v.getPath()); + EXPECT_EQ("", v.getPath()); EXPECT_EQ(90, v.getPort()); EXPECT_EQ("", v.getFragment()); EXPECT_EQ("key1=foo=bar&key2=foobar&", v.getQuery()); @@ -1262,7 +1262,7 @@ void ParseUriTest::onEnter() EXPECT_EQ("ws", v.getScheme()); EXPECT_EQ("localhost", v.getHost()); EXPECT_EQ("localhost", v.getHostName()); - EXPECT_EQ("/", v.getPath()); + EXPECT_EQ("", v.getPath()); EXPECT_EQ(90, v.getPort()); EXPECT_EQ("", v.getFragment()); EXPECT_EQ("key1=foo=bar&key2=foobar&", v.getQuery()); @@ -1386,20 +1386,20 @@ void ParseUriTest::onEnter() EXPECT_EQ(u4.getScheme(), ""); EXPECT_EQ(u4.getHost(), "localhost"); EXPECT_EQ(u4.getPort(), 8080); - EXPECT_EQ(u4.getPath(), "/"); + EXPECT_EQ(u4.getPath(), ""); EXPECT_EQ(u4.getPathEtc(), ""); EXPECT_EQ(u5.getScheme(), "bb"); EXPECT_EQ(u5.getHost(), "localhost"); EXPECT_EQ(u5.getPort(), 0); - EXPECT_EQ(u5.getPath(), "/"); + EXPECT_EQ(u5.getPath(), ""); EXPECT_EQ(u5.getPathEtc(), "?&foo=12:4&ccc=13"); EXPECT_EQ(u5.getQuery(), "&foo=12:4&ccc=13"); EXPECT_EQ(u6.getScheme(), "cc"); EXPECT_EQ(u6.getHost(), "localhost"); EXPECT_EQ(u6.getPort(), 91); - EXPECT_EQ(u6.getPath(), "/"); + EXPECT_EQ(u6.getPath(), ""); EXPECT_EQ(u6.getPathEtc(), "?&foo=321&bbb=1"); EXPECT_EQ(u6.getQuery(), "&foo=321&bbb=1"); }