/// <summary>
        /// Sends the death events.
        /// </summary>
        /// <param name="playgroundParticles">Playground particles.</param>
        /// <param name="p">Particle index.</param>
        public static void SendDeathEvents(PlaygroundParticlesC playgroundParticles, int p)
        {
            if ((playgroundParticles.playgroundCache.life[p]>0||playgroundParticles.playgroundCache.changedByPropertyDeath[p]) && !playgroundParticles.playgroundCache.isNonBirthed[p]) {
                if (playgroundParticles.loop || (!playgroundParticles.loop && playgroundParticles.playgroundCache.isFirstLoop[p])) {
                    playgroundParticles.SendEvent(EVENTTYPEC.Death, p);
                }
            }
            if (playgroundParticles.hasEventManipulatorLocal) {
                for (int i = 0; i<playgroundParticles.manipulators.Count; i++) {
                    if (playgroundParticles.manipulators[i].trackParticles &&
                        playgroundParticles.manipulators[i].RemoveParticle (playgroundParticles.particleSystemId, p)) {

                        if (playgroundParticles.manipulators[i].sendEventDeath) {
                            playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p);
                            playgroundParticles.manipulators[i].SendParticleEventDeath();
                        }
                    }
                }
            }
            if (playgroundParticles.hasEventManipulatorGlobal) {

                for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) {
                    if (PlaygroundC.reference.manipulators[i].trackParticles &&
                        PlaygroundC.reference.manipulators[i].RemoveParticle (playgroundParticles.particleSystemId, p)) {

                        if (PlaygroundC.reference.manipulators[i].sendEventDeath) {
                            playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p);
                            PlaygroundC.reference.manipulators[i].SendParticleEventDeath();
                        }
                    }
                }
            }
            playgroundParticles.playgroundCache.isNonBirthed[p] = true;
        }
        public static void ThreadedCalculations(PlaygroundParticlesC playgroundParticles)
        {
            // Refresh delta time
            if (!playgroundParticles.isPrewarming) {
                playgroundParticles.localDeltaTime = (PlaygroundC.globalTime-playgroundParticles.lastTimeUpdated)*playgroundParticles.particleTimescale;
                playgroundParticles.localTime += playgroundParticles.localDeltaTime;
                playgroundParticles.lastTimeUpdated = PlaygroundC.globalTime;
            }

            // Set delta time
            playgroundParticles.t = playgroundParticles.localDeltaTime;

            if (playgroundParticles.particleCount<=0 ||
                playgroundParticles.playgroundCache.color.Length!=playgroundParticles.particleCount ||
                playgroundParticles.playgroundCache.targetPosition.Length!=playgroundParticles.particleCount ||
                playgroundParticles.playgroundCache.targetDirection.Length!=playgroundParticles.particleCount ||
                playgroundParticles.source==SOURCEC.State && playgroundParticles.states[playgroundParticles.activeState].IsInitializing() ||
                playgroundParticles.isYieldRefreshing || !PlaygroundC.IsReady() || playgroundParticles.isSettingParticleTime) {
                playgroundParticles.isDoneThread = true;
                playgroundParticles.threadHadNoActiveParticles = false;
                return;
            }
            if (PlaygroundC.reference.calculate && playgroundParticles.calculate && !playgroundParticles.inTransition && playgroundParticles.hasActiveParticles) {}
            else if (playgroundParticles.source!=SOURCEC.Script) {
                playgroundParticles.isDoneThread = true;
                playgroundParticles.cameFromNonCalculatedFrame = true;
                return;
            } else {
                playgroundParticles.isDoneThread = true;
                return;
            }

            // Check that simplex turbulence is available (will be next frame in case not)
            if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning && playgroundParticles.turbulenceStrength>0 && playgroundParticles.turbulenceType!=TURBULENCETYPE.None) {
                if (playgroundParticles.turbulenceType==TURBULENCETYPE.Simplex && playgroundParticles.turbulenceSimplex==null) {
                    playgroundParticles.isDoneThread = true;
                    playgroundParticles.threadHadNoActiveParticles = false;
                    return;
                }
            }

            float t = playgroundParticles.t;

            // Prepare variables for particle source positions
            Matrix4x4 fMx = new Matrix4x4();
            if (playgroundParticles.source==SOURCEC.State || playgroundParticles.source==SOURCEC.WorldObject || playgroundParticles.source==SOURCEC.SkinnedWorldObject) {
                fMx.SetTRS(Vector3.zero, playgroundParticles.stRot, new Vector3(1f,1f,1f));
            }

            bool noActiveParticles = true;
            if (playgroundParticles.source==SOURCEC.Transform)
                for (int i = 0; i<playgroundParticles.sourceTransforms.Count; i++)
                    playgroundParticles.sourceTransforms[i].UpdateMatrix();

            // Update skinned mesh vertices
            if (playgroundParticles.source==SOURCEC.SkinnedWorldObject && playgroundParticles.skinnedWorldObjectReady && !playgroundParticles.forceSkinnedMeshUpdateOnMainThread && PlaygroundC.reference.skinnedMeshThreadMethod==ThreadMethodComponent.InsideParticleCalculation) {
                playgroundParticles.skinnedWorldObject.Update();
            }

            // Misc
            int pCount = playgroundParticles.particleCache.Length;

            // Check that cache is correct
            if (playgroundParticles.playgroundCache.lifetimeSubtraction.Length!=pCount)
                SetLifetimeSubtraction(playgroundParticles);
            if (playgroundParticles.playgroundCache.maskAlpha.Length!=pCount) {
                playgroundParticles.playgroundCache.maskAlpha = new float[pCount];
                playgroundParticles.playgroundCache.isMasked = new bool[pCount];
            }
            if (playgroundParticles.playgroundCache.manipulatorId.Length!=pCount)
                playgroundParticles.playgroundCache.manipulatorId = new int[pCount];
            if (playgroundParticles.playgroundCache.excludeFromManipulatorId.Length!=pCount)
                playgroundParticles.playgroundCache.excludeFromManipulatorId = new int[pCount];
            if (playgroundParticles.playgroundCache.noForce.Length!=pCount)
                playgroundParticles.playgroundCache.noForce = new bool[pCount];
            if (playgroundParticles.playgroundCache.isNonBirthed.Length!=pCount)
                playgroundParticles.playgroundCache.isNonBirthed = new bool[pCount];
            if (playgroundParticles.playgroundCache.isFirstLoop.Length!=pCount)
                playgroundParticles.playgroundCache.isFirstLoop = new bool[pCount];
            if (playgroundParticles.playgroundCache.isCalculatedThisFrame.Length!=pCount)
                playgroundParticles.playgroundCache.isCalculatedThisFrame = new bool[pCount];
            if (playgroundParticles.playgroundCache.simulate.Length!=pCount) {
                playgroundParticles.playgroundCache.simulate = new bool[pCount];
                for (int p = 0; p<pCount; p++)
                    playgroundParticles.playgroundCache.simulate[p] = true;
            }
            if (playgroundParticles.applyParticleMask && (playgroundParticles.playgroundCache.maskSorting==null || playgroundParticles.playgroundCache.maskSorting.Length!=pCount || playgroundParticles.particleMaskSorting!=playgroundParticles.previousMaskSorting))
                playgroundParticles.RefreshMaskSorting();

            Vector3 zero = Vector3.zero;
            Vector3 up = Vector3.up;
            float initYpos = PlaygroundC.initialTargetPosition.y;

            // Calculation loop
            for (int p = 0; p<playgroundParticles.particleCount; p++) {

                // Check that particle count is correct
                if (pCount != playgroundParticles.particleCache.Length || playgroundParticles.isYieldRefreshing || playgroundParticles.isSettingParticleTime || playgroundParticles.isSettingParticleCount) {
                    playgroundParticles.cameFromNonEmissionFrame = false;
                    playgroundParticles.isDoneThread = true;
                    playgroundParticles.threadHadNoActiveParticles = false;
                    return;
                }

                // Check simulation
                if (!playgroundParticles.playgroundCache.simulate[p]) {
                    if (playgroundParticles.playgroundCache.rebirth[p] && playgroundParticles.loop) {
                        playgroundParticles.InactivateParticle(p);
                    }
                    continue;
                } else {
                    noActiveParticles = false;
                }

                // This particle is about to be calculated
                playgroundParticles.playgroundCache.isCalculatedThisFrame[p] = false;

                // Prepare variables inside scope
                Vector3 deltaVelocity;
                float lifeInSeconds = (playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-playgroundParticles.playgroundCache.lifetimeSubtraction[p];
                float normalizedLife = Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/lifeInSeconds);
                float normalizedP = (p*1f)/(playgroundParticles.particleCount*1f);
                float lifetimePositioningTimeScale = 1f;
                if (playgroundParticles.applyLifetimePositioningTimeScale)
                    lifetimePositioningTimeScale = playgroundParticles.lifetimePositioningTimeScale.Evaluate(normalizedLife);

                // Apply particle mask
                if (playgroundParticles.applyParticleMask) {
                    int maskedP = playgroundParticles.playgroundCache.maskSorting[p];
                    if (p<playgroundParticles.particleMask) {
                        if (playgroundParticles.playgroundCache.maskAlpha[maskedP]<=0 || playgroundParticles.particleMaskTime<=0) {
                            playgroundParticles.playgroundCache.isMasked[maskedP] = true;
                            playgroundParticles.playgroundCache.maskAlpha[maskedP] = 0;
                            playgroundParticles.particleCache[maskedP].size = 0;
                        } else {
                            playgroundParticles.playgroundCache.maskAlpha[maskedP] -= (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime;
                        }
                    } else {
                        if (playgroundParticles.playgroundCache.maskAlpha[maskedP]>=1f || playgroundParticles.particleMaskTime<=0) {
                            playgroundParticles.playgroundCache.isMasked[maskedP] = false;
                            playgroundParticles.playgroundCache.maskAlpha[maskedP] = 1f;
                        } else {
                            playgroundParticles.playgroundCache.maskAlpha[maskedP] += (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime;
                        }
                    }
                } else {
                    playgroundParticles.playgroundCache.isMasked[p] = false;
                    playgroundParticles.playgroundCache.maskAlpha[p] = 1f;
                }

                Color32 lifetimeColor = playgroundParticles.GetParticleColor(p, normalizedLife, normalizedP);

                // Assign color to particle
                playgroundParticles.particleCache[p].color = lifetimeColor;

                // Give Playground Cache its color value
                playgroundParticles.playgroundCache.color[p] = lifetimeColor;

                // Source positions
                if (playgroundParticles.emit) {
                    if (!playgroundParticles.syncPositionsOnMainThread)
                        playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                    switch (playgroundParticles.source) {
                    case SOURCEC.State:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {

                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.states[playgroundParticles.activeState].GetNormal(p)));
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.states[playgroundParticles.activeState].GetNormal(p)));
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife);
                            }

                            playgroundParticles.SetSourcePosition(p);

                            // Local space compensation calculation
                            if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                        }
                    break;
                    case SOURCEC.Transform:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {

                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                if (playgroundParticles.localSpace && playgroundParticles.transformIndex!=playgroundParticles.psTransformNum)
                                    playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.particleSystemInverseRotation*playgroundParticles.sourceTransforms[playgroundParticles.transformIndex].rotation*playgroundParticles.playgroundCache.initialLocalVelocity[p];
                                else
                                    playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.sourceTransforms[playgroundParticles.transformIndex].rotation*playgroundParticles.playgroundCache.initialLocalVelocity[p];
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.sourceTransforms[playgroundParticles.transformIndex].rotation*playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale);
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife*lifetimePositioningTimeScale);
                            }

                            playgroundParticles.SetSourcePosition(p);

                            // Local space compensation calculation
                            if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                        }
                    break;
                    case SOURCEC.WorldObject:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length]));
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length]));
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife);
                            }
                            playgroundParticles.SetSourcePosition(p);

                            // Local space compensation calculation
                            if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = (playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]);
                        }
                    break;
                    case SOURCEC.SkinnedWorldObject:
                        if (playgroundParticles.skinnedWorldObjectReady) {
                            if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                                if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                    playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.skinnedWorldObject.normals[p%playgroundParticles.skinnedWorldObject.normals.Length]));
                                if (playgroundParticles.onlyLifetimePositioning) {
                                    playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.skinnedWorldObject.normals[p%playgroundParticles.skinnedWorldObject.normals.Length]));
                                    if (playgroundParticles.applyLifetimePositioningPositionScale)
                                        playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife);
                                }

                                playgroundParticles.SetSourcePosition(p);

                                // Local space compensation calculation
                                if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                    playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                            }
                        }

                    break;
                    case SOURCEC.Projection:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.projection.GetNormal(p), playgroundParticles.playgroundCache.initialLocalVelocity[p]);
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.projection.GetNormal(p), playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale));
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife);
                            }

                            playgroundParticles.SetSourcePosition(p);
                        }

                    break;
                    case SOURCEC.Paint:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.paint.GetRotation(p)*Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.paint.GetNormal(p));
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.paint.GetRotation(p)*Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.paint.GetNormal(p));
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife);
                            }

                            playgroundParticles.SetSourcePosition(p);

                            // Local space compensation calculation
                            if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                        }
                    break;
                    case SOURCEC.Spline:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                            if (playgroundParticles.treatAsOneSpline)
                                playgroundParticles.splineIndex = (int)((((p*1f)/(pCount*1f))*playgroundParticles.splines.Count)+playgroundParticles.splineTimeOffset)%playgroundParticles.splines.Count;
                            else
                                playgroundParticles.splineIndex = p%playgroundParticles.splines.Count;

                            float pSplineTime;
                            if (playgroundParticles.treatAsOneSpline)
                                pSplineTime = ((p*playgroundParticles.splines.Count*1f) / (pCount*1f))+playgroundParticles.splineTimeOffset;
                            else
                                pSplineTime = ((p*1f) / (pCount*1f)) + playgroundParticles.splineTimeOffset;

                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.splines[playgroundParticles.splineIndex].GetDirection(pSplineTime), playgroundParticles.playgroundCache.initialLocalVelocity[p]);
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.splines[playgroundParticles.splineIndex].GetDirection(pSplineTime), playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale));
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife*lifetimePositioningTimeScale);
                            }

                            playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.splines[playgroundParticles.splineIndex].GetPoint(pSplineTime);
                            if (playgroundParticles.applySourceScatter)
                                playgroundParticles.playgroundCache.targetPosition[p] += Vector3.Scale (playgroundParticles.playgroundCache.scatterPosition[p],playgroundParticles.scatterScale);

                            // Local space compensation calculation
                            if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                        }
                    break;
                    }
                }

                // Set initial particle values if life is 0
                if (playgroundParticles.playgroundCache.life[p]==0) {
                    if (!playgroundParticles.onlyLifetimePositioning) {
                        playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p];

                        // Apply birth position delta
                        if (playgroundParticles.applyDeltaOnRebirth && !playgroundParticles.cameFromNonEmissionFrame && playgroundParticles.localTime-playgroundParticles.emissionStopped > playgroundParticles.lifetime && playgroundParticles.playgroundCache.birthDelay[p]==0 && !playgroundParticles.onlySourcePositioning) {
                            float timeDelta = playgroundParticles.localTime-playgroundParticles.playgroundCache.birth[p];
                            if (playgroundParticles.isPrewarming) timeDelta *=.5f;
                            playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.velocity[p]*playgroundParticles.velocityScale*timeDelta;
                        }
                    }
                    playgroundParticles.playgroundCache.initialColor[p] = lifetimeColor;

                    // Delta movement velocity
                    if (!playgroundParticles.cameFromNonEmissionFrame && playgroundParticles.calculateDeltaMovement && !playgroundParticles.isPrewarming && playgroundParticles.source!=SOURCEC.Script) {
                        deltaVelocity = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                        playgroundParticles.playgroundCache.velocity[p] += (deltaVelocity*playgroundParticles.deltaMovementStrength)/playgroundParticles.realSimulationTime;
                    }

                    playgroundParticles.playgroundCache.birthDelay[p] = 0;
                }

                if (playgroundParticles.playgroundCache.birth[p]>playgroundParticles.localTime) {
                    playgroundParticles.particleCache[p].size = 0;
                    playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;
                }

                // Particle lifetime, velocity and manipulators
                if (playgroundParticles.playgroundCache.rebirth[p]) {

                    // Particle is alive
                    if (playgroundParticles.playgroundCache.birth[p]+playgroundParticles.playgroundCache.life[p]<=playgroundParticles.localTime+lifeInSeconds && (!playgroundParticles.playgroundCache.isNonBirthed[p] || playgroundParticles.onlyLifetimePositioning || playgroundParticles.onlySourcePositioning)) {

                        // Lifetime size
                        if (!playgroundParticles.playgroundCache.changedByPropertySize[p]) {
                            if (playgroundParticles.applyLifetimeSize && !playgroundParticles.applyParticleArraySize)
                                playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(normalizedLife)*playgroundParticles.scale;
                            else if (playgroundParticles.applyLifetimeSize && playgroundParticles.applyParticleArraySize)
                                playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(normalizedLife)*playgroundParticles.particleArraySize.Evaluate((p*1f)/(playgroundParticles.particleCount*1f))*playgroundParticles.scale;
                            else if (playgroundParticles.applyParticleArraySize)
                                playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.particleArraySize.Evaluate((p*1f)/(playgroundParticles.particleCount*1f))*playgroundParticles.scale;
                            else playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.scale;
                        }

                        if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning) {

                            if (!playgroundParticles.playgroundCache.noForce[p] && playgroundParticles.playgroundCache.life[p]>0) {

                                // Velocity bending
                                if (playgroundParticles.applyVelocityBending) {
                                    if (playgroundParticles.velocityBendingType==VELOCITYBENDINGTYPEC.SourcePosition) {
                                        playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect(
                                            new Vector3(
                                            playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x,
                                            playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y,
                                            playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z
                                            ),
                                            (playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.position[p]).normalized
                                            )*t;
                                    } else {
                                        playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect(
                                            new Vector3(
                                            playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x,
                                            playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y,
                                            playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z
                                            ),
                                            (playgroundParticles.playgroundCache.previousParticlePosition[p]-playgroundParticles.playgroundCache.position[p]).normalized
                                            )*t;
                                    }
                                }

                                // Set previous target position (used by delta velocity & local space movement compensation)
                                playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];

                                // Gravity
                                if (playgroundParticles.localSpace && playgroundParticles.source==SOURCEC.Transform && playgroundParticles.transformIndex!=playgroundParticles.psTransformNum)
                                    playgroundParticles.playgroundCache.velocity[p] -= playgroundParticles.sourceTransforms[playgroundParticles.transformIndex].rotation*playgroundParticles.gravity*t;
                                else
                                    playgroundParticles.playgroundCache.velocity[p] -= playgroundParticles.gravity*t;

                                // Lifetime additive velocity
                                if (playgroundParticles.applyLifetimeVelocity)
                                    playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.lifetimeVelocity.Evaluate(normalizedLife, playgroundParticles.lifetimeVelocityScale)*t;

                                // Turbulence inside the calculation loop
                                if (playgroundParticles.HasTurbulence() && PlaygroundC.reference.turbulenceThreadMethod==ThreadMethodComponent.InsideParticleCalculation) {
                                if (!playgroundParticles.playgroundCache.noForce[p])
                                    Turbulence(playgroundParticles, playgroundParticles.turbulenceSimplex, p, playgroundParticles.t, playgroundParticles.turbulenceType, playgroundParticles.turbulenceTimeScale, playgroundParticles.turbulenceScale/playgroundParticles.velocityScale, playgroundParticles.turbulenceStrength, playgroundParticles.turbulenceApplyLifetimeStrength, playgroundParticles.turbulenceLifetimeStrength);
                                }

                                // Damping, max velocity, constraints and final positioning

                                // Max velocity
                                if (playgroundParticles.playgroundCache.velocity[p].sqrMagnitude>playgroundParticles.maxVelocity)
                                    playgroundParticles.playgroundCache.velocity[p] = Vector3.ClampMagnitude(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.maxVelocity);

                                // Damping
                                if (playgroundParticles.damping>0)
                                    playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], zero, playgroundParticles.damping*t);

                                // Transition back to source
                                if (playgroundParticles.transitionBackToSource) {
                                    float transitionAmount = playgroundParticles.transitionBackToSourceAmount.Evaluate(normalizedLife)*playgroundParticles.particleTimescale;
                                    playgroundParticles.playgroundCache.position[p] = Vector3.Lerp(playgroundParticles.playgroundCache.position[p], playgroundParticles.playgroundCache.targetPosition[p], transitionAmount);
                                    playgroundParticles.playgroundCache.velocity[p] *= 1f-transitionAmount;
                                }

                                // Axis constraints
                                if (playgroundParticles.axisConstraints.x)
                                    playgroundParticles.playgroundCache.velocity[p].x = 0;
                                if (playgroundParticles.axisConstraints.y)
                                    playgroundParticles.playgroundCache.velocity[p].y = 0;
                                if (playgroundParticles.axisConstraints.z)
                                    playgroundParticles.playgroundCache.velocity[p].z = 0;

                                // Set calculated collision position
                                playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.position[p];

                                // Relocate
                                playgroundParticles.playgroundCache.position[p] += (playgroundParticles.playgroundCache.velocity[p]*playgroundParticles.velocityScale)*t;
                                if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) {
                                    if (!playgroundParticles.applyMovementCompensationLifetimeStrength)
                                        playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p];
                                    else
                                        playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p]*playgroundParticles.movementCompensationLifetimeStrength.Evaluate(normalizedLife);
                                }

                                // Set particle velocity to be able to stretch towards movement
                                if (playgroundParticles.renderModeStretch && playgroundParticles.realSimulationTime>0) {
                                    if (playgroundParticles.applyLifetimeStretching) {
                                        playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p])/playgroundParticles.realSimulationTime, t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/lifeInSeconds);
                                    } else {
                                        if (playgroundParticles.stretchSpeed>0)
                                            playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p])/playgroundParticles.realSimulationTime, t*playgroundParticles.stretchSpeed);
                                        else playgroundParticles.particleCache[p].velocity = playgroundParticles.stretchStartDirection;
                                    }
                                }

                            }
                        } else {

                            // Only Source Positioning / Lifetime Positioning

                            if (playgroundParticles.onlyLifetimePositioning && !playgroundParticles.onlySourcePositioning) {
                                if (!playgroundParticles.playgroundCache.changedByPropertyTarget[p]) {

                                    // Lifetime Positioning by Vector3 Animation Curve
                                    if (playgroundParticles.lifetimePositioningUsesSourceDirection && playgroundParticles.source!=SOURCEC.Script) {
                                        playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+(playgroundParticles.playgroundCache.targetDirection[p]);
                                    } else {
                                        if (!playgroundParticles.applyLifetimePositioningPositionScale) {
                                            playgroundParticles.playgroundCache.position[p] =
                                            playgroundParticles.playgroundCache.targetPosition[p]+
                                                playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale);
                                        } else {
                                            playgroundParticles.playgroundCache.position[p] =
                                            playgroundParticles.playgroundCache.targetPosition[p]+
                                            playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale)*
                                            playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife*lifetimePositioningTimeScale);
                                        }
                                    }
                                }
                            } else if (playgroundParticles.source!=SOURCEC.Script && !playgroundParticles.playgroundCache.isNonBirthed[p]) {
                                playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p];
                            }

                            // Set particle velocity to be able to stretch towards movement
                            if (playgroundParticles.renderModeStretch && playgroundParticles.realSimulationTime>0) {
                                if (playgroundParticles.applyLifetimeStretching) {
                                    playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p])/playgroundParticles.realSimulationTime, t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/lifeInSeconds);
                                } else {
                                    if (playgroundParticles.stretchSpeed>0)
                                        playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p])/playgroundParticles.realSimulationTime, t*playgroundParticles.stretchSpeed);
                                    else playgroundParticles.particleCache[p].velocity = playgroundParticles.stretchStartDirection;
                                }
                            }

                            playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];

                        }

                        // Rotation
                        if (t!=0) {
                            if (!playgroundParticles.rotateTowardsDirection)
                                playgroundParticles.playgroundCache.rotation[p] += playgroundParticles.playgroundCache.rotationSpeed[p]*t;
                            else if (playgroundParticles.playgroundCache.life[p]!=0) {
                                playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]+SignedAngle(
                                    up,
                                    playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p],
                                    playgroundParticles.rotationNormal
                                );
                            }
                        }

                        if (!playgroundParticles.syncPositionsOnMainThread && playgroundParticles.playgroundCache.life[p]>0)
                            playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p];

                        // Set previous particle position
                        playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.position[p];

                        // Send timed event
                        if (playgroundParticles.hasTimerEvent)
                            playgroundParticles.SendEvent(EVENTTYPEC.Time, p);
                    } else {
                        playgroundParticles.particleCache[p].size = 0;
                        playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;
                    }

                    // Calculate lifetime
                    float evaluatedLife = (playgroundParticles.localTime-playgroundParticles.playgroundCache.birth[p])/lifeInSeconds;

                    // Lifetime
                    if (playgroundParticles.playgroundCache.life[p]<playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p]) {
                        playgroundParticles.playgroundCache.life[p] = lifeInSeconds*evaluatedLife;
                        if (!playgroundParticles.syncPositionsOnMainThread)
                            playgroundParticles.particleCache[p].lifetime = Mathf.Clamp (lifeInSeconds*(1f-evaluatedLife), playgroundParticles.minShurikenLifetime, playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p]);

                        if (playgroundParticles.lifetimeValueMethod==VALUEMETHOD.RandomBetweenTwoValues && playgroundParticles.playgroundCache.life[p]>lifeInSeconds) {

                            // Send death event for particles with lifetime subtraction
                            if ((playgroundParticles.hasEvent||playgroundParticles.hasEventManipulatorGlobal||playgroundParticles.hasEventManipulatorLocal) && !playgroundParticles.playgroundCache.isNonBirthed[p])
                                SendDeathEvents(playgroundParticles, p);

                            playgroundParticles.particleCache[p].size = 0;
                            playgroundParticles.particleCache[p].velocity = zero;
                            playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;

                        }
                    } else {

                        // Particle exceeded with death property
                        if (!playgroundParticles.loop && !playgroundParticles.playgroundCache.isNonBirthed[p]) {

                            // Send death event for particles which died of unnatural cause such as property death, the worst type of death
                            if (playgroundParticles.hasEvent||playgroundParticles.hasEventManipulatorGlobal||playgroundParticles.hasEventManipulatorLocal)
                                SendDeathEvents(playgroundParticles, p);

                            playgroundParticles.InactivateParticle(p);
                            continue;
                        }

                        // Loop exceeded normally
                        if (!playgroundParticles.loop && playgroundParticles.localTime>playgroundParticles.simulationStarted+(playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-.01f) {
                            playgroundParticles.loopExceeded = true;
                            if (playgroundParticles.loopExceededOnParticle==p && evaluatedLife>2f) {
                                if (playgroundParticles.disableOnDone)
                                    playgroundParticles.queueEmissionHalt = true;
                                playgroundParticles.threadHadNoActiveParticles = true;
                                playgroundParticles.hasActiveParticles = false;
                            }
                            if (playgroundParticles.loopExceededOnParticle==-1)
                                playgroundParticles.loopExceededOnParticle = p;
                        }

                        playgroundParticles.particleCache[p].velocity = zero;

                        // Send death event for particles with full lifetime length
                        if ((playgroundParticles.hasEvent||playgroundParticles.hasEventManipulatorGlobal||playgroundParticles.hasEventManipulatorLocal) && !playgroundParticles.playgroundCache.isNonBirthed[p])
                            SendDeathEvents(playgroundParticles, p);

                        // New cycle begins
                        if (playgroundParticles.localTime>=playgroundParticles.playgroundCache.birth[p]+playgroundParticles.playgroundCache.birthDelay[p] && !playgroundParticles.loopExceeded && playgroundParticles.source!=SOURCEC.Script && playgroundParticles.emit) {
                            if (!playgroundParticles.playgroundCache.changedByPropertyDeath[p] || playgroundParticles.playgroundCache.changedByPropertyDeath[p] && playgroundParticles.localTime>playgroundParticles.playgroundCache.death[p]) {
                                Rebirth(playgroundParticles, p, playgroundParticles.internalRandom01);
                            } else {
                                playgroundParticles.particleCache[p].velocity = zero;
                                playgroundParticles.particleCache[p].size = 0;
                                playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;
                            }
                        } else {
                            playgroundParticles.InactivateParticle(p);
                            continue;
                        }

                    }

                    // Local Manipulators
                    for (int m = 0; m<playgroundParticles.manipulators.Count; m++) {
                        CalculateManipulator(playgroundParticles, playgroundParticles.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], (playgroundParticles.localSpace?playgroundParticles.manipulators[m].transform.localPosition:playgroundParticles.manipulators[m].transform.position)+playgroundParticles.manipulatorFix, playgroundParticles.localSpace);
                    }

                    // Global Manipulators
                    if (playgroundParticles.hasGlobalAffectingManipulators) {
                        for (int m = 0; m<PlaygroundC.reference.manipulators.Count; m++) {
                            if ((PlaygroundC.reference.manipulators[m].affects.value & 1<<playgroundParticles.GetLayer())!=0)
                                CalculateManipulator(playgroundParticles, PlaygroundC.reference.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], (playgroundParticles.localSpace?PlaygroundC.reference.manipulators[m].transform.localPosition:PlaygroundC.reference.manipulators[m].transform.position)+playgroundParticles.manipulatorFix, playgroundParticles.localSpace);
                        }
                    }

                    // Set particle size
                    if (!playgroundParticles.syncPositionsOnMainThread)
                        playgroundParticles.particleCache[p].size = (playgroundParticles.playgroundCache.maskAlpha[p]>0&&playgroundParticles.particleCache[p].position.y!=initYpos)?playgroundParticles.playgroundCache.size[p]:0;

                } else {

                    // Particle is set to not rebirth
                    playgroundParticles.InactivateParticle(p);
                }

                // Set particle position if no sync
                if (!playgroundParticles.syncPositionsOnMainThread) {
                    playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.position[p];
                }

                // Particle got calculated
                playgroundParticles.playgroundCache.isCalculatedThisFrame[p] = true;

            }// <- Particle loop ends here

            // Reset for next frame
            if (playgroundParticles.isSettingParticleTime) playgroundParticles.threadHadNoActiveParticles = false;
            else playgroundParticles.threadHadNoActiveParticles = noActiveParticles && !playgroundParticles.emit || noActiveParticles && !playgroundParticles.loop;
            playgroundParticles.cameFromNonEmissionFrame = false;
            playgroundParticles.isDoneThread = true;

            // Turbulence calculated here when using PlaygroundC.turbulenceThreadMethod of ThreadMethodComponent.OnePerSystem
            if (playgroundParticles.HasTurbulence() && PlaygroundC.reference.turbulenceThreadMethod==ThreadMethodComponent.OnePerSystem) {
                PlaygroundC.RunAsync(()=>{
                    for (int p = 0; p<playgroundParticles.particleCount; p++) {
                        if (!playgroundParticles.playgroundCache.noForce[p])
                            Turbulence(playgroundParticles, playgroundParticles.turbulenceSimplex, p, playgroundParticles.t, playgroundParticles.turbulenceType, playgroundParticles.turbulenceTimeScale, playgroundParticles.turbulenceScale/playgroundParticles.velocityScale, playgroundParticles.turbulenceStrength, playgroundParticles.turbulenceApplyLifetimeStrength, playgroundParticles.turbulenceLifetimeStrength);
                    }
                });
            }
        }
        /// <summary>
        /// Calculates particle collisions, this runs automatically if collision is set to true. (Must currently run on main-thread due to the Physics.Raycast dependency.)
        /// </summary>
        /// <param name="playgroundParticles">Particle Playground system.</param>
        public static void Collisions(PlaygroundParticlesC playgroundParticles)
        {
            if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning && playgroundParticles.collisionRadius>0) {
                Ray ray = new Ray();
                float distance;
                bool is3d = playgroundParticles.collisionType==COLLISIONTYPEC.Physics3D;
                bool hasCollisionExclusion = playgroundParticles.collisionExclusion!=null && playgroundParticles.collisionExclusion.Count>0;
                RaycastHit hitInfo;
                RaycastHit2D hitInfo2D;
                bool hasEvents = playgroundParticles.events.Count>0;
                Vector3 preCollisionVelocity;

                // Prepare the infinite collision planes
                if (playgroundParticles.collision && playgroundParticles.colliders.Count>0) {
                    for (int c = 0; c<playgroundParticles.colliders.Count; c++) {
                        playgroundParticles.colliders[c].UpdatePlane();
                    }
                }

                // Check cache length
                if (playgroundParticles.playgroundCache.noForce.Length!=playgroundParticles.particleCount)
                    playgroundParticles.playgroundCache.noForce = new bool[playgroundParticles.particleCount];
                if (playgroundParticles.playgroundCache.lifetimeSubtraction.Length!=playgroundParticles.particleCount)
                    playgroundParticles.playgroundCache.lifetimeSubtraction = new float[playgroundParticles.particleCount];

                for (int p = 0; p<playgroundParticles.particleCount; p++) {

                    if (playgroundParticles.playgroundCache.life[p]==0 || playgroundParticles.playgroundCache.life[p]>=(playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-playgroundParticles.playgroundCache.lifetimeSubtraction[p] || playgroundParticles.playgroundCache.noForce[p]) continue;

                    // Playground Plane colliders (never exceed these)
                    for (int c = 0; c<playgroundParticles.colliders.Count; c++) {
                        if (playgroundParticles.colliders[c].enabled && playgroundParticles.colliders[c].transform && !playgroundParticles.colliders[c].plane.GetSide(playgroundParticles.playgroundCache.position[p])) {

                            // Set particle to location
                            ray.origin = playgroundParticles.playgroundCache.position[p];
                            ray.direction = playgroundParticles.colliders[c].plane.normal;
                            if (playgroundParticles.colliders[c].plane.Raycast(ray, out distance))
                                playgroundParticles.playgroundCache.position[p] = ray.GetPoint(distance);

                            // Store velocity before collision
                            preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p];

                            // Reflect particle
                            playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.colliders[c].plane.normal+RandomVector3(playgroundParticles.internalRandom01, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness;

                            // Apply lifetime loss
                            if (playgroundParticles.lifetimeLoss>0) {
                                playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss);
                                playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true;
                            }

                            // Send event
                            if (hasEvents)
                                playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, playgroundParticles.colliders[c].transform);
                        }
                    }

                    // Colliders in scene
                    if (playgroundParticles.playgroundCache.velocity[p].magnitude>PlaygroundC.collisionSleepVelocity) {

                        // Collide by checking for potential passed collider in the direction of this particle's velocity from the previous frame
                        if (is3d) {
                            if (Physics.Raycast(
                                playgroundParticles.playgroundCache.collisionParticlePosition[p],
                                (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p]).normalized,
                                out hitInfo,
                                (Vector3.SqrMagnitude(playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p])+playgroundParticles.collisionRadius)*2f,
                                playgroundParticles.collisionMask))
                            {

                                // Check that this object isn't excluded
                                if (hasCollisionExclusion) {
                                    if (playgroundParticles.collisionExclusion.Contains(hitInfo.transform))
                                        continue;
                                }

                                // Set particle to location
                                playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.collisionParticlePosition[p];

                                // Store velocity before collision
                                preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p];

                                // Reflect particle
                                playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], hitInfo.normal+RandomVector3(playgroundParticles.internalRandom01, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness;

                                // Apply lifetime loss
                                if (playgroundParticles.lifetimeLoss>0) {
                                    playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss);
                                    playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true;
                                }

                                // Add force to rigidbody
                                if (playgroundParticles.affectRigidbodies && hitInfo.rigidbody)
                                    hitInfo.rigidbody.AddForceAtPosition((playgroundParticles.inverseRigidbodyCollision?-preCollisionVelocity:preCollisionVelocity)*playgroundParticles.mass, playgroundParticles.playgroundCache.position[p]);

                                // Send event
                                if (hasEvents)
                                    playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, hitInfo.transform, hitInfo.collider);
                                if (playgroundParticles.hasEventManipulatorLocal) {
                                    for (int i = 0; i<playgroundParticles.manipulators.Count; i++) {
                                        if (playgroundParticles.manipulators[i].trackParticles &&
                                            playgroundParticles.manipulators[i].sendEventCollision &&
                                            playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], playgroundParticles.manipulators[i].transform.position)) {

                                            playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p);
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider = hitInfo.collider;
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo.point;
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo.transform;
                                            playgroundParticles.manipulators[i].SendParticleEventCollision();
                                        }
                                    }
                                }
                                if (playgroundParticles.hasEventManipulatorGlobal) {
                                    for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) {
                                        if (PlaygroundC.reference.manipulators[i].trackParticles &&
                                            PlaygroundC.reference.manipulators[i].sendEventCollision &&
                                            PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], PlaygroundC.reference.manipulators[i].transform.position)) {

                                            playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p);
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider = hitInfo.collider;
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo.point;
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo.transform;
                                            PlaygroundC.reference.manipulators[i].SendParticleEventCollision();
                                        }
                                    }
                                }
                            }
                        } else {
                            hitInfo2D = Physics2D.Raycast(
                                playgroundParticles.playgroundCache.collisionParticlePosition[p],
                                (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p]).normalized,
                                (Vector3.SqrMagnitude(playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p])+playgroundParticles.collisionRadius)*2f,
                                playgroundParticles.collisionMask,
                                playgroundParticles.minCollisionDepth,
                                playgroundParticles.maxCollisionDepth
                            );
                            if (hitInfo2D.collider!=null) {

                                // Check that this object isn't excluded
                                if (hasCollisionExclusion) {
                                    if (playgroundParticles.collisionExclusion.Contains(hitInfo2D.transform))
                                        continue;
                                }

                                // Set particle to location
                                playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.collisionParticlePosition[p];

                                // Store velocity before collision
                                preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p];

                                // Reflect particle
                                playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], (Vector3)hitInfo2D.normal+RandomVector3(playgroundParticles.internalRandom01, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness;

                                // Apply lifetime loss
                                if (playgroundParticles.lifetimeLoss>0) {
                                    playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss);
                                    playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true;
                                }

                                // Add force to rigidbody
                                if (playgroundParticles.affectRigidbodies && hitInfo2D.rigidbody)
                                    hitInfo2D.rigidbody.AddForceAtPosition((playgroundParticles.inverseRigidbodyCollision?-preCollisionVelocity:preCollisionVelocity)*playgroundParticles.mass, playgroundParticles.playgroundCache.position[p]);

                                // Send event
                                if (hasEvents)
                                    playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, hitInfo2D.transform, hitInfo2D.collider);
                                if (playgroundParticles.hasEventManipulatorLocal) {
                                    for (int i = 0; i<playgroundParticles.manipulators.Count; i++) {
                                        if (playgroundParticles.manipulators[i].trackParticles &&
                                            playgroundParticles.manipulators[i].sendEventCollision &&
                                            playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], playgroundParticles.manipulators[i].transform.position)) {

                                            playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p);
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider2D = hitInfo2D.collider;
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo2D.point;
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo2D.transform;
                                            playgroundParticles.manipulators[i].SendParticleEventCollision();
                                        }
                                    }
                                }
                                if (playgroundParticles.hasEventManipulatorGlobal) {
                                    for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) {
                                        if (PlaygroundC.reference.manipulators[i].trackParticles &&
                                            PlaygroundC.reference.manipulators[i].sendEventCollision &&
                                            PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], PlaygroundC.reference.manipulators[i].transform.position)) {

                                            playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p);
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider2D = hitInfo2D.collider;
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo2D.point;
                                            playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo2D.transform;
                                            PlaygroundC.reference.manipulators[i].SendParticleEventCollision();
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        playgroundParticles.playgroundCache.velocity[p] = Vector3.zero;
                    }
                }
            }
        }
        // Rebirth of a specified particle
        public static void Rebirth(PlaygroundParticlesC playgroundParticles, int p, System.Random random)
        {
            if (!playgroundParticles.hasActiveParticles) return;

            // Set initial values
            playgroundParticles.playgroundCache.rebirth[p] = playgroundParticles.source==SOURCEC.Script?true:(playgroundParticles.emit && (playgroundParticles.loop || playgroundParticles.playgroundCache.isNonBirthed[p]) && playgroundParticles.playgroundCache.emission[p]);
            playgroundParticles.playgroundCache.isFirstLoop[p] = playgroundParticles.playgroundCache.isNonBirthed[p];
            playgroundParticles.playgroundCache.isNonBirthed[p] = false;
            playgroundParticles.playgroundCache.life[p] = 0f;
            playgroundParticles.playgroundCache.birth[p] = playgroundParticles.playgroundCache.death[p];
            playgroundParticles.playgroundCache.death[p] += playgroundParticles.lifetime;
            playgroundParticles.playgroundCache.velocity[p] = Vector3.zero;
            playgroundParticles.playgroundCache.noForce[p] = false;

            // Reset manipulators influence
            playgroundParticles.playgroundCache.changedByProperty[p] = false;
            playgroundParticles.playgroundCache.changedByPropertyColor[p] = false;
            playgroundParticles.playgroundCache.changedByPropertyColorLerp[p] = false;
            playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p] = false;
            playgroundParticles.playgroundCache.changedByPropertySize[p] = false;
            playgroundParticles.playgroundCache.changedByPropertyTarget[p] = false;
            playgroundParticles.playgroundCache.changedByPropertyDeath[p] = false;
            playgroundParticles.playgroundCache.propertyTarget[p] = 0;
            playgroundParticles.playgroundCache.propertyId[p] = 0;
            playgroundParticles.playgroundCache.propertyColorId[p] = 0;
            playgroundParticles.playgroundCache.manipulatorId[p] = 0;

            // Set new random size
            if (playgroundParticles.applyRandomSizeOnRebirth)
                playgroundParticles.playgroundCache.initialSize[p] = RandomRange(random, playgroundParticles.sizeMin, playgroundParticles.sizeMax);

            // Initial velocity
            if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning) {

                // Initial global velocity
                if (playgroundParticles.applyInitialVelocity) {
                    if (playgroundParticles.applyRandomInitialVelocityOnRebirth) {
                        if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.Spherical)
                            playgroundParticles.playgroundCache.initialVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialVelocityMin.x, playgroundParticles.initialVelocityMax.x);
                        else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.SphericalLinear)
                            playgroundParticles.playgroundCache.initialVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialVelocityMin.x, playgroundParticles.initialVelocityMax.x, (p*1f)/(playgroundParticles.particleCount*1f));
                        else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.RectangularLinear)
                            playgroundParticles.playgroundCache.initialVelocity[p] = Vector3.Lerp (playgroundParticles.initialVelocityMin, playgroundParticles.initialVelocityMax, (p*1f)/(playgroundParticles.particleCount*1f));
                        //else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.SphericalSector)
                        //	playgroundParticles.playgroundCache.initialVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialVelocityMin.x, playgroundParticles.initialVelocityMax.x, playgroundParticles.initialVelocityMin.y, playgroundParticles.initialVelocityMax.y);
                        //else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.SphericalSectorLinear)
                        //	playgroundParticles.playgroundCache.initialVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialVelocityMin.x, playgroundParticles.initialVelocityMax.x, playgroundParticles.initialVelocityMin.y, playgroundParticles.initialVelocityMax.y, (p*1f)/(playgroundParticles.particleCount*1f));
                        else playgroundParticles.playgroundCache.initialVelocity[p] = RandomRange(random, playgroundParticles.initialVelocityMin, playgroundParticles.initialVelocityMax);
                    }
                    playgroundParticles.playgroundCache.velocity[p] = playgroundParticles.playgroundCache.initialVelocity[p];

                    // Give this spawning particle its velocity shape
                    if (playgroundParticles.applyInitialVelocityShape)
                        playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f), playgroundParticles.initialVelocityShapeScale));
                }

                // Initial local velocity
                if (playgroundParticles.applyInitialLocalVelocity && playgroundParticles.source!=SOURCEC.Script) {
                    if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.Spherical)
                        playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialLocalVelocityMin.x, playgroundParticles.initialLocalVelocityMax.x);
                    else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.SphericalLinear)
                        playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialLocalVelocityMin.x, playgroundParticles.initialLocalVelocityMax.x, (p*1f)/(playgroundParticles.particleCount*1f));
                    else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.RectangularLinear)
                        playgroundParticles.playgroundCache.initialLocalVelocity[p] = Vector3.Lerp (playgroundParticles.initialLocalVelocityMin, playgroundParticles.initialLocalVelocityMax, (p*1f)/(playgroundParticles.particleCount*1f));
                    //else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.SphericalSector)
                    //	playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialLocalVelocityMin.x, playgroundParticles.initialLocalVelocityMax.x, playgroundParticles.initialLocalVelocityMin.y, playgroundParticles.initialLocalVelocityMax.y);
                    //else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.SphericalSectorLinear)
                    //	playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialLocalVelocityMin.x, playgroundParticles.initialLocalVelocityMax.x, playgroundParticles.initialLocalVelocityMin.y, playgroundParticles.initialLocalVelocityMax.y, (p*1f)/(playgroundParticles.particleCount*1f));
                    else playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRange(random, playgroundParticles.initialLocalVelocityMin, playgroundParticles.initialLocalVelocityMax);
                    playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.playgroundCache.targetDirection[p];

                    // Give this spawning particle its local velocity shape
                    if (playgroundParticles.applyInitialVelocityShape)
                        playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f), playgroundParticles.initialVelocityShapeScale));
                }

                // Initial stretch
                playgroundParticles.particleCache[p].velocity = playgroundParticles.renderModeStretch&&playgroundParticles.applyStretchStartDirection?playgroundParticles.stretchStartDirection:Vector3.zero;
            }
            if (playgroundParticles.source==SOURCEC.Script) {
                // Velocity for script mode
                if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning)
                    playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.scriptedEmissionVelocity;
                playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.scriptedEmissionPosition;
                playgroundParticles.particleCache[p].velocity = playgroundParticles.renderModeStretch&&playgroundParticles.applyStretchStartDirection?playgroundParticles.stretchStartDirection:Vector3.zero;
            }

            // Set new random lifetime
            if (playgroundParticles.scriptedLifetime==0) {
                if (playgroundParticles.applyRandomLifetimeOnRebirth) {
                    if (playgroundParticles.lifetimeValueMethod==VALUEMETHOD.Constant) {
                        playgroundParticles.playgroundCache.lifetimeSubtraction[p] = 0;
                    } else {
                        playgroundParticles.playgroundCache.lifetimeSubtraction[p] = playgroundParticles.lifetime-RandomRange (random, playgroundParticles.lifetimeMin, playgroundParticles.lifetime);
                    }
                }

                // Set shuriken particles lifetime
                if (!playgroundParticles.syncPositionsOnMainThread)
                    playgroundParticles.particleCache[p].lifetime = playgroundParticles.lifetime;
                playgroundParticles.particleCache[p].startLifetime = playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p];
            } else {

                playgroundParticles.playgroundCache.lifetimeSubtraction[p] = 0;
                if (!playgroundParticles.syncPositionsOnMainThread)
                    playgroundParticles.particleCache[p].lifetime = playgroundParticles.scriptedLifetime;
                playgroundParticles.particleCache[p].startLifetime = playgroundParticles.scriptedLifetime;
            }

            if (playgroundParticles.playgroundCache.rebirth[p]) {

                playgroundParticles.particleCache[p].color = playgroundParticles.GetParticleColor(p, 0f, (p*1f)/(playgroundParticles.particleCount*1f));

                // Source Scattering
                if (playgroundParticles.applySourceScatter && playgroundParticles.source!=SOURCEC.Script) {
                    if (playgroundParticles.playgroundCache.scatterPosition[p]==Vector3.zero || playgroundParticles.applyRandomScatterOnRebirth) {
                        if (playgroundParticles.sourceScatterMethod==MINMAXVECTOR3METHOD.Rectangular)
                            playgroundParticles.playgroundCache.scatterPosition[p] = RandomRange(random, playgroundParticles.sourceScatterMin, playgroundParticles.sourceScatterMax);
                        else if (playgroundParticles.sourceScatterMethod==MINMAXVECTOR3METHOD.RectangularLinear)
                            playgroundParticles.playgroundCache.scatterPosition[p] = Vector3.Lerp (playgroundParticles.sourceScatterMin, playgroundParticles.sourceScatterMax, (p*1f)/(playgroundParticles.particleCount*1f));
                        else if (playgroundParticles.sourceScatterMethod==MINMAXVECTOR3METHOD.SphericalLinear)
                            playgroundParticles.playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, playgroundParticles.sourceScatterMin.x, playgroundParticles.sourceScatterMax.x, (p*1f)/(playgroundParticles.particleCount*1f));
                        //else if (playgroundParticles.sourceScatterMethod==MINMAXVECTOR3METHOD.SphericalSector)
                        //	playgroundParticles.playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, playgroundParticles.sourceScatterMin.x, playgroundParticles.sourceScatterMax.x, playgroundParticles.sourceScatterMin.y, playgroundParticles.sourceScatterMax.y);
                        //else if (playgroundParticles.sourceScatterMethod==MINMAXVECTOR3METHOD.SphericalSectorLinear)
                        //	playgroundParticles.playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, playgroundParticles.sourceScatterMin.x, playgroundParticles.sourceScatterMax.x, playgroundParticles.sourceScatterMin.y, playgroundParticles.sourceScatterMax.y, (p*1f)/(playgroundParticles.particleCount*1f));
                        else playgroundParticles.playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, playgroundParticles.sourceScatterMin.x, playgroundParticles.sourceScatterMax.x);
                    }
                } else playgroundParticles.playgroundCache.scatterPosition[p] = Vector3.zero;

                if (!playgroundParticles.onlyLifetimePositioning) {
                    playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p];
                    if (!playgroundParticles.syncPositionsOnMainThread)
                        playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.targetPosition[p];
                    playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                    playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                } else if (!playgroundParticles.onlySourcePositioning) {

                    // Lifetime Positioning by Vector3 Animation Curve
                    if (playgroundParticles.lifetimePositioningUsesSourceDirection && playgroundParticles.source!=SOURCEC.Script) {
                        playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+(playgroundParticles.playgroundCache.targetDirection[p]);
                    } else {
                        if (!playgroundParticles.applyLifetimePositioningPositionScale) {
                            playgroundParticles.playgroundCache.position[p] =
                                playgroundParticles.playgroundCache.targetPosition[p]+
                                    playgroundParticles.lifetimePositioning.Evaluate(0, playgroundParticles.lifetimePositioningScale);
                        } else {
                            playgroundParticles.playgroundCache.position[p] =
                                playgroundParticles.playgroundCache.targetPosition[p]+
                                    playgroundParticles.lifetimePositioning.Evaluate(0, playgroundParticles.lifetimePositioningScale)*
                                    playgroundParticles.lifetimePositioningPositionScale.Evaluate(0);
                        }
                    }
                    if (!playgroundParticles.syncPositionsOnMainThread)
                        playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.targetPosition[p];
                    playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                    playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                }

                if (playgroundParticles.applyInitialColorOnRebirth) {
                    playgroundParticles.particleCache[p].color = playgroundParticles.playgroundCache.initialColor[p];
                    playgroundParticles.playgroundCache.color[p] = playgroundParticles.playgroundCache.initialColor[p];
                }
            } else {
                playgroundParticles.particleCache[p].position = PlaygroundC.initialTargetPosition;
            }

            // Set new random rotation
            if (playgroundParticles.applyRandomRotationOnRebirth && !playgroundParticles.rotateTowardsDirection)
                playgroundParticles.playgroundCache.initialRotation[p] = RandomRange(random, playgroundParticles.initialRotationMin, playgroundParticles.initialRotationMax);

            if (!playgroundParticles.rotateTowardsDirection)
                playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p];
            else {
                Vector3 particleDir;
                if (!playgroundParticles.onlySourcePositioning&&playgroundParticles.onlyLifetimePositioning)
                    particleDir = (playgroundParticles.playgroundCache.position[p]+playgroundParticles.lifetimePositioning.Evaluate(.01f, playgroundParticles.lifetimePositioningScale))-playgroundParticles.playgroundCache.position[p];
                else
                    particleDir = playgroundParticles.playgroundCache.velocity[p];
                playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]+SignedAngle(
                    Vector3.up,
                    particleDir,
                    playgroundParticles.rotationNormal
                );
            }

            if (!playgroundParticles.syncPositionsOnMainThread)
                playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p];

            // Set size
            if (playgroundParticles.applyLifetimeSize && !playgroundParticles.applyParticleArraySize)
                playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(0)*playgroundParticles.scale;
            else if (playgroundParticles.applyLifetimeSize && playgroundParticles.applyParticleArraySize)
                playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(0)*playgroundParticles.particleArraySize.Evaluate((p*1f)/(playgroundParticles.particleCount*1f))*playgroundParticles.scale;
            else if (playgroundParticles.applyParticleArraySize)
                playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.particleArraySize.Evaluate((p*1f)/(playgroundParticles.particleCount*1f))*playgroundParticles.scale;
            else playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.scale;
            if (!playgroundParticles.syncPositionsOnMainThread)
                playgroundParticles.particleCache[p].size = playgroundParticles.playgroundCache.maskAlpha[p]>0?playgroundParticles.playgroundCache.size[p]:0;

            // Set color gradient id
            if (playgroundParticles.colorSource==COLORSOURCEC.LifetimeColors && playgroundParticles.lifetimeColors.Count>0) {
                playgroundParticles.lifetimeColorId++;playgroundParticles.lifetimeColorId=playgroundParticles.lifetimeColorId%playgroundParticles.lifetimeColors.Count;
                playgroundParticles.playgroundCache.lifetimeColorId[p] = playgroundParticles.lifetimeColorId;
            }

            // Local Manipulators
            if (playgroundParticles.calculateManipulatorOnRebirth) {
                for (int m = 0; m<playgroundParticles.manipulators.Count; m++) {
                    if (playgroundParticles.manipulators[m].transform!=null) {
                        CalculateManipulator(playgroundParticles, playgroundParticles.manipulators[m], p, playgroundParticles.t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], (playgroundParticles.localSpace?playgroundParticles.manipulators[m].transform.localPosition:playgroundParticles.manipulators[m].transform.position)+playgroundParticles.manipulatorFix, playgroundParticles.localSpace);
                    }
                }
            }

            // Send birth event
            if (playgroundParticles.events.Count>0 && playgroundParticles.playgroundCache.rebirth[p])
                playgroundParticles.SendEvent(EVENTTYPEC.Birth, p);
            if (playgroundParticles.hasEventManipulatorLocal) {
                for (int i = 0; i<playgroundParticles.manipulators.Count; i++) {
                    if (playgroundParticles.manipulators[i].trackParticles &&
                        playgroundParticles.manipulators[i].sendEventBirth &&
                        playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.targetPosition[p], playgroundParticles.manipulators[i].transform.position)) {

                        playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p);
                        playgroundParticles.manipulators[i].SendParticleEventBirth();
                    }
                }
            }
            if (playgroundParticles.hasEventManipulatorGlobal) {
                for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) {
                    if (PlaygroundC.reference.manipulators[i].trackParticles &&
                        PlaygroundC.reference.manipulators[i].sendEventBirth &&
                        PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.targetPosition[p], PlaygroundC.reference.manipulators[i].transform.position)) {

                        playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p);
                        PlaygroundC.reference.manipulators[i].SendParticleEventBirth();
                    }
                }
            }
        }
		// Rebirth of a specified particle
		public static void Rebirth (PlaygroundParticlesC playgroundParticles, int p, System.Random random) {

			// Set initial values
			playgroundParticles.playgroundCache.birthDelay[p] = 0f;
			playgroundParticles.playgroundCache.life[p] = 0f;
			playgroundParticles.playgroundCache.birth[p] = playgroundParticles.playgroundCache.death[p]; 
			playgroundParticles.playgroundCache.death[p] += playgroundParticles.lifetime;
			playgroundParticles.playgroundCache.rebirth[p] = playgroundParticles.source==SOURCEC.Script?true:(playgroundParticles.emit && playgroundParticles.playgroundCache.emission[p]);
			playgroundParticles.playgroundCache.velocity[p] = Vector3.zero;

			// Set new random size
			if (playgroundParticles.applyRandomSizeOnRebirth)
				playgroundParticles.playgroundCache.initialSize[p] = RandomRange(random, playgroundParticles.sizeMin, playgroundParticles.sizeMax);

			// Initial velocity
			if (!playgroundParticles.onlySourcePositioning) {
						
				// Initial global velocity
				if (playgroundParticles.applyInitialVelocity) {
					if (playgroundParticles.applyRandomInitialVelocityOnRebirth)
						playgroundParticles.playgroundCache.initialVelocity[p] = RandomRange(random, playgroundParticles.initialVelocityMin, playgroundParticles.initialVelocityMax);
					playgroundParticles.playgroundCache.velocity[p] = playgroundParticles.playgroundCache.initialVelocity[p];
					
					// Give this spawning particle its velocity shape
					if (playgroundParticles.applyInitialVelocityShape)
						playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f)));
				}

				// Initial local velocity
				if (playgroundParticles.applyInitialLocalVelocity && playgroundParticles.source!=SOURCEC.Script) {
					playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRange(random, playgroundParticles.initialLocalVelocityMin, playgroundParticles.initialLocalVelocityMax);
					playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.playgroundCache.targetDirection[p];

					// Give this spawning particle its local velocity shape
					if (playgroundParticles.applyInitialVelocityShape)
						playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f)));
				}

				// Initial stretch
				if (playgroundParticles.renderModeStretch) {
					if (playgroundParticles.playgroundCache.velocity[p]!=Vector3.zero)
						playgroundParticles.particleCache[p].velocity = playgroundParticles.playgroundCache.velocity[p];
					else 
						playgroundParticles.particleCache[p].velocity = playgroundParticles.stretchStartDirection;
				} else
					playgroundParticles.particleCache[p].velocity = Vector3.zero;
			}
			if (playgroundParticles.source==SOURCEC.Script) {
				// Velocity for script mode
				if (!playgroundParticles.onlySourcePositioning)
					playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.scriptedEmissionVelocity;
				playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.scriptedEmissionPosition;
			}
			
			if (playgroundParticles.playgroundCache.rebirth[p]) {

				// Set new random rotation
				if (playgroundParticles.playgroundCache.initialRotation.Length!=playgroundParticles.particleCount) return;
				if (playgroundParticles.applyRandomRotationOnRebirth)
					playgroundParticles.playgroundCache.initialRotation[p] = RandomRange(random, playgroundParticles.initialRotationMin, playgroundParticles.initialRotationMax);
				playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p];
				playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p];
				
				// Source Scattering
				if (playgroundParticles.applySourceScatter && playgroundParticles.source!=SOURCEC.Script) {
					if (playgroundParticles.playgroundCache.scatterPosition[p]==Vector3.zero || playgroundParticles.applyRandomScatterOnRebirth)
						playgroundParticles.playgroundCache.scatterPosition[p] = RandomRange(random, playgroundParticles.sourceScatterMin, playgroundParticles.sourceScatterMax);
				} else playgroundParticles.playgroundCache.scatterPosition[p] = Vector3.zero;

				playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p];
				playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.targetPosition[p];
				playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
				playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p];

				if (playgroundParticles.applyInitialColorOnRebirth) {
					playgroundParticles.particleCache[p].color = playgroundParticles.playgroundCache.initialColor[p];
					playgroundParticles.playgroundCache.color[p] = playgroundParticles.playgroundCache.initialColor[p];
				}
			} else playgroundParticles.particleCache[p].position = PlaygroundC.initialTargetPosition;

			// Set color gradient id
			if (playgroundParticles.colorSource==COLORSOURCEC.LifetimeColors && playgroundParticles.lifetimeColors.Count>0) {
				playgroundParticles.lifetimeColorId++;playgroundParticles.lifetimeColorId=playgroundParticles.lifetimeColorId%playgroundParticles.lifetimeColors.Count;
				playgroundParticles.playgroundCache.lifetimeColorId[p] = playgroundParticles.lifetimeColorId;
			}

			// Reset manipulators influence
			playgroundParticles.playgroundCache.changedByProperty[p] = false;
			playgroundParticles.playgroundCache.changedByPropertyColor[p] = false;
			playgroundParticles.playgroundCache.changedByPropertyColorLerp[p] = false;
			playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p] = false;
			playgroundParticles.playgroundCache.changedByPropertySize[p] = false;
			playgroundParticles.playgroundCache.changedByPropertyTarget[p] = false;
			playgroundParticles.playgroundCache.changedByPropertyDeath[p] = false;
			playgroundParticles.playgroundCache.propertyTarget[p] = 0;
			playgroundParticles.playgroundCache.propertyId[p] = 0;
			playgroundParticles.playgroundCache.propertyColorId[p] = 0;

			// Send birth event
			if (playgroundParticles.events.Count>0)
				playgroundParticles.SendEvent(EVENTTYPEC.Birth, p);
		}
		public static void Collisions (PlaygroundParticlesC playgroundParticles) {

			// Particle collisions (must currently run on main-thread due to the Physics.Raycast dependency)
			if (!playgroundParticles.onlySourcePositioning && playgroundParticles.collisionRadius>0) {
				Ray ray = new Ray();
				float distance;
				bool is3d = playgroundParticles.collisionType==COLLISIONTYPEC.Physics3D;
				RaycastHit hitInfo;
				RaycastHit2D hitInfo2D;
				bool hasEvents = playgroundParticles.events.Count>0;
				Vector3 preCollisionVelocity;

				// Prepare the infinite collision planes
				if (playgroundParticles.collision && playgroundParticles.collisionRadius>0 && playgroundParticles.colliders.Count>0) {
					for (int c = 0; c<playgroundParticles.colliders.Count; c++) {
						playgroundParticles.colliders[c].UpdatePlane();
					}
				}

				for (int p = 0; p<playgroundParticles.particleCount; p++) {

					if (playgroundParticles.playgroundCache.life[p]==0 || playgroundParticles.playgroundCache.life[p]>=playgroundParticles.lifetime) continue;

					// Playground Plane colliders (never exceed these)
					for (int c = 0; c<playgroundParticles.colliders.Count; c++) {
						if (playgroundParticles.colliders[c].enabled && playgroundParticles.colliders[c].transform && !playgroundParticles.colliders[c].plane.GetSide(playgroundParticles.playgroundCache.position[p])) {
							
							// Set particle to location
							ray.origin = playgroundParticles.playgroundCache.position[p];
							ray.direction = playgroundParticles.colliders[c].plane.normal;
							if (playgroundParticles.colliders[c].plane.Raycast(ray, out distance))
								playgroundParticles.playgroundCache.position[p] = ray.GetPoint(distance);

							// Store velocity before collision
							preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p];

							// Reflect particle
							playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.colliders[c].plane.normal+RandomVector3(playgroundParticles.internalRandom03, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness;
							
							// Apply lifetime loss
							if (playgroundParticles.lifetimeLoss>0) {
								playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss);
								playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true;
							}

							// Send event
							if (hasEvents)
								playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, playgroundParticles.colliders[c].transform);
						}
					}
					
					// Colliders in scene
					if (playgroundParticles.playgroundCache.velocity[p].magnitude>PlaygroundC.collisionSleepVelocity) {

						// Collide by checking for potential passed collider in the direction of this particle's velocity from the previous frame
						if (is3d) {
							if (Physics.Raycast(
								playgroundParticles.playgroundCache.collisionParticlePosition[p], 
								(playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p]).normalized, 
								out hitInfo, 
								Vector3.Distance(playgroundParticles.playgroundCache.collisionParticlePosition[p], playgroundParticles.playgroundCache.position[p])+playgroundParticles.collisionRadius, 
								playgroundParticles.collisionMask)) 
							{
								
								// Set particle to location
								playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.collisionParticlePosition[p];

								// Store velocity before collision
								preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p];

								// Reflect particle
								playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], hitInfo.normal+RandomVector3(playgroundParticles.internalRandom03, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness;
								
								// Apply lifetime loss
								if (playgroundParticles.lifetimeLoss>0) {
									playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss);
									playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true;
								}
								
								// Add force to rigidbody
								if (playgroundParticles.affectRigidbodies && hitInfo.rigidbody)
									hitInfo.rigidbody.AddForceAtPosition(playgroundParticles.playgroundCache.velocity[p]*playgroundParticles.mass, playgroundParticles.playgroundCache.position[p]);

								// Send event
								if (hasEvents)
									playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, hitInfo.transform, hitInfo.collider);
							}
						} else {
							hitInfo2D = Physics2D.Raycast(
								playgroundParticles.playgroundCache.collisionParticlePosition[p], 
								(playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p]).normalized, 
								Vector3.Distance(playgroundParticles.playgroundCache.collisionParticlePosition[p], playgroundParticles.playgroundCache.position[p])+playgroundParticles.collisionRadius, 
								playgroundParticles.collisionMask,
								playgroundParticles.minCollisionDepth,
								playgroundParticles.maxCollisionDepth
							);
							if (hitInfo2D.collider!=null) {
								
								// Set particle to location
								playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.collisionParticlePosition[p];

								// Store velocity before collision
								preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p];

								// Reflect particle
								playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], (Vector3)hitInfo2D.normal+RandomVector3(playgroundParticles.internalRandom03, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness;
								
								// Apply lifetime loss
								if (playgroundParticles.lifetimeLoss>0) {
									playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss);
									playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true;
								}
								
								// Add force to rigidbody
								if (playgroundParticles.affectRigidbodies && hitInfo2D.rigidbody)
									hitInfo2D.rigidbody.AddForceAtPosition(playgroundParticles.playgroundCache.velocity[p]*playgroundParticles.mass, playgroundParticles.playgroundCache.position[p]);

								// Send event
								if (hasEvents)
									playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, hitInfo2D.transform, hitInfo2D.collider);
							}
						}
					} else {
						playgroundParticles.playgroundCache.velocity[p] = Vector3.zero;
					}
				}
			}
		}
		public static void ThreadedCalculations (PlaygroundParticlesC playgroundParticles) {

			// Component disabled?
			if (!playgroundParticles.enabled || !playgroundParticles.isDoneThread) return;

			// Has the calculation been paused previously?
			if (playgroundParticles.cameFromNonCalculatedFrame) {
				playgroundParticles.Start();
				playgroundParticles.cameFromNonCalculatedFrame = false;
				return;
			}
		
			// Apply locked position
			if (playgroundParticles.applyLockPosition) {
				if (playgroundParticles.lockPositionIsLocal)
					playgroundParticles.particleSystemTransform.localPosition = playgroundParticles.lockPosition;
				else
					playgroundParticles.particleSystemTransform.position = playgroundParticles.lockPosition;
			}

			// Apply locked rotation
			if (playgroundParticles.applyLockRotation) {
				if (playgroundParticles.lockRotationIsLocal)
					playgroundParticles.particleSystemTransform.localRotation = Quaternion.Euler(playgroundParticles.lockRotation);
				else
					playgroundParticles.particleSystemTransform.rotation = Quaternion.Euler(playgroundParticles.lockRotation);
			}

			// Apply locked scale
			if (playgroundParticles.applyLockScale)
				playgroundParticles.particleSystemTransform.localScale = playgroundParticles.lockScale;
				

			// Set delta time
			playgroundParticles.localDeltaTime = PlaygroundC.globalTime-playgroundParticles.lastTimeUpdated;
			playgroundParticles.lastTimeUpdated = PlaygroundC.globalTime;
			float t = (playgroundParticles.localDeltaTime*playgroundParticles.particleTimescale);

			// Prepare Source positions
			Vector3 	stPos = Vector3.zero;
			Quaternion 	stRot = Quaternion.identity;
			Vector3 	stSca = new Vector3(1f, 1f, 1f);
			Vector3		stDir = new Vector3();
			bool 		localSpace = (playgroundParticles.shurikenParticleSystem.simulationSpace == ParticleSystemSimulationSpace.Local);
			bool 		overflow = (playgroundParticles.overflowOffset!=Vector3.zero);
			bool 		skinnedWorldObjectReady = false;
			playgroundParticles.renderModeStretch = playgroundParticles.particleSystemRenderer2.renderMode==ParticleSystemRenderMode.Stretch;
			
			if (playgroundParticles.emit) {
				switch (playgroundParticles.source) {
				case SOURCEC.Script:

				break;
				case SOURCEC.State:
					if (playgroundParticles.states.Count>0) {
						if (playgroundParticles.states[playgroundParticles.activeState].stateTransform!=null) {

							if (!playgroundParticles.states[playgroundParticles.activeState].initialized)
								playgroundParticles.states[playgroundParticles.activeState].Initialize ();

							stPos = playgroundParticles.states[playgroundParticles.activeState].stateTransform.position;
							stRot = playgroundParticles.states[playgroundParticles.activeState].stateTransform.rotation;
							stSca = playgroundParticles.states[playgroundParticles.activeState].stateTransform.localScale;
							if (localSpace && (playgroundParticles.states[playgroundParticles.activeState].stateTransform.parent==playgroundParticles.particleSystemTransform || playgroundParticles.states[playgroundParticles.activeState].stateTransform==playgroundParticles.particleSystemTransform)) {
								stPos = Vector3.zero;
								stRot = Quaternion.Euler (Vector3.zero);
							}
						}
					} else return;
					break;
				case SOURCEC.Transform:
					stPos = playgroundParticles.sourceTransform.position;
					stRot = playgroundParticles.sourceTransform.rotation;
					stSca = playgroundParticles.sourceTransform.localScale;
					if (localSpace && playgroundParticles.sourceTransform==playgroundParticles.particleSystemTransform) {
						stPos = Vector3.zero;
						stRot = Quaternion.Euler (Vector3.zero);
					}
					break;
				case SOURCEC.WorldObject:
					
					// Handle vertex data in active World Object
					if (playgroundParticles.worldObject.gameObject!=null) {
						if (playgroundParticles.worldObject.gameObject.GetInstanceID()!=playgroundParticles.worldObject.cachedId) 
							playgroundParticles.worldObject = NewWorldObject(playgroundParticles.worldObject.gameObject.transform);
						if (playgroundParticles.worldObject.mesh!=null) {
							stPos = playgroundParticles.worldObject.transform.position;
							stRot = playgroundParticles.worldObject.transform.rotation;
							stSca = playgroundParticles.worldObject.transform.localScale;
							if (localSpace) {
								stPos = Vector3.zero;
								stRot = Quaternion.Euler (Vector3.zero);
							}
							
							playgroundParticles.worldObject.updateNormals = playgroundParticles.worldObjectUpdateNormals;
							if (playgroundParticles.worldObjectUpdateVertices)
								playgroundParticles.worldObject.Update ();
						} else return;
					} else return;
					break;
				case SOURCEC.SkinnedWorldObject:

					// Handle vertex data in active Skinned World Object
					if (playgroundParticles.skinnedWorldObject.gameObject!=null) {
						if (playgroundParticles.skinnedWorldObject.gameObject.GetInstanceID()!=playgroundParticles.skinnedWorldObject.cachedId)
							playgroundParticles.skinnedWorldObject = NewSkinnedWorldObject(playgroundParticles.skinnedWorldObject.gameObject.transform, playgroundParticles.skinnedWorldObject.downResolution);
					}
					skinnedWorldObjectReady = playgroundParticles.skinnedWorldObject.gameObject!=null && playgroundParticles.skinnedWorldObject.mesh!=null;
					if (skinnedWorldObjectReady) {
						
						stPos = playgroundParticles.skinnedWorldObject.transform.position;
						stRot = playgroundParticles.skinnedWorldObject.transform.rotation;
						stSca = playgroundParticles.skinnedWorldObject.transform.localScale;
						stDir = playgroundParticles.skinnedWorldObject.transform.TransformDirection (playgroundParticles.overflowOffset);
						
						playgroundParticles.skinnedWorldObject.updateNormals = playgroundParticles.worldObjectUpdateNormals;

						if (Time.frameCount%PlaygroundC.skinnedUpdateRate==0) {
							if (playgroundParticles.worldObjectUpdateVertices)
								playgroundParticles.skinnedWorldObject.MeshUpdate();
							playgroundParticles.skinnedWorldObject.BoneUpdate();
						}
					} else return;
				break;
				case SOURCEC.Paint:
					if (playgroundParticles.paint.initialized) {
						stPos = playgroundParticles.particleSystemTransform.position;
						stRot = playgroundParticles.particleSystemTransform.rotation;
						stSca = playgroundParticles.particleSystemTransform.localScale;

						if (playgroundParticles.paint.positionLength>0) {
							for (int p = 0; p<playgroundParticles.particleCache.Length; p++) {
								playgroundParticles.paint.Update(p);
							}
						} else return;
					} else {
						playgroundParticles.paint.Initialize ();
						return;
					}
				break;
				case SOURCEC.Projection:
					if (playgroundParticles.projection.projectionTexture!=null && playgroundParticles.projection.projectionTransform!=null) {
						if (!playgroundParticles.projection.initialized)
							playgroundParticles.projection.Initialize();

						stPos = playgroundParticles.projection.projectionTransform.position;
						stRot = playgroundParticles.projection.projectionTransform.rotation;
						stSca = playgroundParticles.projection.projectionTransform.localScale;
						
						if (localSpace)
							playgroundParticles.shurikenParticleSystem.simulationSpace = ParticleSystemSimulationSpace.World;
						
						if (playgroundParticles.projection.liveUpdate || !playgroundParticles.projection.hasRefreshed) {
							playgroundParticles.projection.UpdateSource();
							playgroundParticles.projection.Update();
							stDir = playgroundParticles.projection.projectionTransform.TransformDirection (playgroundParticles.overflowOffset);

							playgroundParticles.projection.hasRefreshed = true;
						}
						
					} else return;
				break;
				}
			}

			// Collision detection (runs on main-thread)
			if (playgroundParticles.collision)
				Collisions(playgroundParticles);

			// Sync positions to main-thread
			if (playgroundParticles.syncPositionsOnMainThread) {
				for (int p = 0; p<playgroundParticles.particleCount; p++) {
					playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.position[p];
				}
			}

			// Main-thread preparations
			// Prepare Particle colors
			bool stateReadyForTextureColor = (playgroundParticles.source==SOURCEC.State && playgroundParticles.states[playgroundParticles.activeState].stateTexture!=null);

			// Prepare local manipulators
			for (int m = 0; m<playgroundParticles.manipulators.Count; m++) {
				playgroundParticles.manipulators[m].Update();
				playgroundParticles.manipulators[m].transform.SetLocalPosition(playgroundParticles.particleSystemTransform);
				playgroundParticles.manipulators[m].SetLocalTargetsPosition(playgroundParticles.particleSystemTransform);
				playgroundParticles.manipulators[m].willAffect = true;
			}
			// Prepare global manipulators from this local space
			for (int m = 0; m<PlaygroundC.reference.manipulators.Count; m++) {
				PlaygroundC.reference.manipulators[m].willAffect = ((PlaygroundC.reference.manipulators[m].affects.value & 1<<playgroundParticles.particleSystemGameObject.layer)!=0);
				PlaygroundC.reference.manipulators[m].transform.SetLocalPosition(playgroundParticles.particleSystemTransform);
			}

			// Prepare events
			bool hasEvent = playgroundParticles.events.Count>0;
			bool hasTimerEvent = false;
			if (hasEvent) {
				for (int i = 0; i<playgroundParticles.events.Count; i++) {
					playgroundParticles.events[i].Initialize();
					if (playgroundParticles.events[i].initializedTarget && playgroundParticles.events[i].broadcastType!=EVENTBROADCASTC.EventListeners)
						if (!playgroundParticles.events[i].target.eventControlledBy.Contains (playgroundParticles))
							playgroundParticles.events[i].target.eventControlledBy.Add (playgroundParticles);
					
					if (playgroundParticles.events[i].eventType==EVENTTYPEC.Time) {
						hasTimerEvent = playgroundParticles.events[i].UpdateTime();
					}
				}
			}

			// Calculate all thread-safe data on a new thread
			if (!playgroundParticles.isDoneThread) return;
			playgroundParticles.isDoneThread = false;
			PlaygroundC.RunAsync(()=>{
				if (playgroundParticles.isDoneThread) return;
				lock (playgroundParticles.locker) {

				// Prepare variables for particle source positions
				Matrix4x4 stMx = new Matrix4x4();
				Matrix4x4 fMx = new Matrix4x4();
				stMx.SetTRS(stPos, stRot, stSca);
				fMx.SetTRS(Vector3.zero, stRot, new Vector3(1f,1f,1f));
				int downResolution = playgroundParticles.skinnedWorldObject.downResolution;
				int downResolutionP;

				// Prepare variables for lifetime, velocity & manipulators
				int m = 0;
				float evaluatedLife;
				Vector3 deltaVelocity;
				Vector3 manipulatorPosition;
				float manipulatorDistance;
				Vector3 zero = Vector3.zero;
				Vector3 up = Vector3.up;

				// Prepare variables for colors
				Color lifetimeColor = new Color();
				bool hasLifetimeColors = (playgroundParticles.lifetimeColors.Count>0);

				// Update skinned mesh vertices
				if (skinnedWorldObjectReady)
					playgroundParticles.skinnedWorldObject.Update();
				
				int pCount = playgroundParticles.particleCache.Length;
				bool applyMask = false;

				// Check that cache is correct
				if (playgroundParticles.playgroundCache.maskAlpha.Length!=pCount) {
					playgroundParticles.playgroundCache.maskAlpha = new float[pCount];
					playgroundParticles.playgroundCache.isMasked = new bool[pCount];
				}

				// Calculation loop
				for (int p = 0; p<playgroundParticles.particleCache.Length; p++) {
					
					// Check that particle count is correct
					if (pCount != playgroundParticles.particleCache.Length) {
						playgroundParticles.cameFromNonEmissionFrame = false;
						playgroundParticles.isDoneThread = true;
						return;
					}

					// Apply particle mask
					if (p<playgroundParticles.particleMask) {
						if (playgroundParticles.playgroundCache.maskAlpha[p]<=0 || playgroundParticles.particleMaskTime<=0) {
							playgroundParticles.playgroundCache.isMasked[p] = true;
							playgroundParticles.playgroundCache.maskAlpha[p] = 0;
							playgroundParticles.particleCache[p].size = 0;
							applyMask = true;
						} else {
							applyMask = false;
							playgroundParticles.playgroundCache.maskAlpha[p] -= (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime;
						}
					} else {
						applyMask = false;
						if (playgroundParticles.playgroundCache.maskAlpha[p]<1f && playgroundParticles.particleMaskTime>0) {
							playgroundParticles.playgroundCache.maskAlpha[p] += (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime;
						} else {
							playgroundParticles.playgroundCache.maskAlpha[p] = 1f;
							playgroundParticles.playgroundCache.isMasked[p] = false;
						}
					}

					// Get this particle's color
					if (!playgroundParticles.playgroundCache.changedByPropertyColor[p] && !playgroundParticles.playgroundCache.changedByPropertyColorLerp[p]) {
						switch (playgroundParticles.colorSource) {
						case COLORSOURCEC.Source:
							switch (playgroundParticles.source) {
							case SOURCEC.Script:
								lifetimeColor = playgroundParticles.playgroundCache.scriptedColor[p];
								break;
							case SOURCEC.State:
								if (stateReadyForTextureColor)
									lifetimeColor = playgroundParticles.states[playgroundParticles.activeState].GetColor(p);
								else
									lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime));
								break;
							case SOURCEC.Projection:
								lifetimeColor = playgroundParticles.projection.GetColor(p);
								break;
							case SOURCEC.Paint:
								lifetimeColor = playgroundParticles.paint.GetColor(p);
								break;
							default:
								lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime));
								break;
							}
							if (playgroundParticles.sourceUsesLifetimeAlpha)
								lifetimeColor.a = Mathf.Clamp(playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)).a, 0, lifetimeColor.a);
							break;
						case COLORSOURCEC.LifetimeColor:
							lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime));
							break;
						case COLORSOURCEC.LifetimeColors:
							if (hasLifetimeColors)
								lifetimeColor = playgroundParticles.lifetimeColors[playgroundParticles.playgroundCache.lifetimeColorId[p]%playgroundParticles.lifetimeColors.Count].gradient.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime));
							else
								lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime));
							break;
						}
						
					} else if (playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p]) {
						lifetimeColor = playgroundParticles.particleCache[p].color;
						lifetimeColor.a = Mathf.Clamp(playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)).a, 0, lifetimeColor.a);
					}

					// Assign color to particle
					lifetimeColor*=playgroundParticles.playgroundCache.maskAlpha[p];
					playgroundParticles.particleCache[p].color = lifetimeColor;
					
					// Give Playground Cache its color value
					playgroundParticles.playgroundCache.color[p] = lifetimeColor;

					// Source positions
					if (playgroundParticles.emit) {
						switch (playgroundParticles.source) {
						case SOURCEC.State:
							if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) {
								playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
								if (playgroundParticles.applyInitialLocalVelocity)
									playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.states[playgroundParticles.activeState].GetNormal(p)));
								if (!overflow) {
									playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p))+playgroundParticles.playgroundCache.scatterPosition[p];
								} else {
									switch (playgroundParticles.overflowMode) {
									case OVERFLOWMODEC.SourceTransform:
										playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.states[playgroundParticles.activeState].positionLength))+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									case OVERFLOWMODEC.World:
										playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p))+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.states[playgroundParticles.activeState].positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									case OVERFLOWMODEC.SourcePoint:
										playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.states[playgroundParticles.activeState].GetNormal(p), p, playgroundParticles.states[playgroundParticles.activeState].positionLength))+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									}
								}
								
								// Local space compensation calculation
								if (localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
									playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
							}
						break;
						case SOURCEC.Transform:
							if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) {
								playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
								if (playgroundParticles.applyInitialLocalVelocity)
									playgroundParticles.playgroundCache.targetDirection[p] = stRot*playgroundParticles.playgroundCache.initialLocalVelocity[p];
								if (!overflow) {
									playgroundParticles.playgroundCache.targetPosition[p] = stPos+playgroundParticles.playgroundCache.scatterPosition[p];
								} else {
									switch (playgroundParticles.overflowMode) {
									case OVERFLOWMODEC.SourceTransform:
										playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(GetOverflow2(playgroundParticles.overflowOffset, p, 1))+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									case OVERFLOWMODEC.World:
										playgroundParticles.playgroundCache.targetPosition[p] = stPos+GetOverflow2(playgroundParticles.overflowOffset, p, 1)+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									case OVERFLOWMODEC.SourcePoint:
										playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(GetOverflow2(playgroundParticles.overflowOffset, p, 1))+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									}
								}
								
								// Local space compensation calculation
								if (localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
									playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
							}
						break;
						case SOURCEC.WorldObject:
							if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) {
								playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
								if (playgroundParticles.applyInitialLocalVelocity)
									playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.vertexPositions.Length]));
								if (!overflow) {
									playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(
										playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]
										)+playgroundParticles.playgroundCache.scatterPosition[p];
								} else {
									switch (playgroundParticles.overflowMode) {
									case OVERFLOWMODEC.SourceTransform:
										playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(
											playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.worldObject.vertexPositions.Length)
											)+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									case OVERFLOWMODEC.World:
										playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(
											playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]
											)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.worldObject.vertexPositions.Length)+
											playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									case OVERFLOWMODEC.SourcePoint:
										playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(
											playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]+GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length], p, playgroundParticles.worldObject.vertexPositions.Length)
											)+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									}
								}
								
								// Local space compensation calculation
								if (localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
									playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
							}
						break;
						case SOURCEC.SkinnedWorldObject:
							if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) {
								playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
								if (playgroundParticles.applyInitialLocalVelocity)
									playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.skinnedWorldObject.normals[p%playgroundParticles.skinnedWorldObject.normals.Length]));
								downResolutionP = Mathf.RoundToInt(p*downResolution)%playgroundParticles.skinnedWorldObject.vertexPositions.Length;
								if (!overflow) {
									playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+playgroundParticles.playgroundCache.scatterPosition[p];
								} else {
									switch (playgroundParticles.overflowMode) {
									case OVERFLOWMODEC.SourceTransform:
										playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+
											GetOverflow2(
												stDir,
												p, 
												playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution
												)+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									case OVERFLOWMODEC.World:
										playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+
											GetOverflow2(
												playgroundParticles.overflowOffset,
												p, 
												playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution
												)+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									case OVERFLOWMODEC.SourcePoint:
										playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+
											GetOverflow2(
												playgroundParticles.overflowOffset,
												playgroundParticles.skinnedWorldObject.normals[downResolutionP],
												p, 
												playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution
												)+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									}
								}
								
								// Local space compensation calculation
								if (localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
									playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
							}
						
						break;
						case SOURCEC.Projection:
								if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) {
									playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];

									if (playgroundParticles.applyInitialLocalVelocity)
										playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.projection.GetNormal(p), playgroundParticles.playgroundCache.initialLocalVelocity[p]);
									if (!overflow) {
										playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+playgroundParticles.playgroundCache.scatterPosition[p];
									} else {
										switch (playgroundParticles.overflowMode) {
										case OVERFLOWMODEC.SourceTransform:
											playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(stDir, p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
											break;
										case OVERFLOWMODEC.World:
											playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
											break;
										case OVERFLOWMODEC.SourcePoint:
											playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(Vector3Scale(stDir, playgroundParticles.projection.GetNormal(p)), p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
											break;
										}
									}
								}

						break;
						case SOURCEC.Paint:
							if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) {
								playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
								if (playgroundParticles.applyInitialLocalVelocity)
									playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.paint.GetRotation(p)*Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.paint.GetNormal(p));
								if (!overflow) {
									playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+playgroundParticles.playgroundCache.scatterPosition[p];
								} else {
									switch (playgroundParticles.overflowMode) {
									case OVERFLOWMODEC.SourceTransform:
										playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+GetOverflow2(playgroundParticles.paint.GetRotation(p)*playgroundParticles.overflowOffset, p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									case OVERFLOWMODEC.World:
										playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									case OVERFLOWMODEC.SourcePoint:
										playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+playgroundParticles.paint.GetRotation(p)*GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.paint.GetNormal(p), p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
										break;
									}
								}
								
								// Local space compensation calculation
								if (localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) 
									playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
							}
						break;
						}
					}

					// Set initial particle values if life is 0
					if (playgroundParticles.playgroundCache.life[p]==0) {
						playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p];
						playgroundParticles.playgroundCache.initialColor[p] = lifetimeColor;
					}

					// Particle lifetime, velocity and manipulators
					if (playgroundParticles.playgroundCache.rebirth[p]) {
						
						// Particle is alive
						if (playgroundParticles.playgroundCache.life[p]<=PlaygroundC.globalTime+playgroundParticles.lifetime) {
							
							// Reset particle velocity
							//playgroundParticles.particleCache[p].velocity = Vector3.zero;
							
							// Lifetime size
							if (!playgroundParticles.playgroundCache.changedByPropertySize[p]) {
								if (playgroundParticles.applyLifetimeSize)
									playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime))*playgroundParticles.scale;
								else
									playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.scale;
							}
							playgroundParticles.particleCache[p].size = !applyMask?playgroundParticles.playgroundCache.size[p]:0;
							

							
							
							// Local Manipulators
							for (m = 0; m<playgroundParticles.manipulators.Count; m++) {
								if (playgroundParticles.manipulators[m].transform!=null) {
									manipulatorPosition = localSpace?playgroundParticles.manipulators[m].transform.localPosition:playgroundParticles.manipulators[m].transform.position;
									manipulatorDistance = playgroundParticles.manipulators[m].strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/playgroundParticles.manipulators[m].strengthDistanceEffect:10f;
									CalculateManipulator(playgroundParticles, playgroundParticles.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], manipulatorPosition, manipulatorDistance, localSpace);
								}
							}
							
							// Global Manipulators
							for (m = 0; m<PlaygroundC.reference.manipulators.Count; m++) {
								if (PlaygroundC.reference.manipulators[m].transform!=null) {
									manipulatorPosition = localSpace?PlaygroundC.reference.manipulators[m].transform.localPosition:PlaygroundC.reference.manipulators[m].transform.position;
									manipulatorDistance = PlaygroundC.reference.manipulators[m].strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/PlaygroundC.reference.manipulators[m].strengthDistanceEffect:10f;
									CalculateManipulator(playgroundParticles, PlaygroundC.reference.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], manipulatorPosition, manipulatorDistance, localSpace);
								}
							}

							if (!playgroundParticles.onlySourcePositioning) {
								
								// Velocity bending
								if (playgroundParticles.applyVelocityBending) {
									if (playgroundParticles.velocityBendingType==VELOCITYBENDINGTYPEC.SourcePosition) {
										playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect(
											new Vector3(
											playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x,
											playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y, 
											playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z
											),
											(playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.position[p]).normalized
											)*t;
									} else {
										playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect(
											new Vector3(
											playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x,
											playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y, 
											playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z
											),
											(playgroundParticles.playgroundCache.previousParticlePosition[p]-playgroundParticles.playgroundCache.position[p]).normalized
											)*t;
									}
								}
								
								// Delta velocity
								if (!playgroundParticles.cameFromNonEmissionFrame && playgroundParticles.calculateDeltaMovement && playgroundParticles.playgroundCache.life[p]==0 && playgroundParticles.source!=SOURCEC.Script) {
									deltaVelocity = playgroundParticles.playgroundCache.targetPosition[p]-(playgroundParticles.playgroundCache.previousTargetPosition[p]);
									playgroundParticles.playgroundCache.velocity[p] += deltaVelocity*playgroundParticles.deltaMovementStrength;
								}

								// Set previous target position (used by delta velocity & local space movement compensation)
								playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
								
								// Gravity
								playgroundParticles.playgroundCache.velocity[p] -= playgroundParticles.gravity*t;
								
								// Lifetime additive velocity
								if (playgroundParticles.applyLifetimeVelocity) 
									playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.lifetimeVelocity.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime))*t;
								
								// Damping, max velocity, constraints and final positioning
								if (playgroundParticles.playgroundCache.velocity[p]!=zero) {

									// Max velocity
									if (playgroundParticles.playgroundCache.velocity[p].sqrMagnitude>playgroundParticles.maxVelocity)
										playgroundParticles.playgroundCache.velocity[p] = Vector3.ClampMagnitude(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.maxVelocity);

									// Damping
									if (playgroundParticles.damping>0)
										playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], zero, playgroundParticles.damping*t);
									
									// Axis constraints
									if (playgroundParticles.axisConstraints.x)
										playgroundParticles.playgroundCache.velocity[p].x = 0;
									if (playgroundParticles.axisConstraints.y)
										playgroundParticles.playgroundCache.velocity[p].y = 0;
									if (playgroundParticles.axisConstraints.z)
										playgroundParticles.playgroundCache.velocity[p].z = 0;
									
									
									// Set calculated collision position 
									playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.previousParticlePosition[p];

									
									// Relocate
									playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.velocity[p]*t;
									if (playgroundParticles.applyLocalSpaceMovementCompensation) {
										if (!playgroundParticles.applyMovementCompensationLifetimeStrength)
											playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p];
										else
											playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p]*playgroundParticles.movementCompensationLifetimeStrength.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime));
									}

										// Set particle velocity to be able to stretch towards movement
										if (playgroundParticles.renderModeStretch) {
											if (playgroundParticles.applyLifetimeStretching)
												playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime);
											else
												playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], t*playgroundParticles.stretchSpeed);
										}

								}
								
							} else {
								
								// Only Source Positioning
								// Set particle velocity to be able to stretch towards movement
								if (playgroundParticles.renderModeStretch) {
									if (playgroundParticles.applyLifetimeStretching)
										playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p], t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime);
									else
										playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p], t*playgroundParticles.stretchSpeed);
								}
								playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
								if (playgroundParticles.source!=SOURCEC.Script)
									playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p];
								
							}

							// Rotation
							if (!playgroundParticles.rotateTowardsDirection)
								playgroundParticles.playgroundCache.rotation[p] += playgroundParticles.playgroundCache.rotationSpeed[p]*t;
							else {
								playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]+SignedAngle(
									up, 
									playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p],
									playgroundParticles.rotationNormal
								);
							}
								
							playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p];

								// Set previous particle position
								playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.position[p];

							// Send timed event
							if (hasTimerEvent)
								playgroundParticles.SendEvent(EVENTTYPEC.Time, p);
						}


						// Calculate lifetime
						evaluatedLife = (PlaygroundC.globalTime-playgroundParticles.playgroundCache.birth[p])/playgroundParticles.lifetime;
						
						// Lifetime
						if (playgroundParticles.playgroundCache.life[p]<playgroundParticles.lifetime) {
							playgroundParticles.playgroundCache.life[p] = playgroundParticles.lifetime*evaluatedLife;
							playgroundParticles.particleCache[p].lifetime = Mathf.Clamp(playgroundParticles.lifetime - playgroundParticles.playgroundCache.life[p], .1f, playgroundParticles.lifetime);
						} else {
							
							// Loop exceeded
							if (!playgroundParticles.loop && PlaygroundC.globalTime>playgroundParticles.simulationStarted+playgroundParticles.lifetime-.01f) {
								playgroundParticles.loopExceeded = true;
								
								if (playgroundParticles.disableOnDone && playgroundParticles.loopExceededOnParticle==p && evaluatedLife>2)
									playgroundParticles.queueEmissionHalt = true;
								if (playgroundParticles.loopExceededOnParticle==-1)
									playgroundParticles.loopExceededOnParticle = p;
							}
							
							// Send death event
							if (hasEvent) {
								playgroundParticles.SendEvent(EVENTTYPEC.Death, p);
							}
							
							// New cycle begins
							if (PlaygroundC.globalTime>=playgroundParticles.playgroundCache.birth[p]+playgroundParticles.playgroundCache.birthDelay[p] && !playgroundParticles.loopExceeded && playgroundParticles.source!=SOURCEC.Script) {
								if (!playgroundParticles.playgroundCache.changedByPropertyDeath[p] || playgroundParticles.playgroundCache.changedByPropertyDeath[p] && PlaygroundC.globalTime>playgroundParticles.playgroundCache.death[p])
									Rebirth(playgroundParticles, p, playgroundParticles.internalRandom02);
								else {
									playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;
								}
							} else {
								playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;
							}
							
						}

					} else {
						
						// Particle is set to not rebirth
						playgroundParticles.playgroundCache.targetPosition[p] = PlaygroundC.initialTargetPosition;
						playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;
					}

					// Set particle position if no sync
					if (!playgroundParticles.syncPositionsOnMainThread)
						playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.position[p];
				}

				}
				playgroundParticles.cameFromNonEmissionFrame = false;
				playgroundParticles.isDoneThread = true;
			});

			// Turbulence
			if (!playgroundParticles.onlySourcePositioning && playgroundParticles.turbulenceStrength>0 && playgroundParticles.turbulenceType!=TURBULENCETYPE.None) {

				float strengthMultiplier = 1f;
				float zeroFixX = 1f;
				float zeroFixY = 2f; 
				float zeroFixZ = 3f; 

				// Simplex
				if (playgroundParticles.turbulenceType==TURBULENCETYPE.Simplex) {
					if (playgroundParticles.turbulenceSimplex==null)
						playgroundParticles.turbulenceSimplex = new SimplexNoise();
					PlaygroundC.RunAsync(()=>{

						for (int p = 0; p<playgroundParticles.particleCount; p++) {
							if (playgroundParticles.playgroundCache.rebirth[p]) {
								if (playgroundParticles.turbulenceTimeScale>0) {
									if (playgroundParticles.turbulenceApplyLifetimeStrength)
										strengthMultiplier = playgroundParticles.turbulenceLifetimeStrength.Evaluate (Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime));
									if (strengthMultiplier>0) {
									if (!playgroundParticles.axisConstraints.x)
										playgroundParticles.playgroundCache.velocity[p].x += (float)playgroundParticles.turbulenceSimplex.noise(
										(playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale,
										PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale
										)*playgroundParticles.turbulenceStrength*t*strengthMultiplier;
									if (!playgroundParticles.axisConstraints.y)
										playgroundParticles.playgroundCache.velocity[p].y += (float)playgroundParticles.turbulenceSimplex.noise(
										(playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale,
										PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale
										)*playgroundParticles.turbulenceStrength*t*strengthMultiplier;
									if (!playgroundParticles.axisConstraints.z)
										playgroundParticles.playgroundCache.velocity[p].z += (float)playgroundParticles.turbulenceSimplex.noise(
										(playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale,
										PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale
										)*playgroundParticles.turbulenceStrength*t*strengthMultiplier;
									}
								} else {
									if (strengthMultiplier>0) {
									if (!playgroundParticles.axisConstraints.x)
										playgroundParticles.playgroundCache.velocity[p].x += (float)playgroundParticles.turbulenceSimplex.noise(
										(playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale
										)*playgroundParticles.turbulenceStrength*t*strengthMultiplier;
									if (!playgroundParticles.axisConstraints.y)
										playgroundParticles.playgroundCache.velocity[p].y += (float)playgroundParticles.turbulenceSimplex.noise(
										(playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale
										)*playgroundParticles.turbulenceStrength*t*strengthMultiplier;
									if (!playgroundParticles.axisConstraints.z)
										playgroundParticles.playgroundCache.velocity[p].z += (float)playgroundParticles.turbulenceSimplex.noise(
										(playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale,
										(playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale
										)*playgroundParticles.turbulenceStrength*t*strengthMultiplier;
									}
								}
							}
						}
					});
				}

				// Perlin
				if (playgroundParticles.turbulenceType==TURBULENCETYPE.Perlin) {
					PlaygroundC.RunAsync(()=>{
						for (int p = 0; p<playgroundParticles.particleCount; p++) {
							if (playgroundParticles.playgroundCache.rebirth[p]) {
								if (playgroundParticles.turbulenceStrength>0) {
									if (playgroundParticles.turbulenceApplyLifetimeStrength)
										strengthMultiplier = playgroundParticles.turbulenceLifetimeStrength.Evaluate (Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime));
									if (strengthMultiplier>0) {
									if (!playgroundParticles.axisConstraints.x)
										playgroundParticles.playgroundCache.velocity[p].x += (Mathf.PerlinNoise (
											PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale,
											playgroundParticles.playgroundCache.position[p].z*playgroundParticles.turbulenceScale
										)-.5f)*playgroundParticles.turbulenceStrength*t*strengthMultiplier;
									if (!playgroundParticles.axisConstraints.y)
										playgroundParticles.playgroundCache.velocity[p].y += (Mathf.PerlinNoise (
											PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale,
											playgroundParticles.playgroundCache.position[p].x*playgroundParticles.turbulenceScale

										)-.5f)*playgroundParticles.turbulenceStrength*t*strengthMultiplier;
									if (!playgroundParticles.axisConstraints.z)
										playgroundParticles.playgroundCache.velocity[p].z += (Mathf.PerlinNoise (
											PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale,
											playgroundParticles.playgroundCache.position[p].y*playgroundParticles.turbulenceScale

										)-.5f)*playgroundParticles.turbulenceStrength*t*strengthMultiplier;
									}
								}
							}
						}
					});
				}
			}

		}
        public static void ThreadedCalculations(PlaygroundParticlesC playgroundParticles)
        {
            playgroundParticles.lastTimeUpdated = PlaygroundC.globalTime;

            if (playgroundParticles.particleCount==0 ||
                playgroundParticles.playgroundCache.color.Length!=playgroundParticles.particleCount ||
                playgroundParticles.playgroundCache.targetPosition.Length!=playgroundParticles.particleCount ||
                playgroundParticles.playgroundCache.targetDirection.Length!=playgroundParticles.particleCount ||
                playgroundParticles.isYieldRefreshing || !PlaygroundC.IsReady()) {
                playgroundParticles.isDoneThread = true;
                playgroundParticles.threadHadNoActiveParticles = false;
                return;
            }
            if (PlaygroundC.reference.calculate && playgroundParticles.calculate && !playgroundParticles.inTransition && playgroundParticles.hasActiveParticles) {}
            else if (playgroundParticles.source!=SOURCEC.Script) {
                playgroundParticles.isDoneThread = true;
                playgroundParticles.cameFromNonCalculatedFrame = true;
                return;
            } else {
                playgroundParticles.isDoneThread = true;
                return;
            }

            float t = playgroundParticles.t;

            // Prepare variables for particle source positions
            Matrix4x4 stMx = new Matrix4x4();
            Matrix4x4 fMx = new Matrix4x4();
            stMx.SetTRS(playgroundParticles.stPos, playgroundParticles.stRot, playgroundParticles.stSca);
            fMx.SetTRS(Vector3.zero, playgroundParticles.stRot, new Vector3(1f,1f,1f));
            int downResolution = playgroundParticles.skinnedWorldObject.downResolution;
            int downResolutionP;
            bool noActiveParticles = true;

            // Update skinned mesh vertices
            if (playgroundParticles.source==SOURCEC.SkinnedWorldObject && playgroundParticles.skinnedWorldObjectReady && PlaygroundC.reference.skinnedMeshThreadMethod==ThreadMethodComponent.InsideParticleCalculation) {
                playgroundParticles.skinnedWorldObject.Update();
            }

            // Misc
            int pCount = playgroundParticles.particleCache.Length;
            bool applyMask = false;

            // Check that cache is correct
            if (playgroundParticles.playgroundCache.lifetimeSubtraction.Length!=pCount)
                SetLifetimeSubtraction(playgroundParticles);
            if (playgroundParticles.playgroundCache.maskAlpha.Length!=pCount) {
                playgroundParticles.playgroundCache.maskAlpha = new float[pCount];
                playgroundParticles.playgroundCache.isMasked = new bool[pCount];
            }
            if (playgroundParticles.playgroundCache.manipulatorId.Length!=pCount)
                playgroundParticles.playgroundCache.manipulatorId = new int[pCount];
            if (playgroundParticles.playgroundCache.excludeFromManipulatorId.Length!=pCount)
                playgroundParticles.playgroundCache.excludeFromManipulatorId = new int[pCount];
            if (playgroundParticles.playgroundCache.noForce.Length!=pCount)
                playgroundParticles.playgroundCache.noForce = new bool[pCount];
            if (playgroundParticles.playgroundCache.isNonBirthed.Length!=pCount)
                playgroundParticles.playgroundCache.isNonBirthed = new bool[pCount];
            if (playgroundParticles.playgroundCache.isFirstLoop.Length!=pCount)
                playgroundParticles.playgroundCache.isFirstLoop = new bool[pCount];
            if (playgroundParticles.playgroundCache.simulate.Length!=pCount) {
                playgroundParticles.playgroundCache.simulate = new bool[pCount];
                for (int p = 0; p<pCount; p++)
                    playgroundParticles.playgroundCache.simulate[p] = true;
            }

            Color lifetimeColor = new Color();
            Vector3 zero = Vector3.zero;
            Vector3 up = Vector3.up;

            // Calculation loop
            for (int p = 0; p<playgroundParticles.particleCount; p++) {

                // Check that particle count is correct
                if (pCount != playgroundParticles.particleCache.Length || playgroundParticles.isYieldRefreshing) {
                    playgroundParticles.cameFromNonEmissionFrame = false;
                    playgroundParticles.isDoneThread = true;
                    playgroundParticles.threadHadNoActiveParticles = false;
                    return;
                }

                // Check simulation
                if (!playgroundParticles.playgroundCache.simulate[p]) {
                    if (playgroundParticles.playgroundCache.rebirth[p] && playgroundParticles.loop) {
                        playgroundParticles.InactivateParticle(p);
                    }
                    continue;
                } else {
                    noActiveParticles = false;
                }

                // Prepare variables inside scope
                bool hasLifetimeColors = (playgroundParticles.lifetimeColors.Count>0);
                float manipulatorDistance;
                Vector3 deltaVelocity;
                Vector3 manipulatorPosition;
                float normalizedLife = Mathf.Clamp01 (playgroundParticles.playgroundCache.life[p]/(playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p]));
                float evaluatedLife;
                float lifetimePositioningTimeScale = 1f;
                if (playgroundParticles.applyLifetimePositioningTimeScale)
                    lifetimePositioningTimeScale = playgroundParticles.lifetimePositioningTimeScale.Evaluate(normalizedLife);

                // Apply particle mask
                if (p<playgroundParticles.particleMask) {
                    if (playgroundParticles.playgroundCache.maskAlpha[p]<=0 || playgroundParticles.particleMaskTime<=0) {
                        playgroundParticles.playgroundCache.isMasked[p] = true;
                        playgroundParticles.playgroundCache.maskAlpha[p] = 0;
                        playgroundParticles.particleCache[p].size = 0;
                        applyMask = true;
                    } else {
                        applyMask = false;
                        playgroundParticles.playgroundCache.maskAlpha[p] -= (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime;
                    }
                } else {
                    applyMask = false;
                    if (playgroundParticles.playgroundCache.maskAlpha[p]<1f && playgroundParticles.particleMaskTime>0) {
                        playgroundParticles.playgroundCache.maskAlpha[p] += (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime;
                    } else {
                        playgroundParticles.playgroundCache.maskAlpha[p] = 1f;
                        playgroundParticles.playgroundCache.isMasked[p] = false;
                    }
                }

                // Get this particle's color
                if (!playgroundParticles.playgroundCache.changedByPropertyColor[p] && !playgroundParticles.playgroundCache.changedByPropertyColorLerp[p]) {
                    switch (playgroundParticles.colorSource) {
                    case COLORSOURCEC.Source:
                        switch (playgroundParticles.source) {
                        case SOURCEC.Script:
                            lifetimeColor = playgroundParticles.playgroundCache.scriptedColor[p];
                            break;
                        case SOURCEC.State:
                            if (playgroundParticles.stateReadyForTextureColor)
                                lifetimeColor = playgroundParticles.states[playgroundParticles.activeState].GetColor(p);
                            else
                                lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(normalizedLife);
                            break;
                        case SOURCEC.Projection:
                            lifetimeColor = playgroundParticles.projection.GetColor(p);
                            break;
                        case SOURCEC.Paint:
                            lifetimeColor = playgroundParticles.paint.GetColor(p);
                            break;
                        default:
                            lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(normalizedLife);
                            break;
                        }
                        if (playgroundParticles.sourceUsesLifetimeAlpha)
                            lifetimeColor.a = Mathf.Clamp(playgroundParticles.lifetimeColor.Evaluate(normalizedLife).a, 0, lifetimeColor.a);
                        break;
                    case COLORSOURCEC.LifetimeColor:
                        lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(normalizedLife);
                        break;
                    case COLORSOURCEC.LifetimeColors:
                        if (hasLifetimeColors)
                            lifetimeColor = playgroundParticles.lifetimeColors[playgroundParticles.playgroundCache.lifetimeColorId[p]%playgroundParticles.lifetimeColors.Count].gradient.Evaluate(normalizedLife);
                        else
                            lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(normalizedLife);
                        break;
                    }

                } else if (playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p] || playgroundParticles.playgroundCache.changedByPropertyColorLerp[p]) {
                    lifetimeColor = playgroundParticles.particleCache[p].color;
                    lifetimeColor.a = Mathf.Clamp(playgroundParticles.lifetimeColor.Evaluate(normalizedLife).a, 0, lifetimeColor.a);
                }

                // Assign color to particle
                lifetimeColor*=playgroundParticles.playgroundCache.maskAlpha[p];
                playgroundParticles.particleCache[p].color = lifetimeColor;

                // Give Playground Cache its color value
                playgroundParticles.playgroundCache.color[p] = lifetimeColor;

                // Source positions
                if (playgroundParticles.emit) {
                    switch (playgroundParticles.source) {
                    case SOURCEC.State:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                            playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.states[playgroundParticles.activeState].GetNormal(p)));
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.states[playgroundParticles.activeState].GetNormal(p)));
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife);
                            }
                            if (!playgroundParticles.overflow) {
                                playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p))+playgroundParticles.playgroundCache.scatterPosition[p];
                            } else {
                                switch (playgroundParticles.overflowMode) {
                                case OVERFLOWMODEC.SourceTransform:
                                    playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.states[playgroundParticles.activeState].positionLength))+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                case OVERFLOWMODEC.World:
                                    playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p))+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.states[playgroundParticles.activeState].positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                case OVERFLOWMODEC.SourcePoint:
                                    playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.states[playgroundParticles.activeState].GetNormal(p), p, playgroundParticles.states[playgroundParticles.activeState].positionLength))+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                }
                            }

                            // Local space compensation calculation
                            if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                        }
                    break;
                    case SOURCEC.Transform:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                            playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.stRot*playgroundParticles.playgroundCache.initialLocalVelocity[p];
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.stRot*playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale);
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife*lifetimePositioningTimeScale);
                            }
                            if (!playgroundParticles.overflow) {
                                playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.stPos+playgroundParticles.playgroundCache.scatterPosition[p];
                            } else {
                                switch (playgroundParticles.overflowMode) {
                                case OVERFLOWMODEC.SourceTransform:
                                    playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(GetOverflow2(playgroundParticles.overflowOffset, p, 1))+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                case OVERFLOWMODEC.World:
                                    playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.stPos+GetOverflow2(playgroundParticles.overflowOffset, p, 1)+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                case OVERFLOWMODEC.SourcePoint:
                                    playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(GetOverflow2(playgroundParticles.overflowOffset, p, 1))+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                }
                            }

                            // Local space compensation calculation
                            if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                        }
                    break;
                    case SOURCEC.WorldObject:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                            playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length]));
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length]));
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife);
                            }
                            if (!playgroundParticles.overflow) {
                                playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(
                                    playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]
                                    )+playgroundParticles.playgroundCache.scatterPosition[p];
                            } else {
                                switch (playgroundParticles.overflowMode) {
                                case OVERFLOWMODEC.SourceTransform:
                                    playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(
                                        playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.worldObject.vertexPositions.Length)
                                        )+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                case OVERFLOWMODEC.World:
                                    playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(
                                        playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]
                                        )+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.worldObject.vertexPositions.Length)+
                                        playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                case OVERFLOWMODEC.SourcePoint:
                                    playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(
                                        playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]+GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length], p, playgroundParticles.worldObject.vertexPositions.Length)
                                        )+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                }
                            }

                            // Local space compensation calculation
                            if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                        }
                    break;
                    case SOURCEC.SkinnedWorldObject:
                        if (playgroundParticles.skinnedWorldObjectReady) {
                            if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                                if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                    playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.skinnedWorldObject.normals[p%playgroundParticles.skinnedWorldObject.normals.Length]));
                                if (playgroundParticles.onlyLifetimePositioning) {
                                    playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.skinnedWorldObject.normals[p%playgroundParticles.skinnedWorldObject.normals.Length]));
                                    if (playgroundParticles.applyLifetimePositioningPositionScale)
                                        playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife);
                                }
                                downResolutionP = Mathf.RoundToInt(p*downResolution)%playgroundParticles.skinnedWorldObject.vertexPositions.Length;
                                if (!playgroundParticles.overflow) {
                                    playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+playgroundParticles.playgroundCache.scatterPosition[p];
                                } else {
                                    switch (playgroundParticles.overflowMode) {
                                    case OVERFLOWMODEC.SourceTransform:
                                        playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+
                                            GetOverflow2(
                                                playgroundParticles.stDir,
                                                p,
                                                playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution
                                                )+playgroundParticles.playgroundCache.scatterPosition[p];
                                        break;
                                    case OVERFLOWMODEC.World:
                                        playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+
                                            GetOverflow2(
                                                playgroundParticles.overflowOffset,
                                                p,
                                                playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution
                                                )+playgroundParticles.playgroundCache.scatterPosition[p];
                                        break;
                                    case OVERFLOWMODEC.SourcePoint:
                                        playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+
                                            GetOverflow2(
                                                playgroundParticles.overflowOffset,
                                                playgroundParticles.skinnedWorldObject.normals[downResolutionP],
                                                p,
                                                playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution
                                                )+playgroundParticles.playgroundCache.scatterPosition[p];
                                        break;
                                    }
                                }

                                // Local space compensation calculation
                                if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                    playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                            }
                        }

                    break;
                    case SOURCEC.Projection:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                            playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];

                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.projection.GetNormal(p), playgroundParticles.playgroundCache.initialLocalVelocity[p]);
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.projection.GetNormal(p), playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale));
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife);
                            }
                            if (!playgroundParticles.overflow) {
                                playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+playgroundParticles.playgroundCache.scatterPosition[p];
                            } else {
                                switch (playgroundParticles.overflowMode) {
                                case OVERFLOWMODEC.SourceTransform:
                                    playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(playgroundParticles.stDir, p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                case OVERFLOWMODEC.World:
                                    playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                case OVERFLOWMODEC.SourcePoint:
                                    playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(Vector3Scale(playgroundParticles.stDir, playgroundParticles.projection.GetNormal(p)), p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                }
                            }
                        }

                    break;
                    case SOURCEC.Paint:
                        if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) {
                            playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                            if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning)
                                playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.paint.GetRotation(p)*Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.paint.GetNormal(p));
                            if (playgroundParticles.onlyLifetimePositioning) {
                                playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.paint.GetRotation(p)*Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.paint.GetNormal(p));
                                if (playgroundParticles.applyLifetimePositioningPositionScale)
                                    playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife);
                            }
                            if (!playgroundParticles.overflow) {
                                playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+playgroundParticles.playgroundCache.scatterPosition[p];
                            } else {
                                switch (playgroundParticles.overflowMode) {
                                case OVERFLOWMODEC.SourceTransform:
                                    playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+GetOverflow2(playgroundParticles.paint.GetRotation(p)*playgroundParticles.overflowOffset, p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                case OVERFLOWMODEC.World:
                                    playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                case OVERFLOWMODEC.SourcePoint:
                                    playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+playgroundParticles.paint.GetRotation(p)*GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.paint.GetNormal(p), p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p];
                                    break;
                                }
                            }

                            // Local space compensation calculation
                            if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation)
                                playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p];
                        }
                    break;
                    }
                }

                // Set initial particle values if life is 0
                if (playgroundParticles.playgroundCache.life[p]==0) {
                    if (!playgroundParticles.onlyLifetimePositioning)
                        playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p];
                    playgroundParticles.playgroundCache.initialColor[p] = lifetimeColor;
                }

                // Particle lifetime, velocity and manipulators
                if (playgroundParticles.playgroundCache.rebirth[p]) {

                    // Particle is alive
                    if (playgroundParticles.playgroundCache.birth[p]+playgroundParticles.playgroundCache.life[p]<=PlaygroundC.globalTime+(playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p]) && (!playgroundParticles.playgroundCache.isNonBirthed[p] || playgroundParticles.onlyLifetimePositioning || playgroundParticles.onlySourcePositioning)) {

                        // Lifetime size
                        if (!playgroundParticles.playgroundCache.changedByPropertySize[p]) {
                            if (playgroundParticles.applyLifetimeSize)
                                playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(normalizedLife)*playgroundParticles.scale;
                            else
                                playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.scale;
                        }

                        // Local Manipulators
                        for (int m = 0; m<playgroundParticles.manipulators.Count; m++) {
                            if (playgroundParticles.manipulators[m].transform!=null) {
                                manipulatorPosition = playgroundParticles.localSpace?playgroundParticles.manipulators[m].transform.localPosition:playgroundParticles.manipulators[m].transform.position;
                                manipulatorDistance = playgroundParticles.manipulators[m].strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/playgroundParticles.manipulators[m].strengthDistanceEffect:10f;
                                CalculateManipulator(playgroundParticles, playgroundParticles.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], manipulatorPosition, manipulatorDistance, playgroundParticles.localSpace);
                            }
                        }

                        // Set particle size
                        playgroundParticles.particleCache[p].size = !applyMask?playgroundParticles.playgroundCache.size[p]:0;

                        if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning) {

                            if (!playgroundParticles.playgroundCache.noForce[p] && playgroundParticles.playgroundCache.life[p]!=0) {

                                // Velocity bending
                                if (playgroundParticles.applyVelocityBending) {
                                    if (playgroundParticles.velocityBendingType==VELOCITYBENDINGTYPEC.SourcePosition) {
                                        playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect(
                                            new Vector3(
                                            playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x,
                                            playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y,
                                            playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z
                                            ),
                                            (playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.position[p]).normalized
                                            )*t;
                                    } else {
                                        playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect(
                                            new Vector3(
                                            playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x,
                                            playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y,
                                            playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z
                                            ),
                                            (playgroundParticles.playgroundCache.previousParticlePosition[p]-playgroundParticles.playgroundCache.position[p]).normalized
                                            )*t;
                                    }
                                }

                                // Delta velocity
                                if (!playgroundParticles.cameFromNonEmissionFrame && playgroundParticles.calculateDeltaMovement && playgroundParticles.playgroundCache.life[p]==0 && playgroundParticles.source!=SOURCEC.Script) {
                                    deltaVelocity = playgroundParticles.playgroundCache.targetPosition[p]-(playgroundParticles.playgroundCache.previousTargetPosition[p]);
                                    playgroundParticles.playgroundCache.velocity[p] += deltaVelocity*playgroundParticles.deltaMovementStrength;
                                }

                                // Set previous target position (used by delta velocity & local space movement compensation)
                                playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];

                                // Gravity
                                playgroundParticles.playgroundCache.velocity[p] -= playgroundParticles.gravity*t;

                                // Lifetime additive velocity
                                if (playgroundParticles.applyLifetimeVelocity)
                                    playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.lifetimeVelocity.Evaluate(normalizedLife, playgroundParticles.lifetimeVelocityScale)*t;

                                // Turbulence inside the calculation loop
                                if (playgroundParticles.HasTurbulence() && PlaygroundC.reference.turbulenceThreadMethod==ThreadMethodComponent.InsideParticleCalculation) {
                                if (!playgroundParticles.playgroundCache.noForce[p])
                                    Turbulence (playgroundParticles, playgroundParticles.turbulenceSimplex, p, playgroundParticles.t, playgroundParticles.turbulenceType, playgroundParticles.turbulenceTimeScale, playgroundParticles.turbulenceScale/playgroundParticles.velocityScale, playgroundParticles.turbulenceStrength, playgroundParticles.turbulenceApplyLifetimeStrength, playgroundParticles.turbulenceLifetimeStrength);
                                }

                                // Damping, max velocity, constraints and final positioning
                                if (playgroundParticles.playgroundCache.velocity[p]!=zero) {

                                    // Max velocity
                                    if (playgroundParticles.playgroundCache.velocity[p].sqrMagnitude>playgroundParticles.maxVelocity)
                                        playgroundParticles.playgroundCache.velocity[p] = Vector3.ClampMagnitude(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.maxVelocity);

                                    // Damping
                                    if (playgroundParticles.damping>0)
                                        playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], zero, playgroundParticles.damping*t);

                                    // Axis constraints
                                    if (playgroundParticles.axisConstraints.x)
                                        playgroundParticles.playgroundCache.velocity[p].x = 0;
                                    if (playgroundParticles.axisConstraints.y)
                                        playgroundParticles.playgroundCache.velocity[p].y = 0;
                                    if (playgroundParticles.axisConstraints.z)
                                        playgroundParticles.playgroundCache.velocity[p].z = 0;

                                    // Set calculated collision position
                                    playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.previousParticlePosition[p];

                                    // Relocate
                                    playgroundParticles.playgroundCache.position[p] += (playgroundParticles.playgroundCache.velocity[p]*playgroundParticles.velocityScale)*t;
                                    if (playgroundParticles.applyLocalSpaceMovementCompensation) {
                                        if (!playgroundParticles.applyMovementCompensationLifetimeStrength)
                                            playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p];
                                        else
                                            playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p]*playgroundParticles.movementCompensationLifetimeStrength.Evaluate(normalizedLife);
                                    }

                                        // Set particle velocity to be able to stretch towards movement
                                        if (playgroundParticles.renderModeStretch) {
                                            if (playgroundParticles.applyLifetimeStretching)
                                                playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime);
                                            else
                                                playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], t*playgroundParticles.stretchSpeed);
                                        }

                                }
                            } else playgroundParticles.particleCache[p].velocity = Vector3.zero;
                        } else {

                            // Only Source Positioning / Lifetime Positioning
                            // Set particle velocity to be able to stretch towards movement
                            if (playgroundParticles.renderModeStretch) {
                                if (playgroundParticles.applyLifetimeStretching)
                                    playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p], t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime);
                                else
                                    playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p], t*playgroundParticles.stretchSpeed);
                            }

                            if (playgroundParticles.onlyLifetimePositioning && !playgroundParticles.onlySourcePositioning) {
                                if (!playgroundParticles.playgroundCache.changedByPropertyTarget[p]) {
                                    if (playgroundParticles.lifetimePositioningUsesSourceDirection && playgroundParticles.source!=SOURCEC.Script) {
                                        playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+(playgroundParticles.playgroundCache.targetDirection[p]);
                                    } else {
                                        if (!playgroundParticles.applyLifetimePositioningPositionScale) {
                                            playgroundParticles.playgroundCache.position[p] =
                                            playgroundParticles.playgroundCache.targetPosition[p]+
                                                playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale);
                                        } else {
                                            playgroundParticles.playgroundCache.position[p] =
                                            playgroundParticles.playgroundCache.targetPosition[p]+
                                            playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale)*
                                            playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife*lifetimePositioningTimeScale);
                                        }
                                    }
                                }
                            } else if (playgroundParticles.source!=SOURCEC.Script) {
                                playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p];
                            }

                            playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p];

                        }

                        // Rotation
                        if (!playgroundParticles.rotateTowardsDirection)
                            playgroundParticles.playgroundCache.rotation[p] += playgroundParticles.playgroundCache.rotationSpeed[p]*t;
                        else if (playgroundParticles.playgroundCache.life[p]!=0) {
                            playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]+SignedAngle(
                                up,
                                playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p],
                                playgroundParticles.rotationNormal
                            );
                        }

                        if (playgroundParticles.playgroundCache.life[p]>0)
                            playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p];

                        // Set previous particle position
                        playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.position[p];

                        // Send timed event
                        if (playgroundParticles.hasTimerEvent)
                            playgroundParticles.SendEvent(EVENTTYPEC.Time, p);
                    } else {
                        playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;
                        playgroundParticles.particleCache[p].size = 0;
                    }

                    // Calculate lifetime
                    evaluatedLife = (PlaygroundC.globalTime-playgroundParticles.playgroundCache.birth[p])/(playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p]);

                    // Lifetime
                    if (playgroundParticles.playgroundCache.life[p]<playgroundParticles.lifetime) {
                        playgroundParticles.playgroundCache.life[p] = (playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p])*evaluatedLife;
                        playgroundParticles.particleCache[p].lifetime = Mathf.Clamp(playgroundParticles.lifetime - playgroundParticles.playgroundCache.life[p], .1f, playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p]);

                        if (playgroundParticles.lifetimeValueMethod==VALUEMETHOD.RandomBetweenTwoValues && playgroundParticles.playgroundCache.life[p]>playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p]) {

                            // Send death event for particles with lifetime subtraction
                            if (playgroundParticles.hasEvent && !playgroundParticles.playgroundCache.isNonBirthed[p])
                                SendDeathEvents(playgroundParticles, p);

                            playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;
                            playgroundParticles.particleCache[p].size = 0;
                        }
                    } else {

                        // Loop exceeded
                        if (!playgroundParticles.loop && PlaygroundC.globalTime>playgroundParticles.simulationStarted+playgroundParticles.lifetime-.01f) {
                            playgroundParticles.loopExceeded = true;

                            if (playgroundParticles.loopExceededOnParticle==p && evaluatedLife>2f) {
                                if (playgroundParticles.disableOnDone)
                                    playgroundParticles.queueEmissionHalt = true;
                                playgroundParticles.threadHadNoActiveParticles = true;
                                playgroundParticles.hasActiveParticles = false;
                            }
                            if (playgroundParticles.loopExceededOnParticle==-1)
                                playgroundParticles.loopExceededOnParticle = p;
                        }

                        // Send death event for particles with full lifetime length
                        if (playgroundParticles.hasEvent && !playgroundParticles.playgroundCache.isNonBirthed[p])
                            SendDeathEvents(playgroundParticles, p);

                        // New cycle begins
                        if (PlaygroundC.globalTime>=playgroundParticles.playgroundCache.birth[p]+playgroundParticles.playgroundCache.birthDelay[p] && !playgroundParticles.loopExceeded && playgroundParticles.source!=SOURCEC.Script && playgroundParticles.emit) {
                            if (!playgroundParticles.playgroundCache.changedByPropertyDeath[p] || playgroundParticles.playgroundCache.changedByPropertyDeath[p] && PlaygroundC.globalTime>playgroundParticles.playgroundCache.death[p]) {
                                Rebirth(playgroundParticles, p, playgroundParticles.internalRandom01);
                            } else {
                                playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;
                                playgroundParticles.particleCache[p].size = 0;
                            }
                        } else {
                            playgroundParticles.InactivateParticle(p);
                        }

                    }

                } else {

                    // Particle is set to not rebirth
                    playgroundParticles.InactivateParticle(p);
                }

                // Set particle position if no sync
                if (!playgroundParticles.syncPositionsOnMainThread) {
                    playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.position[p];
                }

            }// <- Particle loop ends here

            // Reset for next frame
            playgroundParticles.threadHadNoActiveParticles = noActiveParticles;
            playgroundParticles.cameFromNonEmissionFrame = false;
            playgroundParticles.isDoneThread = true;

            // Turbulence calculated here when using PlaygroundC.turbulenceThreadMethod of ThreadMethodComponent.OnePerSystem
            if (playgroundParticles.HasTurbulence() && PlaygroundC.reference.turbulenceThreadMethod==ThreadMethodComponent.OnePerSystem) {
                PlaygroundC.RunAsync(()=>{
                    for (int p = 0; p<playgroundParticles.particleCount; p++) {
                        if (!playgroundParticles.playgroundCache.noForce[p])
                            Turbulence (playgroundParticles, playgroundParticles.turbulenceSimplex, p, playgroundParticles.t, playgroundParticles.turbulenceType, playgroundParticles.turbulenceTimeScale, playgroundParticles.turbulenceScale/playgroundParticles.velocityScale, playgroundParticles.turbulenceStrength, playgroundParticles.turbulenceApplyLifetimeStrength, playgroundParticles.turbulenceLifetimeStrength);
                    }
                });
            }
        }
        // Rebirth of a specified particle
        public static void Rebirth(PlaygroundParticlesC playgroundParticles, int p, System.Random random)
        {
            if (!playgroundParticles.hasActiveParticles) return;
            // Set initial values
            playgroundParticles.playgroundCache.rebirth[p] = playgroundParticles.source==SOURCEC.Script?true:(playgroundParticles.emit && (playgroundParticles.loop || playgroundParticles.playgroundCache.isNonBirthed[p]) && playgroundParticles.playgroundCache.emission[p]);
            playgroundParticles.playgroundCache.isFirstLoop[p] = playgroundParticles.playgroundCache.isNonBirthed[p];
            playgroundParticles.playgroundCache.isNonBirthed[p] = false;
            playgroundParticles.playgroundCache.birthDelay[p] = 0f;
            playgroundParticles.playgroundCache.life[p] = 0f;
            playgroundParticles.playgroundCache.birth[p] = playgroundParticles.playgroundCache.death[p];
            playgroundParticles.playgroundCache.death[p] += playgroundParticles.lifetime;
            playgroundParticles.playgroundCache.velocity[p] = Vector3.zero;
            playgroundParticles.playgroundCache.noForce[p] = false;

            // Set new random lifetime
            if (playgroundParticles.applyRandomLifetimeOnRebirth) {
                if (playgroundParticles.lifetimeValueMethod==VALUEMETHOD.Constant) {
                    playgroundParticles.playgroundCache.lifetimeSubtraction[p] = 0;
                } else {
                    playgroundParticles.playgroundCache.lifetimeSubtraction[p] = playgroundParticles.lifetime-RandomRange (random, playgroundParticles.lifetimeMin, playgroundParticles.lifetime);
                }
            }

            // Set new random size
            if (playgroundParticles.applyRandomSizeOnRebirth)
                playgroundParticles.playgroundCache.initialSize[p] = RandomRange(random, playgroundParticles.sizeMin, playgroundParticles.sizeMax);

            // Initial velocity
            if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning) {

                // Initial global velocity
                if (playgroundParticles.applyInitialVelocity) {
                    if (playgroundParticles.applyRandomInitialVelocityOnRebirth)
                        playgroundParticles.playgroundCache.initialVelocity[p] = RandomRange(random, playgroundParticles.initialVelocityMin, playgroundParticles.initialVelocityMax);
                    playgroundParticles.playgroundCache.velocity[p] = playgroundParticles.playgroundCache.initialVelocity[p];

                    // Give this spawning particle its velocity shape
                    if (playgroundParticles.applyInitialVelocityShape)
                        playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f), playgroundParticles.initialVelocityShapeScale));
                }

                // Initial local velocity
                if (playgroundParticles.applyInitialLocalVelocity && playgroundParticles.source!=SOURCEC.Script) {
                    playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRange(random, playgroundParticles.initialLocalVelocityMin, playgroundParticles.initialLocalVelocityMax);
                    playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.playgroundCache.targetDirection[p];

                    // Give this spawning particle its local velocity shape
                    if (playgroundParticles.applyInitialVelocityShape)
                        playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f), playgroundParticles.initialVelocityShapeScale));
                }

                // Initial stretch
                if (playgroundParticles.renderModeStretch) {
                    if (playgroundParticles.playgroundCache.velocity[p]!=Vector3.zero)
                        playgroundParticles.particleCache[p].velocity = playgroundParticles.playgroundCache.velocity[p];
                    else
                        playgroundParticles.particleCache[p].velocity = playgroundParticles.stretchStartDirection;
                } else
                    playgroundParticles.particleCache[p].velocity = Vector3.zero;
            }
            if (playgroundParticles.source==SOURCEC.Script) {
                // Velocity for script mode
                if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning)
                    playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.scriptedEmissionVelocity;
                playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.scriptedEmissionPosition;
                playgroundParticles.particleCache[p].velocity = playgroundParticles.playgroundCache.velocity[p];
            }

            if (playgroundParticles.playgroundCache.rebirth[p]) {

                // Source Scattering
                if (playgroundParticles.applySourceScatter && playgroundParticles.source!=SOURCEC.Script) {
                    if (playgroundParticles.playgroundCache.scatterPosition[p]==Vector3.zero || playgroundParticles.applyRandomScatterOnRebirth)
                        playgroundParticles.playgroundCache.scatterPosition[p] = RandomRange(random, playgroundParticles.sourceScatterMin, playgroundParticles.sourceScatterMax);
                } else playgroundParticles.playgroundCache.scatterPosition[p] = Vector3.zero;

                if (!playgroundParticles.onlyLifetimePositioning) {
                    playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p];
                    playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.targetPosition[p];
                    playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                    playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p];
                } else if (!playgroundParticles.onlySourcePositioning) {
                    Vector3 evalLifetimePosition = playgroundParticles.lifetimePositioning.Evaluate(0f, playgroundParticles.lifetimePositioningScale);
                    if (playgroundParticles.lifetimePositioningUsesSourceDirection)
                        evalLifetimePosition = playgroundParticles.playgroundCache.targetDirection[p];
                    playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+evalLifetimePosition;
                    playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.targetPosition[p]+evalLifetimePosition;
                    playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]+evalLifetimePosition;
                    playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]+evalLifetimePosition;
                }

                if (playgroundParticles.applyInitialColorOnRebirth) {
                    playgroundParticles.particleCache[p].color = playgroundParticles.playgroundCache.initialColor[p];
                    playgroundParticles.playgroundCache.color[p] = playgroundParticles.playgroundCache.initialColor[p];
                }
            } else {
                playgroundParticles.particleCache[p].position = PlaygroundC.initialTargetPosition;
            }

            // Set new random rotation
            if (playgroundParticles.applyRandomRotationOnRebirth && !playgroundParticles.rotateTowardsDirection)
                playgroundParticles.playgroundCache.initialRotation[p] = RandomRange(random, playgroundParticles.initialRotationMin, playgroundParticles.initialRotationMax);

            if (!playgroundParticles.rotateTowardsDirection)
                playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p];
            else {
                Vector3 particleDir;
                if (!playgroundParticles.onlySourcePositioning&&playgroundParticles.onlyLifetimePositioning)
                    particleDir = (playgroundParticles.playgroundCache.position[p]+playgroundParticles.lifetimePositioning.Evaluate(.01f, playgroundParticles.lifetimePositioningScale))-playgroundParticles.playgroundCache.position[p];
                else
                    particleDir = playgroundParticles.playgroundCache.velocity[p];
                playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]+SignedAngle(
                    Vector3.up,
                    particleDir,
                    playgroundParticles.rotationNormal
                );
            }

            playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p];

            // Set size
            if (playgroundParticles.applyLifetimeSize) {
                //playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(0f)*playgroundParticles.scale;
            } else
                playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.scale;
            playgroundParticles.particleCache[p].size = p>=playgroundParticles.particleMask?playgroundParticles.playgroundCache.size[p]:0;

            // Set color gradient id
            if (playgroundParticles.colorSource==COLORSOURCEC.LifetimeColors && playgroundParticles.lifetimeColors.Count>0) {
                playgroundParticles.lifetimeColorId++;playgroundParticles.lifetimeColorId=playgroundParticles.lifetimeColorId%playgroundParticles.lifetimeColors.Count;
                playgroundParticles.playgroundCache.lifetimeColorId[p] = playgroundParticles.lifetimeColorId;
            }

            // Reset manipulators influence
            playgroundParticles.playgroundCache.changedByProperty[p] = false;
            playgroundParticles.playgroundCache.changedByPropertyColor[p] = false;
            playgroundParticles.playgroundCache.changedByPropertyColorLerp[p] = false;
            playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p] = false;
            playgroundParticles.playgroundCache.changedByPropertySize[p] = false;
            playgroundParticles.playgroundCache.changedByPropertyTarget[p] = false;
            playgroundParticles.playgroundCache.changedByPropertyDeath[p] = false;
            playgroundParticles.playgroundCache.propertyTarget[p] = 0;
            playgroundParticles.playgroundCache.propertyId[p] = 0;
            playgroundParticles.playgroundCache.propertyColorId[p] = 0;
            playgroundParticles.playgroundCache.manipulatorId[p] = 0;

            // Send birth event
            if (playgroundParticles.events.Count>0 && playgroundParticles.playgroundCache.rebirth[p])
                playgroundParticles.SendEvent(EVENTTYPEC.Birth, p);
            if (playgroundParticles.hasEventManipulatorLocal) {
                for (int i = 0; i<playgroundParticles.manipulators.Count; i++) {
                    if (playgroundParticles.manipulators[i].trackParticles &&
                        playgroundParticles.manipulators[i].sendEventBirth &&
                        playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.targetPosition[p], playgroundParticles.manipulators[i].transform.position)) {

                        playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p);
                        playgroundParticles.manipulators[i].SendParticleEventBirth();
                    }
                }
            }
            if (playgroundParticles.hasEventManipulatorGlobal) {
                for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) {
                    if (PlaygroundC.reference.manipulators[i].trackParticles &&
                        PlaygroundC.reference.manipulators[i].sendEventBirth &&
                        PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.targetPosition[p], PlaygroundC.reference.manipulators[i].transform.position)) {

                        playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p);
                        PlaygroundC.reference.manipulators[i].SendParticleEventBirth();
                    }
                }
            }
        }