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);
                    }
                });
            }
        }
        // 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();
                    }
                }
            }
        }