private void updateParticleState(float tpf) {
        // Force world transform to update
        this.getWorldTransform();

        TempVars vars = TempVars.get();

        Vector3f min = vars.vect1.set(Vector3f.POSITIVE_INFINITY);
        Vector3f max = vars.vect2.set(Vector3f.NEGATIVE_INFINITY);

        for (int i = 0; i < particles.length; ++i) {
            Particle p = particles[i];
            if (p.life == 0) { // particle is dead
//                assert i <= firstUnUsed;
                continue;
            }

            p.life -= tpf;
            if (p.life <= 0) {
                this.freeParticle(i);
                continue;
            }

            updateParticle(p, tpf, min, max);

            if (firstUnUsed < i) {
                this.swap(firstUnUsed, i);
                if (i == lastUsed) {
                    lastUsed = firstUnUsed;
                }
                firstUnUsed++;
            }
        }

        // Emitter distance from last location
        Vector3f lastDistance = null;
        if (lastPos != null && isInWorldSpace()) {
            lastDistance = getWorldTranslation().subtract(lastPos, lastPos);
        }

        // Spawns particles within the tpf timeslot with proper age
        float interval = 1f / particlesPerSec;
        float originalTpf = tpf;
        tpf += timeDifference;
        while (tpf > interval) {
            tpf -= interval;
            Particle p = emitParticle(min, max);
            if (p != null) {
                p.life -= tpf;
                if (lastPos != null && isInWorldSpace()) {
                    // Generate a hypothetical position by subtracting distance
                    // vector from particle position and use for interpolating
                    // particle. This will fix discrete particles motion when
                    // emitter is moving fast. - Ali-RS 2023-1-2
                    Vector3f lastPos = p.position.subtract(lastDistance, temp);
                    p.position.interpolateLocal(lastPos, 1 - tpf / originalTpf);
                }
                if (p.life <= 0) {
                    freeParticle(lastUsed);
                } else {
                    updateParticle(p, tpf, min, max);
                }
            }
        }
        timeDifference = tpf;

        if (lastPos == null) {
            lastPos = new Vector3f();
        }

        lastPos.set(getWorldTranslation());

        //This check avoids a NaN bounds when all the particles are dead during the first update.
        if (Vector3f.isValidVector(min) && Vector3f.isValidVector(max)) {
            BoundingBox bbox = (BoundingBox) this.getMesh().getBound();
            bbox.setMinMax(min, max);
            this.setBoundRefresh();
        }

        vars.release();
    }
Kills the particle at the given index.

@param index The index of the particle to kill
@see #getParticles()
jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java