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);
                    }
                });
            }
        }
        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);
                    }
                });
            }
        }
        // Set life and death of particles after emit has changed
        public static void SetParticleTimeNowWithRestEmission(PlaygroundParticlesC playgroundParticles)
        {
            float currentGlobalTime = PlaygroundC.globalTime;
            float currentTime = currentGlobalTime+playgroundParticles.lifetimeOffset;
            float emissionDelta = currentGlobalTime-playgroundParticles.emissionStopped;
            bool applyDelta = false;
            bool hasOverflow = playgroundParticles.source!=SOURCEC.Transform&&(playgroundParticles.overflowOffset!=Vector3.zero||playgroundParticles.applySourceScatter&&(playgroundParticles.sourceScatterMin!=Vector3.zero||playgroundParticles.sourceScatterMax!=Vector3.zero));
            if (playgroundParticles.loop && emissionDelta<playgroundParticles.lifetime && emissionDelta>0) {
                applyDelta = true;
                playgroundParticles.cameFromNonEmissionFrame = true;
            }
            for (int p = 0; p<playgroundParticles.particleCount; p++) {
                if (!applyDelta) {
                    if (playgroundParticles.sorting!=SORTINGC.Burst || playgroundParticles.sorting==SORTINGC.NearestNeighbor && hasOverflow || playgroundParticles.sorting==SORTINGC.NearestNeighborReversed && hasOverflow) {
                        playgroundParticles.playgroundCache.life[p] = playgroundParticles.lifetime-(playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeOffset[p]+.01f);
                        playgroundParticles.playgroundCache.birth[p] = currentTime-playgroundParticles.playgroundCache.life[p];
                        playgroundParticles.playgroundCache.death[p] = currentTime+(playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeOffset[p]);
                        if (!playgroundParticles.loop) {
                            playgroundParticles.InactivateParticle(p);
                        }
                    } else {
                        playgroundParticles.playgroundCache.life[p] = playgroundParticles.lifetime;
                        playgroundParticles.playgroundCache.birth[p] = currentTime-playgroundParticles.lifetime;
                        playgroundParticles.playgroundCache.death[p] = currentTime;
                    }
                    playgroundParticles.playgroundCache.birthDelay[p] = 0f;
                    playgroundParticles.playgroundCache.isNonBirthed[p] = true;
                    playgroundParticles.playgroundCache.isFirstLoop[p] = true;
                    playgroundParticles.playgroundCache.rebirth[p] = true;
                    playgroundParticles.playgroundCache.simulate[p] = true;
                } else {
                    playgroundParticles.playgroundCache.birthDelay[p] = emissionDelta;
                }

                playgroundParticles.playgroundCache.emission[p] = playgroundParticles.emit;
            }
        }