diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 0ae8dc88..ed020173 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -9338,7 +9338,7 @@ uint16_t mode_particlefire(void) { uint32_t i; // index variable uint32_t numFlames; // number of flames: depends on fire width. for a fire width of 16 pixels, about 25-30 flames give good results - if (SEGMENT.call == 0) { // initialization TODO: make this a PSinit function, this is needed in every particle FX but first, get this working. + if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, SEGMENT.virtualWidth(), 4)) //maximum number of source (PS may limit based on segment size); need 4 additional bytes for time keeping (uint32_t lastcall) return mode_static(); // allocation failed or not 2D SEGENV.aux0 = hw_random16(); // aux0 is wind position (index) in the perlin noise @@ -9430,7 +9430,7 @@ uint16_t mode_particlepit(void) { ParticleSystem2D *PartSys = nullptr; if (SEGMENT.call == 0) { // initialization - if (!initParticleSystem2D(PartSys, 1, 0, true, false)) // init, request one source (actually dont really need one TODO: test if using zero sources also works) + if (!initParticleSystem2D(PartSys, 0, 0, true, false)) // init return mode_static(); // allocation failed or not 2D PartSys->setKillOutOfBounds(true); PartSys->setGravity(); // enable with default gravity @@ -9501,7 +9501,7 @@ uint16_t mode_particlewaterfall(void) { uint8_t numSprays; uint32_t i = 0; - if (SEGMENT.call == 0) { // initialization TODO: make this a PSinit function, this is needed in every particle FX but first, get this working. + if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, 12)) // init, request 12 sources, no additional data needed return mode_static(); // allocation failed or not 2D @@ -9524,7 +9524,7 @@ uint16_t mode_particlewaterfall(void) { else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! (TODO: ask how to handle this so it always works) + return mode_static(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -9653,7 +9653,7 @@ uint16_t mode_particleperlin(void) { ParticleSystem2D *PartSys = nullptr; uint32_t i; - if (SEGMENT.call == 0) { // initialization TODO: make this a PSinit function, this is needed in every particle FX but first, get this working. + if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, 1, 0, true)) // init with 1 source and advanced properties return mode_static(); // allocation failed or not 2D @@ -9714,20 +9714,19 @@ static const char _data_FX_MODE_PARTICLEPERLIN[] PROGMEM = "PS Fuzzy Noise@Speed uint16_t mode_particleimpact(void) { ParticleSystem2D *PartSys = nullptr; uint32_t i = 0; - uint8_t MaxNumMeteors; + uint32_t numMeteors; PSsettings2D meteorsettings; meteorsettings.asByte = 0b00101000; // PS settings for meteors: bounceY and gravity enabled - if (SEGMENT.call == 0) { // initialization TODO: make this a PSinit function, this is needed in every particle FX but first, get this working. + if (SEGMENT.call == 0) { // initialization if (!initParticleSystem2D(PartSys, NUMBEROFSOURCES)) // init, no additional data needed return mode_static(); // allocation failed or not 2D PartSys->setKillOutOfBounds(true); PartSys->setGravity(); // enable default gravity PartSys->setBounceY(true); // always use ground bounce PartSys->setWallRoughness(220); // high roughness - MaxNumMeteors = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); - for (i = 0; i < MaxNumMeteors; i++) { - // PartSys->sources[i].source.y = 500; + numMeteors = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); + for (i = 0; i < numMeteors; i++) { PartSys->sources[i].source.ttl = hw_random16(10 * i); // set initial delay for meteors PartSys->sources[i].source.vy = 10; // at positive speeds, no particles are emitted and if particle dies, it will be relaunched } @@ -9736,7 +9735,7 @@ uint16_t mode_particleimpact(void) { PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS if (PartSys == nullptr) - return mode_static(); // something went wrong, no data! (TODO: ask how to handle this so it always works) + return mode_static(); // something went wrong, no data! // Particle System settings PartSys->updateSystem(); // update system properties (dimensions and data pointers) @@ -9746,29 +9745,18 @@ uint16_t mode_particleimpact(void) { uint8_t hardness = map(SEGMENT.custom2, 0, 255, PS_P_MINSURFACEHARDNESS - 2, 255); PartSys->setWallHardness(hardness); PartSys->enableParticleCollisions(SEGMENT.check3, hardness); // enable collisions and set particle collision hardness - MaxNumMeteors = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); - uint8_t numMeteors = MaxNumMeteors; // TODO: clean this up map(SEGMENT.custom3, 0, 31, 1, MaxNumMeteors); // number of meteors to use for animation - + numMeteors = min(PartSys->numSources, (uint32_t)NUMBEROFSOURCES); uint32_t emitparticles; // number of particles to emit for each rocket's state for (i = 0; i < numMeteors; i++) { // determine meteor state by its speed: - if ( PartSys->sources[i].source.vy < 0) { // moving down, emit sparks - #ifdef ESP8266 + if ( PartSys->sources[i].source.vy < 0) // moving down, emit sparks emitparticles = 1; - #else - emitparticles = 2; - #endif - } else if ( PartSys->sources[i].source.vy > 0) // moving up means meteor is on 'standby' emitparticles = 0; else { // speed is zero, explode! PartSys->sources[i].source.vy = 10; // set source speed positive so it goes into timeout and launches again - #ifdef ESP8266 - emitparticles = hw_random16(SEGMENT.intensity >> 3) + 5; // defines the size of the explosion - #else - emitparticles = map(SEGMENT.intensity, 0, 255, 10, hw_random16(PartSys->usedParticles>>2)); // defines the size of the explosion !!!TODO: check if this works on ESP8266, drop esp8266 def if it does - #endif + emitparticles = map(SEGMENT.intensity, 0, 255, 10, hw_random16(PartSys->usedParticles>>2)); // defines the size of the explosion } for (int e = emitparticles; e > 0; e--) { PartSys->sprayEmit(PartSys->sources[i]); @@ -9789,13 +9777,13 @@ uint16_t mode_particleimpact(void) { PartSys->sources[i].source.vx = 0; PartSys->sources[i].sourceFlags.collide = true; #ifdef ESP8266 - PartSys->sources[i].maxLife = 180; - PartSys->sources[i].minLife = 20; + PartSys->sources[i].maxLife = 900; + PartSys->sources[i].minLife = 100; #else - PartSys->sources[i].maxLife = 250; - PartSys->sources[i].minLife = 50; + PartSys->sources[i].maxLife = 1250; + PartSys->sources[i].minLife = 250; #endif - PartSys->sources[i].source.ttl = hw_random16((512 - (SEGMENT.speed << 1))) + 40; // standby time til next launch (in frames) + PartSys->sources[i].source.ttl = hw_random16((768 - (SEGMENT.speed << 1))) + 40; // standby time til next launch (in frames) PartSys->sources[i].vy = (SEGMENT.custom1 >> 2); // emitting speed y PartSys->sources[i].var = (SEGMENT.custom1 >> 2); // speed variation around vx,vy (+/- var) } @@ -9810,13 +9798,17 @@ uint16_t mode_particleimpact(void) { PartSys->sources[i].source.hue = hw_random16(); // random color PartSys->sources[i].source.ttl = 500; // long life, will explode at bottom PartSys->sources[i].sourceFlags.collide = false; // trail particles will not collide - PartSys->sources[i].maxLife = 60; // spark particle life - PartSys->sources[i].minLife = 20; + PartSys->sources[i].maxLife = 300; // spark particle life + PartSys->sources[i].minLife = 100; PartSys->sources[i].vy = -9; // emitting speed (down) PartSys->sources[i].var = 3; // speed variation around vx,vy (+/- var) } } + for (uint32_t i = 0; i < PartSys->usedParticles; i++) { + if (PartSys->particles[i].ttl > 5) PartSys->particles[i].ttl -= 5; //ttl is linked to brightness, this allows to use higher brightness but still a short spark lifespan + } + PartSys->update(); // update and render return FRAMETIME; } @@ -10220,7 +10212,7 @@ uint16_t mode_particleghostrider(void) { // emit two particles PartSys->angleEmit(PartSys->sources[0], emitangle, speed); PartSys->angleEmit(PartSys->sources[0], emitangle, speed); - if (SEGMENT.call % (11 - (SEGMENT.custom2 / 25)) == 0) { // every nth frame, cycle color and emit particles //TODO: make this a segment call % SEGMENT.custom2 for better control + if (SEGMENT.call % (11 - (SEGMENT.custom2 / 25)) == 0) { // every nth frame, cycle color and emit particles PartSys->sources[0].source.hue++; } if (SEGMENT.custom2 > 190) //fast color change @@ -10240,7 +10232,7 @@ uint16_t mode_particleblobs(void) { ParticleSystem2D *PartSys = nullptr; if (SEGMENT.call == 0) { - if (!initParticleSystem2D(PartSys, 1, 0, true, true)) //init, request one source, no additional bytes, advanced size & size control (actually dont really need one TODO: test if using zero sources also works) + if (!initParticleSystem2D(PartSys, 0, 0, true, true)) //init, no additional bytes, advanced size & size control return mode_static(); // allocation failed or not 2D PartSys->setBounceX(true); PartSys->setBounceY(true); @@ -10909,7 +10901,7 @@ uint16_t mode_particleHourglass(void) { } // re-order particles in case collisions flipped particles (highest number index particle is on the "bottom") - for (int i = 0; i < PartSys->usedParticles - 1; i++) { + for (uint32_t i = 0; i < PartSys->usedParticles - 1; i++) { if (PartSys->particles[i].x < PartSys->particles[i+1].x && PartSys->particleFlags[i].fixed == false && PartSys->particleFlags[i+1].fixed == false) { std::swap(PartSys->particles[i].x, PartSys->particles[i+1].x); } @@ -11020,10 +11012,7 @@ uint16_t mode_particleBalance(void) { if (SEGMENT.call == 0) { // initialization if (!initParticleSystem1D(PartSys, 1, 128)) // init, no additional data needed, use half of max particles return mode_static(); // allocation failed or is single pixel - //PartSys->setKillOutOfBounds(true); PartSys->setParticleSize(1); - SEGENV.aux0 = 0; - SEGENV.aux1 = 0; //TODO: really need to set to zero or is it calloc'd? } else PartSys = reinterpret_cast(SEGENV.data); // if not first call, just set the pointer to the PS @@ -11175,7 +11164,7 @@ uint16_t mode_particleChase(void) { globalhuestep = 2; // global hue change to add some color variation if ((SEGMENT.call & 0x1F) == 0) SEGENV.step += *stepdir; // change density - for(int32_t i = 0; i < PartSys->usedParticles; i++) { + for(uint32_t i = 0; i < PartSys->usedParticles; i++) { PartSys->particles[i].hue -= globalhuestep; // shift global hue (both directions) PartSys->particles[i].vx = 1 + (SEGMENT.speed >> 2) + ((int32_t(sin16_t(strip.now >> 1) + 32767) * (SEGMENT.speed >> 2)) >> 16); } @@ -11347,7 +11336,7 @@ uint16_t mode_particleFire1D(void) { PartSys->setColorByAge(true); uint32_t emitparticles = 1; uint32_t j = hw_random16(); - for (uint i = 0; i < 3; i++) { // 3 base flames TODO: check if this is ok or needs adjustments + for (uint i = 0; i < 3; i++) { // 3 base flames if (PartSys->sources[i].source.ttl > 50) PartSys->sources[i].source.ttl -= 10; // TODO: in 2D making the source fade out slow results in much smoother flames, need to check if it can be done the same else @@ -11368,7 +11357,7 @@ uint16_t mode_particleFire1D(void) { } } else { - PartSys->sources[j].minLife = PartSys->sources[j].source.ttl + SEGMENT.intensity; // TODO: in 2D, emitted particle ttl depends on source TTL, mimic here the same way? OR: change 2D to the same way it is done here and ditch special fire treatment in emit? + PartSys->sources[j].minLife = PartSys->sources[j].source.ttl + SEGMENT.intensity; PartSys->sources[j].maxLife = PartSys->sources[j].minLife + 50; PartSys->sources[j].v = SEGMENT.speed >> 2; if (SEGENV.call & 0x01) // every second frame @@ -11629,7 +11618,7 @@ uint16_t mode_particleSpringy(void) { PartSys->particles[0].x = dxlimit; // limit the spring length springforce[0] += ((springlength >> 1) - (PartSys->particles[0].x)) * springK; // first particle anchors to x=0 - for (int32_t i = 1; i < PartSys->usedParticles; i++) { + for (uint32_t i = 1; i < PartSys->usedParticles; i++) { // reorder particles if they are out of order to prevent chaos if (PartSys->particles[i].x < PartSys->particles[i-1].x) std::swap(PartSys->particles[i].x, PartSys->particles[i-1].x); // swap particle positions to maintain order @@ -11650,7 +11639,7 @@ uint16_t mode_particleSpringy(void) { } // apply spring forces to particles bool dampenoscillations = (SEGMENT.call % (9 - (SEGMENT.speed >> 5))) == 0; // dampen oscillation if particles are slow, more damping on stiffer springs - for (int32_t i = 0; i < PartSys->usedParticles; i++) { + for (uint32_t i = 0; i < PartSys->usedParticles; i++) { springforce[i] = springforce[i] / 64; // scale spring force (cannot use shifts because of negative values) int maxforce = 120; // limit spring force springforce[i] = springforce[i] > maxforce ? maxforce : springforce[i] < -maxforce ? -maxforce : springforce[i]; // limit spring force @@ -11667,7 +11656,7 @@ uint16_t mode_particleSpringy(void) { PartSys->applyFriction((SEGMENT.intensity >> 2)); // add a small resetting force so particles return to resting position even under high damping - for (int32_t i = 1; i < PartSys->usedParticles - 1; i++) { + for (uint32_t i = 1; i < PartSys->usedParticles - 1; i++) { int restposition = (springlength >> 1) + i * springlength; // resting position int dx = restposition - PartSys->particles[i].x; // distance, always positive PartSys->applyForce(PartSys->particles[i], dx > 0 ? 1 : (dx < 0 ? -1 : 0), PartSys->advPartProps[i].forcecounter); @@ -11717,7 +11706,7 @@ uint16_t mode_particleSpringy(void) { } } - for (int32_t i = 0; i < PartSys->usedParticles; i++) { + for (uint32_t i = 0; i < PartSys->usedParticles; i++) { if (SEGMENT.custom2 == 255) { // map speed to hue int speedclr = ((int8_t(abs(PartSys->particles[i].vx))) >> 2) << 4; // scale for greater color variation, dump small values to avoid flickering //int speed = PartSys->particles[i].vx << 2; // +/- 512 diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp index 1d3d0929..90274fbe 100644 --- a/wled00/FXparticleSystem.cpp +++ b/wled00/FXparticleSystem.cpp @@ -775,7 +775,7 @@ __attribute__((optimize("O2"))) void ParticleSystem2D::renderParticle(const uint // for code simplicity, no y slicing is done, making very tall matrix configurations less efficient // note: also tested adding y slicing, it gives diminishing returns, some FX even get slower. FX not using gravity would benefit with a 10% FPS improvement void ParticleSystem2D::handleCollisions() { - int32_t collDistSq = particleHardRadius << 1; // distance is double the radius note: particleHardRadius is updated when setting global particle size + uint32_t collDistSq = particleHardRadius << 1; // distance is double the radius note: particleHardRadius is updated when setting global particle size collDistSq = collDistSq * collDistSq; // square it for faster comparison (square is one operation) // note: partices are binned in x-axis, assumption is that no more than half of the particles are in the same bin // if they are, collisionStartIdx is increased so each particle collides at least every second frame (which still gives decent collisions) @@ -798,13 +798,15 @@ void ParticleSystem2D::handleCollisions() { // fill the binIndices array for this bin for (uint32_t i = 0; i < usedParticles; i++) { - if (particles[pidx].ttl > 0 && particleFlags[pidx].outofbounds == 0 && particleFlags[pidx].collide) { // colliding particle + if (particles[pidx].ttl > 0) { // is alive if (particles[pidx].x >= binStart && particles[pidx].x <= binEnd) { // >= and <= to include particles on the edge of the bin (overlap to ensure boarder particles collide with adjacent bins) - if (binParticleCount >= maxBinParticles) { // bin is full, more particles in this bin so do the rest next frame - nextFrameStartIdx = pidx; // bin overflow can only happen once as bin size is at least half of the particles (or half +1) - break; + if(particleFlags[pidx].outofbounds == 0 && particleFlags[pidx].collide) { // particle is in frame and does collide note: checking flags is quite slow and usually these are set, so faster to check here + if (binParticleCount >= maxBinParticles) { // bin is full, more particles in this bin so do the rest next frame + nextFrameStartIdx = pidx; // bin overflow can only happen once as bin size is at least half of the particles (or half +1) + break; + } + binIndices[binParticleCount++] = pidx; } - binIndices[binParticleCount++] = pidx; } } pidx++; @@ -834,7 +836,7 @@ void ParticleSystem2D::handleCollisions() { // handle a collision if close proximity is detected, i.e. dx and/or dy smaller than 2*PS_P_RADIUS // takes two pointers to the particles to collide and the particle hardness (softer means more energy lost in collision, 255 means full hard) -__attribute__((optimize("O2"))) void ParticleSystem2D::collideParticles(PSparticle &particle1, PSparticle &particle2, int32_t dx, int32_t dy, const int32_t collDistSq) { +__attribute__((optimize("O2"))) void ParticleSystem2D::collideParticles(PSparticle &particle1, PSparticle &particle2, int32_t dx, int32_t dy, const uint32_t collDistSq) { int32_t distanceSquared = dx * dx + dy * dy; // Calculate relative velocity note: could zero check but that does not improve overall speed but deminish it as that is rarely the case and pushing is still required int32_t relativeVx = (int32_t)particle2.vx - (int32_t)particle1.vx; @@ -1415,6 +1417,12 @@ void ParticleSystem1D::render() { CRGB baseRGB; uint32_t brightness; // particle brightness, fades if dying + #ifdef ESP8266 // no local buffer on ESP8266 + if (motionBlur) + SEGMENT.fadeToBlackBy(255 - motionBlur); + else + SEGMENT.fill(BLACK); // clear the buffer before rendering to it + #else if (motionBlur) { // blurring active for (int32_t x = 0; x <= maxXpixel; x++) { fast_color_scale(framebuffer[x], motionBlur); @@ -1423,7 +1431,7 @@ void ParticleSystem1D::render() { else { // no blurring: clear buffer memset(framebuffer, 0, (maxXpixel+1) * sizeof(CRGB)); } - + #endif // go over particles and render them to the buffer for (uint32_t i = 0; i < usedParticles; i++) { if ( particles[i].ttl == 0 || particleFlags[i].outofbounds) @@ -1444,7 +1452,11 @@ void ParticleSystem1D::render() { } // apply smear-blur to rendered frame if (smearBlur) { + #ifdef ESP8266 + SEGMENT.blur(smearBlur, true); // no local buffer on ESP8266 + #else blur1D(framebuffer, maxXpixel + 1, smearBlur, 0); + #endif } // add background color @@ -1452,14 +1464,20 @@ void ParticleSystem1D::render() { if (bg_color > 0) { //if not black CRGB bg_color_crgb = bg_color; // convert to CRGB for (int32_t i = 0; i <= maxXpixel; i++) { + #ifdef ESP8266 // no local buffer on ESP8266 + SEGMENT.addPixelColor(i, bg_color, true); + #else fast_color_add(framebuffer[i], bg_color_crgb); + #endif } } + #ifndef ESP8266 // transfer the frame-buffer to segment for (int x = 0; x <= maxXpixel; x++) { SEGMENT.setPixelColor(x, framebuffer[x]); } + #endif } // calculate pixel positions and brightness distribution and render the particle to local buffer or global buffer @@ -1471,10 +1489,11 @@ __attribute__((optimize("O2"))) void ParticleSystem1D::renderParticle(const uint if (size == 0) { //single pixel particle, can be out of bounds as oob checking is made for 2-pixel particles (and updating it uses more code) uint32_t x = particles[particleindex].x >> PS_P_RADIUS_SHIFT_1D; if (x <= (uint32_t)maxXpixel) { //by making x unsigned there is no need to check < 0 as it will overflow - if (framebuffer) - fast_color_add(framebuffer[x], color, brightness); - else - SEGMENT.addPixelColor(x, color.scale8(brightness), true); + #ifdef ESP8266 // no local buffer on ESP8266 + SEGMENT.addPixelColor(x, color.scale8(brightness), true); + #else + fast_color_add(framebuffer[x], color, brightness); + #endif } return; } @@ -1536,10 +1555,11 @@ __attribute__((optimize("O2"))) void ParticleSystem1D::renderParticle(const uint else continue; } - if (framebuffer) - fast_color_add(framebuffer[xfb], renderbuffer[xrb]); - else - SEGMENT.addPixelColor(xfb, renderbuffer[xrb]); + #ifdef ESP8266 // no local buffer on ESP8266 + SEGMENT.addPixelColor(xfb, renderbuffer[xrb], true); + #else + fast_color_add(framebuffer[xfb], renderbuffer[xrb]); + #endif } } else { // standard rendering (2 pixels per particle) @@ -1558,10 +1578,11 @@ __attribute__((optimize("O2"))) void ParticleSystem1D::renderParticle(const uint } for (uint32_t i = 0; i < 2; i++) { if (pxlisinframe[i]) { - if (framebuffer) - fast_color_add(framebuffer[pixco[i]], color, pxlbrightness[i]); - else - SEGMENT.addPixelColor(pixco[i], color.scale8((uint8_t)pxlbrightness[i]), true); + #ifdef ESP8266 // no local buffer on ESP8266 + SEGMENT.addPixelColor(pixco[i], color.scale8((uint8_t)pxlbrightness[i]), true); + #else + fast_color_add(framebuffer[pixco[i]], color, pxlbrightness[i]); + #endif } } } @@ -1570,10 +1591,10 @@ __attribute__((optimize("O2"))) void ParticleSystem1D::renderParticle(const uint // detect collisions in an array of particles and handle them void ParticleSystem1D::handleCollisions() { - int32_t collisiondistance = particleHardRadius << 1; + uint32_t collisiondistance = particleHardRadius << 1; // note: partices are binned by position, assumption is that no more than half of the particles are in the same bin // if they are, collisionStartIdx is increased so each particle collides at least every second frame (which still gives decent collisions) - constexpr int BIN_WIDTH = 32 * PS_P_RADIUS_1D; // width of each bin, a compromise between speed and accuracy (lareger bins are faster but collapse more) + constexpr int BIN_WIDTH = 32 * PS_P_RADIUS_1D; // width of each bin, a compromise between speed and accuracy (larger bins are faster but collapse more) int32_t overlap = particleHardRadius << 1; // overlap bins to include edge particles to neighbouring bins if (advPartProps) //may be using individual particle size overlap += 256; // add 2 * max radius (approximately) @@ -1590,13 +1611,15 @@ void ParticleSystem1D::handleCollisions() { // fill the binIndices array for this bin for (uint32_t i = 0; i < usedParticles; i++) { - if (particles[pidx].ttl > 0 && particleFlags[pidx].outofbounds == 0 && particleFlags[pidx].collide) { // colliding particle + if (particles[pidx].ttl > 0) { // alivee if (particles[pidx].x >= binStart && particles[pidx].x <= binEnd) { // >= and <= to include particles on the edge of the bin (overlap to ensure boarder particles collide with adjacent bins) - if (binParticleCount >= maxBinParticles) { // bin is full, more particles in this bin so do the rest next frame - nextFrameStartIdx = pidx; // bin overflow can only happen once as bin size is at least half of the particles (or half +1) - break; + if(particleFlags[pidx].outofbounds == 0 && particleFlags[pidx].collide) { // particle is in frame and does collide note: checking flags is quite slow and usually these are set, so faster to check here + if (binParticleCount >= maxBinParticles) { // bin is full, more particles in this bin so do the rest next frame + nextFrameStartIdx = pidx; // bin overflow can only happen once as bin size is at least half of the particles (or half +1) + break; + } + binIndices[binParticleCount++] = pidx; } - binIndices[binParticleCount++] = pidx; } } pidx++; @@ -1622,7 +1645,7 @@ void ParticleSystem1D::handleCollisions() { } // handle a collision if close proximity is detected, i.e. dx and/or dy smaller than 2*PS_P_RADIUS // takes two pointers to the particles to collide and the particle hardness (softer means more energy lost in collision, 255 means full hard) -__attribute__((optimize("O2"))) void ParticleSystem1D::collideParticles(PSparticle1D &particle1, const PSparticleFlags1D &particle1flags, PSparticle1D &particle2, const PSparticleFlags1D &particle2flags, const int32_t dx, const uint32_t dx_abs, const int32_t collisiondistance) { +__attribute__((optimize("O2"))) void ParticleSystem1D::collideParticles(PSparticle1D &particle1, const PSparticleFlags1D &particle1flags, PSparticle1D &particle2, const PSparticleFlags1D &particle2flags, const int32_t dx, const uint32_t dx_abs, const uint32_t collisiondistance) { int32_t dv = particle2.vx - particle1.vx; int32_t dotProduct = (dx * dv); // is always negative if moving towards each other @@ -1710,11 +1733,15 @@ void ParticleSystem1D::updatePSpointers(bool isadvanced) { particleFlags = reinterpret_cast(this + 1); // pointer to particle flags particles = reinterpret_cast(particleFlags + numParticles); // pointer to particles sources = reinterpret_cast(particles + numParticles); // pointer to source(s) + #ifdef ESP8266 // no local buffer on ESP8266 + PSdataEnd = reinterpret_cast(sources + numSources); + #else framebuffer = reinterpret_cast(sources + numSources); // pointer to framebuffer // align pointer after framebuffer to 4bytes uintptr_t p = reinterpret_cast(framebuffer + (maxXpixel+1)); p = (p + 3) & ~0x03; // align to 4-byte boundary PSdataEnd = reinterpret_cast(p); // pointer to first available byte after the PS for FX additional data + #endif if (isadvanced) { advPartProps = reinterpret_cast(PSdataEnd); PSdataEnd = reinterpret_cast(advPartProps + numParticles); @@ -1770,7 +1797,9 @@ bool allocateParticleSystemMemory1D(const uint32_t numparticles, const uint32_t requiredmemory += sizeof(PSparticleFlags1D) * numparticles; requiredmemory += sizeof(PSparticle1D) * numparticles; requiredmemory += sizeof(PSsource1D) * numsources; + #ifndef ESP8266 // no local buffer on ESP8266 requiredmemory += sizeof(CRGB) * SEGMENT.virtualLength(); + #endif requiredmemory += additionalbytes + 3; // add 3 to ensure room for stuffing bytes to make it 4 byte aligned if (isadvanced) requiredmemory += sizeof(PSadvancedParticle1D) * numparticles; diff --git a/wled00/FXparticleSystem.h b/wled00/FXparticleSystem.h index c62e025d..95e6f522 100644 --- a/wled00/FXparticleSystem.h +++ b/wled00/FXparticleSystem.h @@ -192,7 +192,7 @@ private: //paricle physics applied by system if flags are set void applyGravity(); // applies gravity to all particles void handleCollisions(); - [[gnu::hot]] void collideParticles(PSparticle &particle1, PSparticle &particle2, const int32_t dx, const int32_t dy, const int32_t collDistSq); + [[gnu::hot]] void collideParticles(PSparticle &particle1, PSparticle &particle2, const int32_t dx, const int32_t dy, const uint32_t collDistSq); void fireParticleupdate(); //utility functions void updatePSpointers(const bool isadvanced, const bool sizecontrol); // update the data pointers to current segment data space @@ -356,14 +356,16 @@ private: //paricle physics applied by system if flags are set void applyGravity(); // applies gravity to all particles void handleCollisions(); - [[gnu::hot]] void collideParticles(PSparticle1D &particle1, const PSparticleFlags1D &particle1flags, PSparticle1D &particle2, const PSparticleFlags1D &particle2flags, const int32_t dx, const uint32_t dx_abs, const int32_t collisiondistance); + [[gnu::hot]] void collideParticles(PSparticle1D &particle1, const PSparticleFlags1D &particle1flags, PSparticle1D &particle2, const PSparticleFlags1D &particle2flags, const int32_t dx, const uint32_t dx_abs, const uint32_t collisiondistance); //utility functions void updatePSpointers(const bool isadvanced); // update the data pointers to current segment data space //void updateSize(PSadvancedParticle *advprops, PSsizeControl *advsize); // advanced size control [[gnu::hot]] void bounce(int8_t &incomingspeed, int8_t ¶llelspeed, int32_t &position, const uint32_t maxposition); // bounce on a wall // note: variables that are accessed often are 32bit for speed + #ifndef ESP8266 CRGB *framebuffer; // local frame buffer for rendering + #endif PSsettings1D particlesettings; // settings used when updating particles uint32_t numParticles; // total number of particles allocated by this system uint32_t emitIndex; // index to count through particles to emit so searching for dead pixels is faster