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; } }