// // ZMLParticleSystem.cpp // Billiards // // Created by zhu on 2019/2/15. // #include "ZMLParticleSystem.h" #include "Curl.h" #include "vmath.hpp" //#include "" NS_RU_BEGIN class ZMLParticleSystemQuad; inline static float RANDOM_M11(unsigned int *seed) { *seed = *seed * 134775813 + 1; union { uint32_t d; float f; } u; u.d = (((uint32_t)(*seed) & 0x7fff) << 8) | 0x40000000; return u.f - 3.0f; } inline void normalize_point(float x, float y, particle_point* out) { float n = x * x + y * y; // Already normalized. if (n == 1.0f) return; n = sqrt(n); // Too close to zero. if (n < MATH_TOLERANCE) return; n = 1.0f / n; out->x = x * n; out->y = y * n; } ZMLParticleDataExpansion::ZMLParticleDataExpansion() { memset(this, 0, sizeof(ZMLParticleDataExpansion)); } bool ZMLParticleDataExpansion::init(int count) { maxCount = count; startColorR = (float*)malloc(count * sizeof(float)); startColorG = (float*)malloc(count * sizeof(float)); startColorB = (float*)malloc(count * sizeof(float)); startColorA = (float*)malloc(count * sizeof(float)); deltaColorRAllLife = (float*)malloc(count * sizeof(float)); deltaColorGAllLife = (float*)malloc(count * sizeof(float)); deltaColorBAllLife = (float*)malloc(count * sizeof(float)); deltaColorAAllLife = (float*)malloc(count * sizeof(float)); sizeWidth = (float*)malloc(count * sizeof(float)); startSizeWidth = (float*)malloc(count * sizeof(float)); deltaSizeWidthAllLife = (float*)malloc(count * sizeof(float)); sizeHeight = (float*)malloc(count * sizeof(float)); startSizeHeighth = (float*)malloc(count * sizeof(float)); deltaSizeHeightAllLife = (float*)malloc(count * sizeof(float)); startRotation = (float*)malloc(count * sizeof(float)); deltaRotationAllLife = (float*)malloc(count * sizeof(float)); deltaLifePercentage = (float*)malloc(count * sizeof(float)); lifePercentage = (float*)malloc(count * sizeof(float)); textureIndex = (int*)malloc(count * sizeof(int)); startTextureIndex = (int*)malloc(count * sizeof(int)); posz = (float*)malloc(count * sizeof(float)); maxSpeendZOffset = (float*)malloc(count * sizeof(float)); life = (float*)malloc(count * sizeof(float)); return startColorR && startColorG && startColorB && startColorA && deltaColorRAllLife && deltaColorGAllLife && deltaColorBAllLife && deltaColorAAllLife && sizeWidth && startSizeWidth && deltaSizeWidthAllLife && sizeHeight && startSizeHeighth && deltaSizeHeightAllLife && startRotation && deltaRotationAllLife && deltaLifePercentage && lifePercentage && textureIndex && startTextureIndex && posz && maxSpeendZOffset && life; } void ZMLParticleDataExpansion::release() { CC_SAFE_FREE(startColorR); CC_SAFE_FREE(startColorG); CC_SAFE_FREE(startColorB); CC_SAFE_FREE(startColorA); CC_SAFE_FREE(deltaColorRAllLife); CC_SAFE_FREE(deltaColorGAllLife); CC_SAFE_FREE(deltaColorBAllLife); CC_SAFE_FREE(deltaColorAAllLife); CC_SAFE_FREE(sizeWidth); CC_SAFE_FREE(startSizeWidth); CC_SAFE_FREE(deltaSizeWidthAllLife); CC_SAFE_FREE(sizeHeight); CC_SAFE_FREE(startSizeHeighth); CC_SAFE_FREE(deltaSizeHeightAllLife); CC_SAFE_FREE(startRotation); CC_SAFE_FREE(deltaRotationAllLife); CC_SAFE_FREE(deltaLifePercentage); CC_SAFE_FREE(lifePercentage); CC_SAFE_FREE(textureIndex); CC_SAFE_FREE(startTextureIndex); CC_SAFE_FREE(posz); CC_SAFE_FREE(maxSpeendZOffset); CC_SAFE_FREE(life); } ZMLParticleSystem::ZMLParticleSystem() : _oldPos(Vec2::ZERO) , _launcherangle(0) , _launcherV(0) , _texNumber(0) , _frameRate(-1) , _startSizeWidth(0) , _startSizeWidthVar(0) , _endSizeWidth(0) , _endSizeWidthVar(0) , _startSizeHeight(0) , _startSizeHeightVar(0) , _endSizeHeight(0) , _endSizeHeightVar(0) , _posZVar(0) , _maxWindX(0) , _maxWindY(0) , _windCycle(0) , _windTimelinePostion(0) , _winfTimelineDposPreS(0) , _lastAddParticleWordPostion(Vec2(-1,-1)) ,_mainTexName("") ,_maskName("") ,_colorPower(1.0f) , _lockAspectRatio(false) , _aspectRatio(0) , _sizeWidthChangeLaw(nullptr) , _sizeHeightChangeLaw(nullptr) , _colorAChangeLaw(nullptr) , _spinChangeLaw(nullptr) , _speedXOffset(nullptr) , _speedYOffset(nullptr) , _speedZOffset(nullptr) , _windOffset(nullptr) { // CC_SAFE_FREE(_sizeChangeLaw); // CC_SAFE_FREE(_spinChangeLaw); // CC_SAFE_FREE(_colorAChangeLaw); modules.clear(); } ZMLParticleSystem::~ZMLParticleSystem(){ CC_SAFE_FREE(_sizeWidthChangeLaw); CC_SAFE_FREE(_sizeHeightChangeLaw); CC_SAFE_FREE(_spinChangeLaw); CC_SAFE_FREE(_colorAChangeLaw); CC_SAFE_FREE(_speedXOffset); CC_SAFE_FREE(_speedYOffset); CC_SAFE_FREE(_speedZOffset); CC_SAFE_FREE(_windOffset); _particleDataExpansion.release(); auto iter = modules.begin(); while(iter != modules.end()) { delete iter->second; iter->second = nullptr; modules.erase(iter++); } } void ZMLParticleSystem::addModule(ParticleSystemModule* module) { ParticleSystemModuleFlag flag = module->getFlag(); if (modules.find(flag) != modules.end()) return; module->setParticleSystem(this); modules.insert(pair(module->getFlag(),module)); } ParticleSystemModule* ZMLParticleSystem::getModule(ParticleSystemModuleFlag flag) { auto iter = modules.find(flag); if (iter == modules.end() ) { return nullptr; } return iter->second; } void ZMLParticleSystem::SetColorPower(float colorPower) { _colorPower = colorPower; getGLProgramState()->setUniformFloat("color_power", colorPower); } ZMLParticleSystem* ZMLParticleSystem::createWithJsonString(string jsonStr){ ZMLParticleSystem *ret = new (std::nothrow) ZMLParticleSystem(); if (ret && ret->initWithJsonString(jsonStr)) { ret->autorelease(); return ret; } CC_SAFE_DELETE(ret); return ret; } ZMLParticleSystem* ZMLParticleSystem::createWithJsonFile(string path){ ZMLParticleSystem *ret = new (std::nothrow) ZMLParticleSystem(); if (ret && ret->initWithJsonFile(path)) { ret->autorelease(); return ret; } CC_SAFE_DELETE(ret); return ret; } //static std::map s_mapJson; bool ZMLParticleSystem::initWithJsonFile(string path){ string cfg; static std::map s_mapJson; if (s_mapJson.find(path) != s_mapJson.end()) { cfg = s_mapJson.find(path)->second; }else { Data datas = FileUtils::getInstance()->getDataFromFile(path); unsigned char* data = datas.getBytes(); ssize_t len = datas.getSize(); string err = ""; cfg = string(data,data+len); s_mapJson.insert(pair(path,cfg)); if(err != ""){ return false; } } return initWithJsonString(cfg); } bool ZMLParticleSystem::initWithJsonString(string jsonStr){ bool ret = false; string err = ""; Json rootjson = Json::parse(jsonStr, err); if (err != ""){ return false; } do { int maxParticles = rootjson["maxParticles"].int_value(); _mainTexName = rootjson["mainTexture"].string_value(); _maskName = rootjson["alphaTexture"].string_value(); _colorPower = rootjson["colorPower"].number_value(); _shaderType = rootjson["shaderType"].int_value(); _particleDataExpansion.release(); if( !_particleDataExpansion.init(maxParticles)) { CCLOG("Particle system: not enough memory"); this->release(); return false; } // _particleCount = 0; // self, not super if(this->initWithTotalParticles(maxParticles)) { // Emitter name in particle designer 2.0 // _configName = dictionary["configName"].asString(); // angle _angle = rootjson["angle"].number_value(); _angleVar = rootjson["angleVariance"].number_value(); _angleEven = rootjson["angleEvenDist"].bool_value(); // duration _duration = rootjson["duration"].number_value(); _explosiveness_ratio = rootjson["explosiveness_ratio"].number_value(); // blend function { //TODO: 叠加模式 _blendFunc.src = rootjson["blendFuncSource"].number_value(); _blendFunc.dst = rootjson["blendFuncDestination"].number_value(); } switch (rootjson["positionType"].int_value()) { case 0: _positionType = ParticleSystem::PositionType::FREE; break; case 1: _positionType = ParticleSystem::PositionType::RELATIVE; break; case 2: _positionType = ParticleSystem::PositionType::GROUPED; break; default: _positionType = ParticleSystem::PositionType::FREE; break; } // color _startColor.a = rootjson["startColorAlpha"].number_value(); _startColor.b = rootjson["startColorBlue"].number_value(); _startColor.g = rootjson["startColorGreen"].number_value(); _startColor.r = rootjson["startColorRed"].number_value(); _startColorVar.a = rootjson["startColorVarianceAlpha"].number_value(); _startColorVar.b = rootjson["startColorVarianceBlue"].number_value(); _startColorVar.g = rootjson["startColorVarianceGreen"].number_value(); _startColorVar.r = rootjson["startColorVarianceRed"].number_value(); _endColor.a = rootjson["finishColorAlpha"].number_value(); _endColor.b = rootjson["finishColorBlue"].number_value(); _endColor.g = rootjson["finishColorGreen"].number_value(); _endColor.r = rootjson["finishColorRed"].number_value(); _endColorVar.a = rootjson["finishColorVarianceAlpha"].number_value(); _endColorVar.b = rootjson["finishColorVarianceBlue"].number_value(); _endColorVar.g = rootjson["finishColorVarianceGreen"].number_value(); _endColorVar.r = rootjson["finishColorVarianceRed"].number_value(); // particle size _startSizeWidth = rootjson["startParticleSizeWidth"].number_value(); _startSizeWidthVar = rootjson["startParticleSizeWidthVariance"].number_value(); _endSizeWidth = rootjson["finishParticleSizeWidth"].number_value(); _endSizeWidthVar = rootjson["finishParticleSizeWidthVariance"].number_value(); _lockAspectRatio = rootjson["lockAspectRatio"].bool_value(); _startSizeHeight = rootjson["startParticleSizeHeight"].number_value(); if(_lockAspectRatio){ if(_startSizeWidth != 0){ _aspectRatio = _startSizeHeight / _startSizeWidth; } else { _aspectRatio = 1; } } _startSizeHeightVar = rootjson["startParticleSizeHeightVariance"].number_value(); _endSizeHeight = rootjson["finishParticleSizeHeight"].number_value(); _endSizeHeightVar = rootjson["finishParticleSizeHeightVariance"].number_value(); // position 不用在这搞 // float x = dictionary["sourcePositionx"].asFloat(); // float y = dictionary["sourcePositiony"].asFloat(); _posVar.x = rootjson["sourcePositionVariancex"].number_value(); _posVar.y = rootjson["sourcePositionVariancey"].number_value(); _posZVar = rootjson["sourcePositionVariancez"].number_value(); // Spinning _startSpin = rootjson["rotationStart"].number_value(); _startSpinVar = rootjson["rotationStartVariance"].number_value(); _endSpin = rootjson["rotationEnd"].number_value(); _endSpinVar = rootjson["rotationEndVariance"].number_value(); _frameRate = rootjson["frameRate"].int_value(); // life span _life = rootjson["particleLifespan"].number_value(); _lifeVar = rootjson["particleLifespanVariance"].number_value(); // emission Rate _emissionRate = _totalParticles / _life * 0.85; // _emitCounter = MAX(1, _emissionRate / 60); Json::array sizeWidthChangeLaw = rootjson["sizeWidthChangeLaw"].array_items(); _sizeWidthChangeLaw = (float*)malloc(sizeWidthChangeLaw.size() * sizeof(float)); for(int i = 0; i < sizeWidthChangeLaw.size(); i++){ _sizeWidthChangeLaw[i] = sizeWidthChangeLaw.at(i).number_value(); } Json::array sizeHeightChangeLa = rootjson["sizeHeightChangeLaw"].array_items(); _sizeHeightChangeLaw = (float*)malloc(sizeHeightChangeLa.size() * sizeof(float)); for(int i = 0; i < sizeHeightChangeLa.size(); i++){ _sizeHeightChangeLaw[i] = sizeHeightChangeLa.at(i).number_value(); } Json::array spinChangeLaw = rootjson["spinChangeLaw"].array_items(); _spinChangeLaw = (float*)malloc(spinChangeLaw.size() * sizeof(float)); for(int i = 0; i < spinChangeLaw.size(); i++){ _spinChangeLaw[i] = spinChangeLaw.at(i).number_value(); } Json::array colorAChangeLaw = rootjson["colorAChangeLaw"].array_items(); _colorAChangeLaw = (float*)malloc(colorAChangeLaw.size() * sizeof(float)); for(int i = 0; i < colorAChangeLaw.size(); i++){ _colorAChangeLaw[i] = colorAChangeLaw.at(i).number_value(); } _emitterMode = (Mode)rootjson["emitterMode"].int_value(); // Mode A: Gravity + tangential accel + radial accel if(_emitterMode == Mode::GRAVITY) { // gravity modeA.gravity.x = rootjson["gravityx"].number_value(); modeA.gravity.y = rootjson["gravityy"].number_value(); // speed modeA.speed = rootjson["speed"].number_value(); modeA.speedVar = rootjson["speedVariance"].number_value(); // radial acceleration modeA.radialAccel = rootjson["radialAcceleration"].number_value(); modeA.radialAccelVar = rootjson["radialAccelVariance"].number_value(); // tangential acceleration modeA.tangentialAccel = rootjson["tangentialAcceleration"].number_value(); modeA.tangentialAccelVar = rootjson["tangentialAccelVariance"].number_value(); // rotation is dir modeA.rotationIsDir = rootjson["rotationIsDir"].bool_value(); // 速度 Json::array speedXOffset = rootjson["speedXOffset"].array_items(); _speedXOffset = (float*)malloc(speedXOffset.size() * sizeof(float)); for(int i = 0; i < speedXOffset.size(); i++){ _speedXOffset[i] = speedXOffset.at(i).number_value(); } Json::array speedYOffset = rootjson["speedYOffset"].array_items(); _speedYOffset = (float*)malloc(speedYOffset.size() * sizeof(float)); for(int i = 0; i < speedYOffset.size(); i++){ _speedYOffset[i] = speedYOffset.at(i).number_value(); } _maxSpeendZOffset = rootjson["maxSpeendZOffset"].number_value(); Json::array speedZOffset = rootjson["speedZOffset"].array_items(); _speedZOffset = (float*)malloc(speedZOffset.size() * sizeof(float)); for(int i = 0; i < speedZOffset.size(); i++){ _speedZOffset[i] = speedZOffset.at(i).number_value(); } // 风速有关 _maxWindX = rootjson["maxWindX"].number_value(); _maxWindY = rootjson["maxWindY"].number_value(); _windCycle = rootjson["windCycle"].number_value(); Json::array windOffset = rootjson["windOffset"].array_items(); _windOffset = (float*)malloc(windOffset.size() * sizeof(float)); for(int i = 0; i < windOffset.size(); i++){ _windOffset[i] = windOffset.at(i).number_value(); } if(_windCycle == 0){ _windCycle = 1; } _winfTimelineDposPreS = 100 / _windCycle; _windTimelinePostion = 0; } else { modeA.rotationIsDir = rootjson["rotationIsDir"].bool_value(); modeB.startRadius = rootjson["startRadius"].number_value(); modeB.startRadiusVar = rootjson["startRadiusVar"].number_value(); modeB.endRadius = rootjson["endRadius"].number_value(); modeB.endRadiusVar = rootjson["endRadiusVar"].number_value(); modeB.rotatePerSecond = rootjson["rotatePerSecond"].number_value(); modeB.rotatePerSecondVar = rootjson["rotatePerSecondVar"].number_value(); } bool bBigPic = false;//判断粒子图片是否在plist大图中 在 则为true Texture2D* mainTex = nullptr; if (_mainTexName.length() > 0) { #if PARTICLE_EDITOR_MODE string path = rootjson["editorTexturePath"].string_value(); mainTex = Director::getInstance()->getTextureCache()->addImage(path); #else if (FileUtils::getInstance()->fullPathForFilename(_mainTexName).size() == 0){//在plist大图中查找图片 SpriteFrame* frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(_mainTexName); mainTex = frame->getTexture(); bBigPic = true; } else { mainTex = Director::getInstance()->getTextureCache()->addImage(_mainTexName); } #endif } moduleFlag = rootjson["moduleFlag"].int_value(); if ((moduleFlag & (int)ParticleSystemModuleFlag::TEXTURE_SHEET) != 0) { int tileX = rootjson["tileX"].int_value(); int tileY = rootjson["tileY"].int_value(); int startFrame = rootjson["startFrame"].int_value(); int frameCount = rootjson["frameCount"].int_value(); int sheetCycles = rootjson["sheetCycles"].int_value(); int flipTileY = rootjson["flipTileY"].int_value(); bool useRandomStartFrame = rootjson["useRandomStartFrame"].bool_value(); auto module = new TextureSheetAnimationModule(); module->setTiles(Vec2(tileX,tileY)); module->setStartFrame(startFrame); module->setFrameCount(frameCount); module->setFlipY(flipTileY); module->setCycles(sheetCycles); module->setUseRandomStartFrame(useRandomStartFrame); if (bBigPic) { module->setFileName(_mainTexName); } if (mainTex) { module->setTexture(mainTex); } addModule(module); } if ((moduleFlag & (int)ParticleSystemModuleFlag::NOISE) != 0) { auto module = new NoiseModule(); float _frequency = rootjson["noise_frequency"].number_value(); int _octaves = rootjson["noise_octaves"].int_value(); float _lacunarity = rootjson["noise_lacunarity"].number_value(); float _persistence = rootjson["noise_persistence"].number_value(); float _strength = rootjson["noise_strength"].number_value(); module->setFrequency(_frequency); module->setOctaves(_octaves); module->setLacunarity(_lacunarity); module->setPersistence(_persistence); module->setStrength(_strength); //TODO addModule(module); } auto iter = modules.begin(); while(iter != modules.end()) { ParticleSystemModule* module = iter->second; module->setEnable(true); iter++; } ret = true; } } while (0); // free(buffer); // free(deflated); return ret; } void ZMLParticleSystem::addParticles(int count){ if (_paused) return; uint32_t RANDSEED = rand(); 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); _particleDataExpansion.life[i] = _particleData.timeToLive[i]; _particleDataExpansion.lifePercentage[i] = 0; _particleDataExpansion.deltaLifePercentage[i] = 100 / _particleDataExpansion.life[i] ; } TextureSheetAnimationModule * module = dynamic_cast(getModule(ParticleSystemModuleFlag::TEXTURE_SHEET)); //(TextureSheetAnimationModule*)getModule(ParticleSystemModuleFlag::TEXTURE_SHEET); for(int i = start; i < _particleCount ; ++i){ int index = (module != nullptr && !module->isUseRandomStartFrame()) ? 0 : (int)_texNumber * abs(RANDOM_M11(&RANDSEED)); _particleDataExpansion.textureIndex[i] = index; _particleDataExpansion.startTextureIndex[i] = _particleDataExpansion.textureIndex[i] ; // CCLOG("_particleDataExpansion.texIndex[i] is %d",_particleDataExpansion.textureIndex[i]); } //position for (int i = start; i < _particleCount; ++i) { _particleData.posx[i] = _sourcePosition.x + _posVar.x * RANDOM_M11(&RANDSEED); } for (int i = start; i < _particleCount; ++i) { _particleData.posy[i] = _sourcePosition.y + _posVar.y * RANDOM_M11(&RANDSEED); } for (int i = start; i < _particleCount; ++i) { _particleDataExpansion.posz[i] = _posZVar * RANDOM_M11(&RANDSEED); _particleDataExpansion.maxSpeendZOffset[i] = _maxSpeendZOffset * RANDOM_M11(&RANDSEED); } //color #define SET_COLOR(c, b, v)\ for (int i = start; i < _particleCount; ++i)\ {\ c[i] = clampf( b + v * RANDOM_M11(&RANDSEED) , 0 , 1 );\ } SET_COLOR(_particleData.colorR, _startColor.r, _startColorVar.r); SET_COLOR(_particleData.colorG, _startColor.g, _startColorVar.g); SET_COLOR(_particleData.colorB, _startColor.b, _startColorVar.b); SET_COLOR(_particleData.colorA, _startColor.a, _startColorVar.a); SET_COLOR(_particleData.deltaColorR, _endColor.r, _endColorVar.r); SET_COLOR(_particleData.deltaColorG, _endColor.g, _endColorVar.g); SET_COLOR(_particleData.deltaColorB, _endColor.b, _endColorVar.b); SET_COLOR(_particleData.deltaColorA, _endColor.a, _endColorVar.a); #define SET_DELTA_COLOR_AllLife(sc, dca, c, dc)\ for (int i = start; i < _particleCount; ++i)\ {\ sc[i] = c[i];\ dca[i] = dc[i] - c[i];\ } SET_DELTA_COLOR_AllLife(_particleDataExpansion.startColorR, _particleDataExpansion.deltaColorRAllLife,_particleData.colorR, _particleData.deltaColorR); SET_DELTA_COLOR_AllLife(_particleDataExpansion.startColorG, _particleDataExpansion.deltaColorGAllLife,_particleData.colorG, _particleData.deltaColorG); SET_DELTA_COLOR_AllLife(_particleDataExpansion.startColorB, _particleDataExpansion.deltaColorBAllLife,_particleData.colorG, _particleData.deltaColorB); SET_DELTA_COLOR_AllLife(_particleDataExpansion.startColorA, _particleDataExpansion.deltaColorAAllLife,_particleData.colorA, _particleData.deltaColorA); #define SET_DELTA_COLOR(c, dc)\ for (int i = start; i < _particleCount; ++i)\ {\ dc[i] = (dc[i] - c[i]) / _particleData.timeToLive[i];\ } SET_DELTA_COLOR(_particleData.colorR, _particleData.deltaColorR); SET_DELTA_COLOR(_particleData.colorG, _particleData.deltaColorG); SET_DELTA_COLOR(_particleData.colorB, _particleData.deltaColorB); SET_DELTA_COLOR(_particleData.colorA, _particleData.deltaColorA); //size for (int i = start; i < _particleCount; ++i) { _particleDataExpansion.sizeWidth[i] = _startSizeWidth + _startSizeWidthVar * RANDOM_M11(&RANDSEED); _particleDataExpansion.sizeWidth[i] = MAX(0, _particleDataExpansion.sizeWidth[i]); _particleDataExpansion.startSizeWidth[i] = _particleDataExpansion.sizeWidth[i]; _particleDataExpansion.sizeHeight[i] = _startSizeHeight + _startSizeHeightVar * RANDOM_M11(&RANDSEED); _particleDataExpansion.sizeHeight[i] = MAX(0, _particleDataExpansion.sizeHeight[i]); _particleDataExpansion.startSizeHeighth[i] = _particleDataExpansion.sizeHeight[i]; } if (_endSizeWidth != START_SIZE_EQUAL_TO_END_SIZE) { for (int i = start; i < _particleCount; ++i) { float endSizeWidth = _endSizeWidth + _endSizeWidthVar * RANDOM_M11(&RANDSEED); endSizeWidth = MAX(0, endSizeWidth); _particleDataExpansion.deltaSizeWidthAllLife[i] = endSizeWidth - _particleDataExpansion.startSizeWidth[i]; } } else { for (int i = start; i < _particleCount; ++i) { float deltaSizeWidth = _endSizeWidthVar * RANDOM_M11(&RANDSEED); (_particleDataExpansion.startSizeWidth[i] + deltaSizeWidth) < 0 ? _particleDataExpansion.deltaSizeWidthAllLife[i] = -_particleDataExpansion.startSizeWidth[i] : _particleDataExpansion.deltaSizeWidthAllLife[i] = deltaSizeWidth; } } if (_endSizeHeight != START_SIZE_EQUAL_TO_END_SIZE) { for (int i = start; i < _particleCount; ++i) { float endSizeHeigh = _endSizeHeight + _endSizeHeightVar * RANDOM_M11(&RANDSEED); endSizeHeigh = MAX(0, endSizeHeigh); _particleDataExpansion.deltaSizeHeightAllLife[i] = endSizeHeigh - _particleDataExpansion.startSizeHeighth[i]; } }else { for (int i = start; i < _particleCount; ++i) { float deltaSizeHeigh = _endSizeHeightVar * RANDOM_M11(&RANDSEED); (_particleDataExpansion.startSizeHeighth[i] + deltaSizeHeigh) < 0 ? _particleDataExpansion.deltaSizeHeightAllLife[i] = -_particleDataExpansion.startSizeHeighth[i] : _particleDataExpansion.deltaSizeHeightAllLife[i] = deltaSizeHeigh; } } // rotation for (int i = start; i < _particleCount; ++i) { _particleData.rotation[i] = _startSpin + _startSpinVar * RANDOM_M11(&RANDSEED); _particleDataExpansion.startRotation[i] = _particleData.rotation[i]; } if(_endSpin != START_ROTATION_EQUAL_TO_END_ROTATION){ for (int i = start; i < _particleCount; ++i) { float endA = _endSpin + _endSpinVar * RANDOM_M11(&RANDSEED); _particleDataExpansion.deltaRotationAllLife[i] = endA - _particleData.rotation[i]; } } else { // 结束旋转在初始角度基础上 for (int i = start; i < _particleCount; ++i) { _particleDataExpansion.deltaRotationAllLife[i] = _endSpinVar * RANDOM_M11(&RANDSEED); } } // position Vec2 pos; Vec2 changePos; if (_positionType == PositionType::FREE) { if(_lastAddParticleWordPostion != Vec2(-1,-1)){ pos = this->convertToWorldSpace(Vec2::ZERO); changePos = pos - _lastAddParticleWordPostion; _lastAddParticleWordPostion = pos; } else { pos = this->convertToWorldSpace(Vec2::ZERO); _lastAddParticleWordPostion = pos; } } else if (_positionType == PositionType::RELATIVE) { pos = _position; } float v = 1.0 / count; for (int i = start; i < _particleCount; ++i) { // count;//新加的粒子数 _particleData.startPosX[i] = pos.x - changePos.x * v * (i - start); // _particleData.startPosX[i] = pos.x; } for (int i = start; i < _particleCount; ++i) { _particleData.startPosY[i] = pos.y - changePos.y * v * (i - start); // _particleData.startPosY[i] = pos.y; } // Mode Gravity: A if (_emitterMode == Mode::GRAVITY) { // radial accel for (int i = start; i < _particleCount; ++i) { _particleData.modeA.radialAccel[i] = modeA.radialAccel + modeA.radialAccelVar * RANDOM_M11(&RANDSEED); } // tangential accel for (int i = start; i < _particleCount; ++i) { _particleData.modeA.tangentialAccel[i] = modeA.tangentialAccel + modeA.tangentialAccelVar * RANDOM_M11(&RANDSEED); } // rotation is dir if( modeA.rotationIsDir ) { for (int i = start; i < _particleCount; ++i) { float a = CC_DEGREES_TO_RADIANS( _angle + _angleVar * RANDOM_M11(&RANDSEED) ); Vec2 v(cosf( a ), sinf( a )); float s = modeA.speed + modeA.speedVar * RANDOM_M11(&RANDSEED); Vec2 dir = v * s; _particleData.modeA.dirX[i] = dir.x;//v * s ; _particleData.modeA.dirY[i] = dir.y; _particleData.rotation[i] = -CC_RADIANS_TO_DEGREES(dir.getAngle()) + _startSpin + _startSpinVar * RANDOM_M11(&RANDSEED);; _particleDataExpansion.startRotation[i] = _particleData.rotation[i]; } } else{ for (int i = start; i < _particleCount; ++i) { float a = 0; if (_angleEven) { float angleStep = _angleVar / _particleCount; a = CC_DEGREES_TO_RADIANS( _angle + angleStep * i + 10*RANDOM_M11(&RANDSEED)); } else { a = CC_DEGREES_TO_RADIANS( _angle + _angleVar * RANDOM_M11(&RANDSEED)); } Vec2 v(cosf( a ), sinf( a )); float s = modeA.speed + modeA.speedVar * RANDOM_M11(&RANDSEED); Vec2 dir = v * s; _particleData.modeA.dirX[i] = dir.x;//v * s ; _particleData.modeA.dirY[i] = dir.y; } } } // Mode Radius: B else { //Need to check by Jacky // 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); } for (int i = start; i < _particleCount; ++i) { float a = 0; if (_angleEven) { float angleStep = _angleVar / _particleCount; a = CC_DEGREES_TO_RADIANS( _angle + angleStep * i + 10*RANDOM_M11(&RANDSEED)); } else { a = CC_DEGREES_TO_RADIANS( _angle + _angleVar * RANDOM_M11(&RANDSEED)); } _particleData.modeB.angle[i] = a; } for (int i = start; i < _particleCount; ++i) { _particleData.modeB.degreesPerSecond[i] = CC_DEGREES_TO_RADIANS(modeB.rotatePerSecond + modeB.rotatePerSecondVar * RANDOM_M11(&RANDSEED)); } if(modeB.endRadius == START_RADIUS_EQUAL_TO_END_RADIUS) { for (int i = start; i < _particleCount; ++i) { _particleData.modeB.deltaRadius[i] = 0.0f; } } else { for (int i = start; i < _particleCount; ++i) { float endRadius = modeB.endRadius + modeB.endRadiusVar * RANDOM_M11(&RANDSEED); _particleData.modeB.deltaRadius[i] = (endRadius - _particleData.modeB.radius[i]) / _particleData.timeToLive[i]; } } } } void ZMLParticleSystem::update(float dt){ CC_PROFILER_START_CATEGORY(kProfilerCategoryParticles , "ZMLParticleSystem - update"); if (_isActive && _emissionRate) { if (_oldPos != getPosition()){ // _launcherangle = (getPosition() - _oldPos).getAngle(); _launcherV = (getPosition() - _oldPos).getLength() / dt * 0.5; _launcherV = 5; } else { _launcherangle = 0; _launcherV = 0; } _oldPos = getPosition(); _windTimelinePostion += _winfTimelineDposPreS * dt; float rate = 1.0f / _emissionRate; int totalParticles = static_cast(_totalParticles * __totalParticleCountFactor); // { _emitCounter += dt; if (_emitCounter < 0.f) _emitCounter = 0.f; // } int emitCount = MIN(totalParticles - _particleCount, _emitCounter * _emissionRate); if( _explosiveness_ratio > (0.0 + FLT_EPSILON)) { int explosiveCount = (int)(_explosiveness_ratio * totalParticles); //目前只在开始时爆发一次 if (_elapsed < 0.0f + FLT_EPSILON) { addParticles(explosiveCount); CCLOG("emitCount :%d %d %f,%f",_particleCount,emitCount ,_emitCounter,_emissionRate); } }else { addParticles(emitCount); } _emitCounter -= rate *emitCount; _elapsed += dt; if (_elapsed < 0.f) _elapsed = 0.f; if (_duration != DURATION_INFINITY && _duration < _elapsed) { CCLOG("stop particle : %f %f",_duration,_elapsed); this->stopSystem(); } } for (int i = 0; i < _particleCount; ++i) { _particleData.timeToLive[i] -= dt; _particleDataExpansion.lifePercentage[i] += _particleDataExpansion.deltaLifePercentage[i] * dt; _particleDataExpansion.lifePercentage[i] = MAX(0, MIN(_particleDataExpansion.lifePercentage[i], 100)); } for (int i = 0; i < _particleCount; ++i) { if (_particleData.timeToLive[i] <= 0.0f) { int j = _particleCount - 1; while (j > 0 && _particleData.timeToLive[j] <= 0) { _particleCount--; j--; } _particleData.copyParticle(i, _particleCount - 1); _particleDataExpansion.copyParticle(i, _particleCount - 1); if (_batchNode) { //disable the switched particle int currentIndex = _particleData.atlasIndex[i]; _batchNode->disableParticle(_atlasIndex + currentIndex); //switch indexes _particleData.atlasIndex[_particleCount - 1] = currentIndex; } --_particleCount; if( _particleCount == 0 && _isAutoRemoveOnFinish ) { this->unscheduleUpdate(); _parent->removeChild(this, true); return; } } } if (_emitterMode == Mode::GRAVITY) { int windIndex = ((int)_windTimelinePostion) % 101; float windPercentage = _windOffset[windIndex]; float windX = _maxWindX * windPercentage; float windY = _maxWindY * windPercentage; for (int i = 0 ; i < _particleCount; ++i) { particle_point tmp, radial = {0.0f, 0.0f}, tangential; // radial acceleration if (_particleData.posx[i] || _particleData.posy[i]) { normalize_point(_particleData.posx[i], _particleData.posy[i], &radial); } tangential = radial; radial.x *= _particleData.modeA.radialAccel[i]; radial.y *= _particleData.modeA.radialAccel[i]; // tangential acceleration std::swap(tangential.x, tangential.y); tangential.x *= - _particleData.modeA.tangentialAccel[i]; tangential.y *= _particleData.modeA.tangentialAccel[i]; // (gravity + radial + tangential + wind) * dt tmp.x = radial.x + tangential.x + modeA.gravity.x + windX; tmp.y = radial.y + tangential.y + modeA.gravity.y + windY; tmp.x *= dt; tmp.y *= dt; _particleData.modeA.dirX[i] += tmp.x; _particleData.modeA.dirY[i] += tmp.y; // this is cocos2d-x v3.0 // if (_configName.length()>0 && _yCoordFlipped != -1) // this is cocos2d-x v3.0 tmp.x = _particleData.modeA.dirX[i] * dt * _yCoordFlipped; tmp.y = _particleData.modeA.dirY[i] * dt * _yCoordFlipped; _particleData.posx[i] += tmp.x; _particleData.posy[i] += tmp.y; int index = _particleDataExpansion.lifePercentage[i]; float speendXOffset = _speedXOffset[index]; float speendYOffset = _speedYOffset[index]; float speendZOffset = _speedZOffset[index]; CurlNoise::Vector3 noise = CurlNoise::Vector3(0,0,0); NoiseModule* module = dynamic_cast(getModule(ParticleSystemModuleFlag::NOISE)); if (module != nullptr) { CurlNoise::CurlSettings setting; setting.m_bCheapGradient = true; setting.m_Frequency = module->getFrequency(); setting.m_NumOctaves = module->getOctaves(); setting.m_Lacunarity = module->getLacunarity(); setting.m_Persistence = module->getPersistence(); CurlNoise::SetCurlSettings(setting); noise = CurlNoise::ComputeCurlWithoutObstacles(CurlNoise::Vector3(_particleData.posx[i],_particleData.posy[i],0)); noise *= module->getStrength(); } _particleData.posx[i] += (speendXOffset * dt) + noise.getX() ; _particleData.posy[i] += (speendYOffset * dt) + noise.getY() ; _particleDataExpansion.posz[i] += (speendZOffset*_particleDataExpansion.maxSpeendZOffset[i] * 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; } for (int i = 0; i < _particleCount; ++i) { _particleData.modeB.radius[i] += _particleData.modeB.deltaRadius[i] * dt; } for (int i = 0; i < _particleCount; ++i) { _particleData.posx[i] = - cosf(_particleData.modeB.angle[i]) * _particleData.modeB.radius[i]; } for (int i = 0; i < _particleCount; ++i) { _particleData.posy[i] = - sinf(_particleData.modeB.angle[i]) * _particleData.modeB.radius[i] * _yCoordFlipped; } if (modeA.rotationIsDir) { for (int i = 0; i < _particleCount; ++i) { Vec2 dir = Vec2(_particleData.posx[i] - _particleData.startPosX[i],_particleData.posy[i] - _particleData.startPosY[i]); _particleData.rotation[i] = -CC_RADIANS_TO_DEGREES(dir.getAngle()) + _startSpin; // CCLOG("angle : %f %f %f %f",dir.x,dir.y,dir.getAngle(),_particleData.rotation[i]); _particleData.startPosX[i] = _particleData.posx[i]; _particleData.startPosY[i] = _particleData.posy[i]; } } } //color r,g,b,a for (int i = 0 ; i < _particleCount; ++i) { _particleData.colorR[i] += _particleData.deltaColorR[i] * dt; } for (int i = 0 ; i < _particleCount; ++i) { _particleData.colorG[i] += _particleData.deltaColorG[i] * dt; } for (int i = 0 ; i < _particleCount; ++i) { _particleData.colorB[i] += _particleData.deltaColorB[i] * dt; } for (int i = 0 ; i < _particleCount; ++i) { // _particleData.colorA[i] += _particleData.deltaColorA[i] * dt; int index = _particleDataExpansion.lifePercentage[i]; float deltaSizePercentage = _colorAChangeLaw[index] * 0.01; _particleData.colorA[i] = _particleDataExpansion.startColorA[i] + _particleDataExpansion.deltaColorAAllLife[i] * deltaSizePercentage; } //size for (int i = 0 ; i < _particleCount; ++i) { int index = _particleDataExpansion.lifePercentage[i]; float deltaSizeWidthPercentage = _sizeWidthChangeLaw[index] * 0.01; _particleDataExpansion.sizeWidth[i] = (_particleDataExpansion.startSizeWidth[i] + _particleDataExpansion.deltaSizeWidthAllLife[i] * deltaSizeWidthPercentage); _particleDataExpansion.sizeWidth[i] = MAX(0, _particleDataExpansion.sizeWidth[i]); } if(_lockAspectRatio){ for (int i = 0 ; i < _particleCount; ++i) { _particleDataExpansion.sizeHeight[i] = MAX(0, _particleDataExpansion.sizeWidth[i] * _aspectRatio); } } else { for (int i = 0 ; i < _particleCount; ++i) { int index = _particleDataExpansion.lifePercentage[i]; float deltaSizeHeightPercentage = _sizeHeightChangeLaw[index] * 0.01; _particleDataExpansion.sizeHeight[i] = (_particleDataExpansion.startSizeHeighth[i] + _particleDataExpansion.deltaSizeHeightAllLife[i] * deltaSizeHeightPercentage); _particleDataExpansion.sizeHeight[i] = MAX(0, _particleDataExpansion.sizeHeight[i]); } } if (true){ //index 序列帧 if (_frameRate != FRAM_RATE_PLAY_ONCE_IN_ALL_LIFE){ for (int i = 0 ; i < _particleCount; ++i) { float liveTime = _particleDataExpansion.life[i] - _particleData.timeToLive[i]; int totalIndex = liveTime * _frameRate; totalIndex+= _particleDataExpansion.startTextureIndex[i]; int index = totalIndex % _texNumber; //BulldogTool::Log("[update] Tex Index : %d %d %d %d",index,totalIndex,_texNumber,_particleCount); _particleDataExpansion.textureIndex[i] = index; } } else { if (_texNumber > 0) { for (int i = 0 ; i < _particleCount; ++i) { int index = _particleDataExpansion.lifePercentage[i]; float deltaSizePercentage = index * 0.01; int totalIndex = (_texNumber - 1) * deltaSizePercentage; totalIndex+= _particleDataExpansion.startTextureIndex[i]; int texindex = totalIndex % _texNumber; if (_particleDataExpansion.textureIndex[i] != texindex) { _particleDataExpansion.textureIndex[i] += 1; _particleDataExpansion.textureIndex[i] %= _texNumber; } } } } } //angle if (!modeA.rotationIsDir){ //角度和速度相同不计算 for (int i = 0 ; i < _particleCount; ++i) { // _particleData.rotation[i] += _particleData.deltaRotation[i] * dt; int index = _particleDataExpansion.lifePercentage[i]; float deltaSizePercentage = _spinChangeLaw[index] * 0.01; _particleData.rotation[i] = (_particleDataExpansion.startRotation [i] + _particleDataExpansion.deltaRotationAllLife[i] * deltaSizePercentage); } } updateParticleQuads(); _transformSystemDirty = false; // only update gl buffer when visible if (_visible && ! _batchNode) { postStep(); } CC_PROFILER_STOP_CATEGORY(kProfilerCategoryParticles , "ZMLParticleSystem - update"); } NS_RU_END