/// <summary> /// Sends the death events. /// </summary> /// <param name="playgroundParticles">Playground particles.</param> /// <param name="p">Particle index.</param> public static void SendDeathEvents(PlaygroundParticlesC playgroundParticles, int p) { if ((playgroundParticles.playgroundCache.life[p]>0||playgroundParticles.playgroundCache.changedByPropertyDeath[p]) && !playgroundParticles.playgroundCache.isNonBirthed[p]) { if (playgroundParticles.loop || (!playgroundParticles.loop && playgroundParticles.playgroundCache.isFirstLoop[p])) { playgroundParticles.SendEvent(EVENTTYPEC.Death, p); } } if (playgroundParticles.hasEventManipulatorLocal) { for (int i = 0; i<playgroundParticles.manipulators.Count; i++) { if (playgroundParticles.manipulators[i].trackParticles && playgroundParticles.manipulators[i].RemoveParticle (playgroundParticles.particleSystemId, p)) { if (playgroundParticles.manipulators[i].sendEventDeath) { playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p); playgroundParticles.manipulators[i].SendParticleEventDeath(); } } } } if (playgroundParticles.hasEventManipulatorGlobal) { for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) { if (PlaygroundC.reference.manipulators[i].trackParticles && PlaygroundC.reference.manipulators[i].RemoveParticle (playgroundParticles.particleSystemId, p)) { if (PlaygroundC.reference.manipulators[i].sendEventDeath) { playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p); PlaygroundC.reference.manipulators[i].SendParticleEventDeath(); } } } } playgroundParticles.playgroundCache.isNonBirthed[p] = true; }
public static void ThreadedCalculations(PlaygroundParticlesC playgroundParticles) { // Refresh delta time if (!playgroundParticles.isPrewarming) { playgroundParticles.localDeltaTime = (PlaygroundC.globalTime-playgroundParticles.lastTimeUpdated)*playgroundParticles.particleTimescale; playgroundParticles.localTime += playgroundParticles.localDeltaTime; playgroundParticles.lastTimeUpdated = PlaygroundC.globalTime; } // Set delta time playgroundParticles.t = playgroundParticles.localDeltaTime; if (playgroundParticles.particleCount<=0 || playgroundParticles.playgroundCache.color.Length!=playgroundParticles.particleCount || playgroundParticles.playgroundCache.targetPosition.Length!=playgroundParticles.particleCount || playgroundParticles.playgroundCache.targetDirection.Length!=playgroundParticles.particleCount || playgroundParticles.source==SOURCEC.State && playgroundParticles.states[playgroundParticles.activeState].IsInitializing() || playgroundParticles.isYieldRefreshing || !PlaygroundC.IsReady() || playgroundParticles.isSettingParticleTime) { playgroundParticles.isDoneThread = true; playgroundParticles.threadHadNoActiveParticles = false; return; } if (PlaygroundC.reference.calculate && playgroundParticles.calculate && !playgroundParticles.inTransition && playgroundParticles.hasActiveParticles) {} else if (playgroundParticles.source!=SOURCEC.Script) { playgroundParticles.isDoneThread = true; playgroundParticles.cameFromNonCalculatedFrame = true; return; } else { playgroundParticles.isDoneThread = true; return; } // Check that simplex turbulence is available (will be next frame in case not) if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning && playgroundParticles.turbulenceStrength>0 && playgroundParticles.turbulenceType!=TURBULENCETYPE.None) { if (playgroundParticles.turbulenceType==TURBULENCETYPE.Simplex && playgroundParticles.turbulenceSimplex==null) { playgroundParticles.isDoneThread = true; playgroundParticles.threadHadNoActiveParticles = false; return; } } float t = playgroundParticles.t; // Prepare variables for particle source positions Matrix4x4 fMx = new Matrix4x4(); if (playgroundParticles.source==SOURCEC.State || playgroundParticles.source==SOURCEC.WorldObject || playgroundParticles.source==SOURCEC.SkinnedWorldObject) { fMx.SetTRS(Vector3.zero, playgroundParticles.stRot, new Vector3(1f,1f,1f)); } bool noActiveParticles = true; if (playgroundParticles.source==SOURCEC.Transform) for (int i = 0; i<playgroundParticles.sourceTransforms.Count; i++) playgroundParticles.sourceTransforms[i].UpdateMatrix(); // Update skinned mesh vertices if (playgroundParticles.source==SOURCEC.SkinnedWorldObject && playgroundParticles.skinnedWorldObjectReady && !playgroundParticles.forceSkinnedMeshUpdateOnMainThread && PlaygroundC.reference.skinnedMeshThreadMethod==ThreadMethodComponent.InsideParticleCalculation) { playgroundParticles.skinnedWorldObject.Update(); } // Misc int pCount = playgroundParticles.particleCache.Length; // Check that cache is correct if (playgroundParticles.playgroundCache.lifetimeSubtraction.Length!=pCount) SetLifetimeSubtraction(playgroundParticles); if (playgroundParticles.playgroundCache.maskAlpha.Length!=pCount) { playgroundParticles.playgroundCache.maskAlpha = new float[pCount]; playgroundParticles.playgroundCache.isMasked = new bool[pCount]; } if (playgroundParticles.playgroundCache.manipulatorId.Length!=pCount) playgroundParticles.playgroundCache.manipulatorId = new int[pCount]; if (playgroundParticles.playgroundCache.excludeFromManipulatorId.Length!=pCount) playgroundParticles.playgroundCache.excludeFromManipulatorId = new int[pCount]; if (playgroundParticles.playgroundCache.noForce.Length!=pCount) playgroundParticles.playgroundCache.noForce = new bool[pCount]; if (playgroundParticles.playgroundCache.isNonBirthed.Length!=pCount) playgroundParticles.playgroundCache.isNonBirthed = new bool[pCount]; if (playgroundParticles.playgroundCache.isFirstLoop.Length!=pCount) playgroundParticles.playgroundCache.isFirstLoop = new bool[pCount]; if (playgroundParticles.playgroundCache.isCalculatedThisFrame.Length!=pCount) playgroundParticles.playgroundCache.isCalculatedThisFrame = new bool[pCount]; if (playgroundParticles.playgroundCache.simulate.Length!=pCount) { playgroundParticles.playgroundCache.simulate = new bool[pCount]; for (int p = 0; p<pCount; p++) playgroundParticles.playgroundCache.simulate[p] = true; } if (playgroundParticles.applyParticleMask && (playgroundParticles.playgroundCache.maskSorting==null || playgroundParticles.playgroundCache.maskSorting.Length!=pCount || playgroundParticles.particleMaskSorting!=playgroundParticles.previousMaskSorting)) playgroundParticles.RefreshMaskSorting(); Vector3 zero = Vector3.zero; Vector3 up = Vector3.up; float initYpos = PlaygroundC.initialTargetPosition.y; // Calculation loop for (int p = 0; p<playgroundParticles.particleCount; p++) { // Check that particle count is correct if (pCount != playgroundParticles.particleCache.Length || playgroundParticles.isYieldRefreshing || playgroundParticles.isSettingParticleTime || playgroundParticles.isSettingParticleCount) { playgroundParticles.cameFromNonEmissionFrame = false; playgroundParticles.isDoneThread = true; playgroundParticles.threadHadNoActiveParticles = false; return; } // Check simulation if (!playgroundParticles.playgroundCache.simulate[p]) { if (playgroundParticles.playgroundCache.rebirth[p] && playgroundParticles.loop) { playgroundParticles.InactivateParticle(p); } continue; } else { noActiveParticles = false; } // This particle is about to be calculated playgroundParticles.playgroundCache.isCalculatedThisFrame[p] = false; // Prepare variables inside scope Vector3 deltaVelocity; float lifeInSeconds = (playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-playgroundParticles.playgroundCache.lifetimeSubtraction[p]; float normalizedLife = Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/lifeInSeconds); float normalizedP = (p*1f)/(playgroundParticles.particleCount*1f); float lifetimePositioningTimeScale = 1f; if (playgroundParticles.applyLifetimePositioningTimeScale) lifetimePositioningTimeScale = playgroundParticles.lifetimePositioningTimeScale.Evaluate(normalizedLife); // Apply particle mask if (playgroundParticles.applyParticleMask) { int maskedP = playgroundParticles.playgroundCache.maskSorting[p]; if (p<playgroundParticles.particleMask) { if (playgroundParticles.playgroundCache.maskAlpha[maskedP]<=0 || playgroundParticles.particleMaskTime<=0) { playgroundParticles.playgroundCache.isMasked[maskedP] = true; playgroundParticles.playgroundCache.maskAlpha[maskedP] = 0; playgroundParticles.particleCache[maskedP].size = 0; } else { playgroundParticles.playgroundCache.maskAlpha[maskedP] -= (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime; } } else { if (playgroundParticles.playgroundCache.maskAlpha[maskedP]>=1f || playgroundParticles.particleMaskTime<=0) { playgroundParticles.playgroundCache.isMasked[maskedP] = false; playgroundParticles.playgroundCache.maskAlpha[maskedP] = 1f; } else { playgroundParticles.playgroundCache.maskAlpha[maskedP] += (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime; } } } else { playgroundParticles.playgroundCache.isMasked[p] = false; playgroundParticles.playgroundCache.maskAlpha[p] = 1f; } Color32 lifetimeColor = playgroundParticles.GetParticleColor(p, normalizedLife, normalizedP); // Assign color to particle playgroundParticles.particleCache[p].color = lifetimeColor; // Give Playground Cache its color value playgroundParticles.playgroundCache.color[p] = lifetimeColor; // Source positions if (playgroundParticles.emit) { if (!playgroundParticles.syncPositionsOnMainThread) playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; switch (playgroundParticles.source) { case SOURCEC.State: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.states[playgroundParticles.activeState].GetNormal(p))); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.states[playgroundParticles.activeState].GetNormal(p))); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife); } playgroundParticles.SetSourcePosition(p); // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; case SOURCEC.Transform: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) if (playgroundParticles.localSpace && playgroundParticles.transformIndex!=playgroundParticles.psTransformNum) playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.particleSystemInverseRotation*playgroundParticles.sourceTransforms[playgroundParticles.transformIndex].rotation*playgroundParticles.playgroundCache.initialLocalVelocity[p]; else playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.sourceTransforms[playgroundParticles.transformIndex].rotation*playgroundParticles.playgroundCache.initialLocalVelocity[p]; if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.sourceTransforms[playgroundParticles.transformIndex].rotation*playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife*lifetimePositioningTimeScale); } playgroundParticles.SetSourcePosition(p); // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; case SOURCEC.WorldObject: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length])); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length])); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife); } playgroundParticles.SetSourcePosition(p); // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = (playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]); } break; case SOURCEC.SkinnedWorldObject: if (playgroundParticles.skinnedWorldObjectReady) { if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.skinnedWorldObject.normals[p%playgroundParticles.skinnedWorldObject.normals.Length])); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.skinnedWorldObject.normals[p%playgroundParticles.skinnedWorldObject.normals.Length])); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife); } playgroundParticles.SetSourcePosition(p); // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } } break; case SOURCEC.Projection: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.projection.GetNormal(p), playgroundParticles.playgroundCache.initialLocalVelocity[p]); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.projection.GetNormal(p), playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale)); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife); } playgroundParticles.SetSourcePosition(p); } break; case SOURCEC.Paint: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.paint.GetRotation(p)*Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.paint.GetNormal(p)); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.paint.GetRotation(p)*Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.paint.GetNormal(p)); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife); } playgroundParticles.SetSourcePosition(p); // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; case SOURCEC.Spline: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { if (playgroundParticles.treatAsOneSpline) playgroundParticles.splineIndex = (int)((((p*1f)/(pCount*1f))*playgroundParticles.splines.Count)+playgroundParticles.splineTimeOffset)%playgroundParticles.splines.Count; else playgroundParticles.splineIndex = p%playgroundParticles.splines.Count; float pSplineTime; if (playgroundParticles.treatAsOneSpline) pSplineTime = ((p*playgroundParticles.splines.Count*1f) / (pCount*1f))+playgroundParticles.splineTimeOffset; else pSplineTime = ((p*1f) / (pCount*1f)) + playgroundParticles.splineTimeOffset; if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.splines[playgroundParticles.splineIndex].GetDirection(pSplineTime), playgroundParticles.playgroundCache.initialLocalVelocity[p]); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.splines[playgroundParticles.splineIndex].GetDirection(pSplineTime), playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale)); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife*lifetimePositioningTimeScale); } playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.splines[playgroundParticles.splineIndex].GetPoint(pSplineTime); if (playgroundParticles.applySourceScatter) playgroundParticles.playgroundCache.targetPosition[p] += Vector3.Scale (playgroundParticles.playgroundCache.scatterPosition[p],playgroundParticles.scatterScale); // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; } } // Set initial particle values if life is 0 if (playgroundParticles.playgroundCache.life[p]==0) { if (!playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]; // Apply birth position delta if (playgroundParticles.applyDeltaOnRebirth && !playgroundParticles.cameFromNonEmissionFrame && playgroundParticles.localTime-playgroundParticles.emissionStopped > playgroundParticles.lifetime && playgroundParticles.playgroundCache.birthDelay[p]==0 && !playgroundParticles.onlySourcePositioning) { float timeDelta = playgroundParticles.localTime-playgroundParticles.playgroundCache.birth[p]; if (playgroundParticles.isPrewarming) timeDelta *=.5f; playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.velocity[p]*playgroundParticles.velocityScale*timeDelta; } } playgroundParticles.playgroundCache.initialColor[p] = lifetimeColor; // Delta movement velocity if (!playgroundParticles.cameFromNonEmissionFrame && playgroundParticles.calculateDeltaMovement && !playgroundParticles.isPrewarming && playgroundParticles.source!=SOURCEC.Script) { deltaVelocity = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; playgroundParticles.playgroundCache.velocity[p] += (deltaVelocity*playgroundParticles.deltaMovementStrength)/playgroundParticles.realSimulationTime; } playgroundParticles.playgroundCache.birthDelay[p] = 0; } if (playgroundParticles.playgroundCache.birth[p]>playgroundParticles.localTime) { playgroundParticles.particleCache[p].size = 0; playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; } // Particle lifetime, velocity and manipulators if (playgroundParticles.playgroundCache.rebirth[p]) { // Particle is alive if (playgroundParticles.playgroundCache.birth[p]+playgroundParticles.playgroundCache.life[p]<=playgroundParticles.localTime+lifeInSeconds && (!playgroundParticles.playgroundCache.isNonBirthed[p] || playgroundParticles.onlyLifetimePositioning || playgroundParticles.onlySourcePositioning)) { // Lifetime size if (!playgroundParticles.playgroundCache.changedByPropertySize[p]) { if (playgroundParticles.applyLifetimeSize && !playgroundParticles.applyParticleArraySize) playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(normalizedLife)*playgroundParticles.scale; else if (playgroundParticles.applyLifetimeSize && playgroundParticles.applyParticleArraySize) playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(normalizedLife)*playgroundParticles.particleArraySize.Evaluate((p*1f)/(playgroundParticles.particleCount*1f))*playgroundParticles.scale; else if (playgroundParticles.applyParticleArraySize) playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.particleArraySize.Evaluate((p*1f)/(playgroundParticles.particleCount*1f))*playgroundParticles.scale; else playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.scale; } if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning) { if (!playgroundParticles.playgroundCache.noForce[p] && playgroundParticles.playgroundCache.life[p]>0) { // Velocity bending if (playgroundParticles.applyVelocityBending) { if (playgroundParticles.velocityBendingType==VELOCITYBENDINGTYPEC.SourcePosition) { playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect( new Vector3( playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x, playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y, playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z ), (playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.position[p]).normalized )*t; } else { playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect( new Vector3( playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x, playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y, playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z ), (playgroundParticles.playgroundCache.previousParticlePosition[p]-playgroundParticles.playgroundCache.position[p]).normalized )*t; } } // Set previous target position (used by delta velocity & local space movement compensation) playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; // Gravity if (playgroundParticles.localSpace && playgroundParticles.source==SOURCEC.Transform && playgroundParticles.transformIndex!=playgroundParticles.psTransformNum) playgroundParticles.playgroundCache.velocity[p] -= playgroundParticles.sourceTransforms[playgroundParticles.transformIndex].rotation*playgroundParticles.gravity*t; else playgroundParticles.playgroundCache.velocity[p] -= playgroundParticles.gravity*t; // Lifetime additive velocity if (playgroundParticles.applyLifetimeVelocity) playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.lifetimeVelocity.Evaluate(normalizedLife, playgroundParticles.lifetimeVelocityScale)*t; // Turbulence inside the calculation loop if (playgroundParticles.HasTurbulence() && PlaygroundC.reference.turbulenceThreadMethod==ThreadMethodComponent.InsideParticleCalculation) { if (!playgroundParticles.playgroundCache.noForce[p]) Turbulence(playgroundParticles, playgroundParticles.turbulenceSimplex, p, playgroundParticles.t, playgroundParticles.turbulenceType, playgroundParticles.turbulenceTimeScale, playgroundParticles.turbulenceScale/playgroundParticles.velocityScale, playgroundParticles.turbulenceStrength, playgroundParticles.turbulenceApplyLifetimeStrength, playgroundParticles.turbulenceLifetimeStrength); } // Damping, max velocity, constraints and final positioning // Max velocity if (playgroundParticles.playgroundCache.velocity[p].sqrMagnitude>playgroundParticles.maxVelocity) playgroundParticles.playgroundCache.velocity[p] = Vector3.ClampMagnitude(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.maxVelocity); // Damping if (playgroundParticles.damping>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], zero, playgroundParticles.damping*t); // Transition back to source if (playgroundParticles.transitionBackToSource) { float transitionAmount = playgroundParticles.transitionBackToSourceAmount.Evaluate(normalizedLife)*playgroundParticles.particleTimescale; playgroundParticles.playgroundCache.position[p] = Vector3.Lerp(playgroundParticles.playgroundCache.position[p], playgroundParticles.playgroundCache.targetPosition[p], transitionAmount); playgroundParticles.playgroundCache.velocity[p] *= 1f-transitionAmount; } // Axis constraints if (playgroundParticles.axisConstraints.x) playgroundParticles.playgroundCache.velocity[p].x = 0; if (playgroundParticles.axisConstraints.y) playgroundParticles.playgroundCache.velocity[p].y = 0; if (playgroundParticles.axisConstraints.z) playgroundParticles.playgroundCache.velocity[p].z = 0; // Set calculated collision position playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.position[p]; // Relocate playgroundParticles.playgroundCache.position[p] += (playgroundParticles.playgroundCache.velocity[p]*playgroundParticles.velocityScale)*t; if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) { if (!playgroundParticles.applyMovementCompensationLifetimeStrength) playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p]; else playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p]*playgroundParticles.movementCompensationLifetimeStrength.Evaluate(normalizedLife); } // Set particle velocity to be able to stretch towards movement if (playgroundParticles.renderModeStretch && playgroundParticles.realSimulationTime>0) { if (playgroundParticles.applyLifetimeStretching) { playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p])/playgroundParticles.realSimulationTime, t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/lifeInSeconds); } else { if (playgroundParticles.stretchSpeed>0) playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p])/playgroundParticles.realSimulationTime, t*playgroundParticles.stretchSpeed); else playgroundParticles.particleCache[p].velocity = playgroundParticles.stretchStartDirection; } } } } else { // Only Source Positioning / Lifetime Positioning if (playgroundParticles.onlyLifetimePositioning && !playgroundParticles.onlySourcePositioning) { if (!playgroundParticles.playgroundCache.changedByPropertyTarget[p]) { // Lifetime Positioning by Vector3 Animation Curve if (playgroundParticles.lifetimePositioningUsesSourceDirection && playgroundParticles.source!=SOURCEC.Script) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+(playgroundParticles.playgroundCache.targetDirection[p]); } else { if (!playgroundParticles.applyLifetimePositioningPositionScale) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+ playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale); } else { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+ playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale)* playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife*lifetimePositioningTimeScale); } } } } else if (playgroundParticles.source!=SOURCEC.Script && !playgroundParticles.playgroundCache.isNonBirthed[p]) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]; } // Set particle velocity to be able to stretch towards movement if (playgroundParticles.renderModeStretch && playgroundParticles.realSimulationTime>0) { if (playgroundParticles.applyLifetimeStretching) { playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p])/playgroundParticles.realSimulationTime, t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/lifeInSeconds); } else { if (playgroundParticles.stretchSpeed>0) playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p])/playgroundParticles.realSimulationTime, t*playgroundParticles.stretchSpeed); else playgroundParticles.particleCache[p].velocity = playgroundParticles.stretchStartDirection; } } playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; } // Rotation if (t!=0) { if (!playgroundParticles.rotateTowardsDirection) playgroundParticles.playgroundCache.rotation[p] += playgroundParticles.playgroundCache.rotationSpeed[p]*t; else if (playgroundParticles.playgroundCache.life[p]!=0) { playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]+SignedAngle( up, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], playgroundParticles.rotationNormal ); } } if (!playgroundParticles.syncPositionsOnMainThread && playgroundParticles.playgroundCache.life[p]>0) playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p]; // Set previous particle position playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.position[p]; // Send timed event if (playgroundParticles.hasTimerEvent) playgroundParticles.SendEvent(EVENTTYPEC.Time, p); } else { playgroundParticles.particleCache[p].size = 0; playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; } // Calculate lifetime float evaluatedLife = (playgroundParticles.localTime-playgroundParticles.playgroundCache.birth[p])/lifeInSeconds; // Lifetime if (playgroundParticles.playgroundCache.life[p]<playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p]) { playgroundParticles.playgroundCache.life[p] = lifeInSeconds*evaluatedLife; if (!playgroundParticles.syncPositionsOnMainThread) playgroundParticles.particleCache[p].lifetime = Mathf.Clamp (lifeInSeconds*(1f-evaluatedLife), playgroundParticles.minShurikenLifetime, playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p]); if (playgroundParticles.lifetimeValueMethod==VALUEMETHOD.RandomBetweenTwoValues && playgroundParticles.playgroundCache.life[p]>lifeInSeconds) { // Send death event for particles with lifetime subtraction if ((playgroundParticles.hasEvent||playgroundParticles.hasEventManipulatorGlobal||playgroundParticles.hasEventManipulatorLocal) && !playgroundParticles.playgroundCache.isNonBirthed[p]) SendDeathEvents(playgroundParticles, p); playgroundParticles.particleCache[p].size = 0; playgroundParticles.particleCache[p].velocity = zero; playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; } } else { // Particle exceeded with death property if (!playgroundParticles.loop && !playgroundParticles.playgroundCache.isNonBirthed[p]) { // Send death event for particles which died of unnatural cause such as property death, the worst type of death if (playgroundParticles.hasEvent||playgroundParticles.hasEventManipulatorGlobal||playgroundParticles.hasEventManipulatorLocal) SendDeathEvents(playgroundParticles, p); playgroundParticles.InactivateParticle(p); continue; } // Loop exceeded normally if (!playgroundParticles.loop && playgroundParticles.localTime>playgroundParticles.simulationStarted+(playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-.01f) { playgroundParticles.loopExceeded = true; if (playgroundParticles.loopExceededOnParticle==p && evaluatedLife>2f) { if (playgroundParticles.disableOnDone) playgroundParticles.queueEmissionHalt = true; playgroundParticles.threadHadNoActiveParticles = true; playgroundParticles.hasActiveParticles = false; } if (playgroundParticles.loopExceededOnParticle==-1) playgroundParticles.loopExceededOnParticle = p; } playgroundParticles.particleCache[p].velocity = zero; // Send death event for particles with full lifetime length if ((playgroundParticles.hasEvent||playgroundParticles.hasEventManipulatorGlobal||playgroundParticles.hasEventManipulatorLocal) && !playgroundParticles.playgroundCache.isNonBirthed[p]) SendDeathEvents(playgroundParticles, p); // New cycle begins if (playgroundParticles.localTime>=playgroundParticles.playgroundCache.birth[p]+playgroundParticles.playgroundCache.birthDelay[p] && !playgroundParticles.loopExceeded && playgroundParticles.source!=SOURCEC.Script && playgroundParticles.emit) { if (!playgroundParticles.playgroundCache.changedByPropertyDeath[p] || playgroundParticles.playgroundCache.changedByPropertyDeath[p] && playgroundParticles.localTime>playgroundParticles.playgroundCache.death[p]) { Rebirth(playgroundParticles, p, playgroundParticles.internalRandom01); } else { playgroundParticles.particleCache[p].velocity = zero; playgroundParticles.particleCache[p].size = 0; playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; } } else { playgroundParticles.InactivateParticle(p); continue; } } // Local Manipulators for (int m = 0; m<playgroundParticles.manipulators.Count; m++) { CalculateManipulator(playgroundParticles, playgroundParticles.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], (playgroundParticles.localSpace?playgroundParticles.manipulators[m].transform.localPosition:playgroundParticles.manipulators[m].transform.position)+playgroundParticles.manipulatorFix, playgroundParticles.localSpace); } // Global Manipulators if (playgroundParticles.hasGlobalAffectingManipulators) { for (int m = 0; m<PlaygroundC.reference.manipulators.Count; m++) { if ((PlaygroundC.reference.manipulators[m].affects.value & 1<<playgroundParticles.GetLayer())!=0) CalculateManipulator(playgroundParticles, PlaygroundC.reference.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], (playgroundParticles.localSpace?PlaygroundC.reference.manipulators[m].transform.localPosition:PlaygroundC.reference.manipulators[m].transform.position)+playgroundParticles.manipulatorFix, playgroundParticles.localSpace); } } // Set particle size if (!playgroundParticles.syncPositionsOnMainThread) playgroundParticles.particleCache[p].size = (playgroundParticles.playgroundCache.maskAlpha[p]>0&&playgroundParticles.particleCache[p].position.y!=initYpos)?playgroundParticles.playgroundCache.size[p]:0; } else { // Particle is set to not rebirth playgroundParticles.InactivateParticle(p); } // Set particle position if no sync if (!playgroundParticles.syncPositionsOnMainThread) { playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.position[p]; } // Particle got calculated playgroundParticles.playgroundCache.isCalculatedThisFrame[p] = true; }// <- Particle loop ends here // Reset for next frame if (playgroundParticles.isSettingParticleTime) playgroundParticles.threadHadNoActiveParticles = false; else playgroundParticles.threadHadNoActiveParticles = noActiveParticles && !playgroundParticles.emit || noActiveParticles && !playgroundParticles.loop; playgroundParticles.cameFromNonEmissionFrame = false; playgroundParticles.isDoneThread = true; // Turbulence calculated here when using PlaygroundC.turbulenceThreadMethod of ThreadMethodComponent.OnePerSystem if (playgroundParticles.HasTurbulence() && PlaygroundC.reference.turbulenceThreadMethod==ThreadMethodComponent.OnePerSystem) { PlaygroundC.RunAsync(()=>{ for (int p = 0; p<playgroundParticles.particleCount; p++) { if (!playgroundParticles.playgroundCache.noForce[p]) Turbulence(playgroundParticles, playgroundParticles.turbulenceSimplex, p, playgroundParticles.t, playgroundParticles.turbulenceType, playgroundParticles.turbulenceTimeScale, playgroundParticles.turbulenceScale/playgroundParticles.velocityScale, playgroundParticles.turbulenceStrength, playgroundParticles.turbulenceApplyLifetimeStrength, playgroundParticles.turbulenceLifetimeStrength); } }); } }
/// <summary> /// Calculates particle collisions, this runs automatically if collision is set to true. (Must currently run on main-thread due to the Physics.Raycast dependency.) /// </summary> /// <param name="playgroundParticles">Particle Playground system.</param> public static void Collisions(PlaygroundParticlesC playgroundParticles) { if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning && playgroundParticles.collisionRadius>0) { Ray ray = new Ray(); float distance; bool is3d = playgroundParticles.collisionType==COLLISIONTYPEC.Physics3D; bool hasCollisionExclusion = playgroundParticles.collisionExclusion!=null && playgroundParticles.collisionExclusion.Count>0; RaycastHit hitInfo; RaycastHit2D hitInfo2D; bool hasEvents = playgroundParticles.events.Count>0; Vector3 preCollisionVelocity; // Prepare the infinite collision planes if (playgroundParticles.collision && playgroundParticles.colliders.Count>0) { for (int c = 0; c<playgroundParticles.colliders.Count; c++) { playgroundParticles.colliders[c].UpdatePlane(); } } // Check cache length if (playgroundParticles.playgroundCache.noForce.Length!=playgroundParticles.particleCount) playgroundParticles.playgroundCache.noForce = new bool[playgroundParticles.particleCount]; if (playgroundParticles.playgroundCache.lifetimeSubtraction.Length!=playgroundParticles.particleCount) playgroundParticles.playgroundCache.lifetimeSubtraction = new float[playgroundParticles.particleCount]; for (int p = 0; p<playgroundParticles.particleCount; p++) { if (playgroundParticles.playgroundCache.life[p]==0 || playgroundParticles.playgroundCache.life[p]>=(playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-playgroundParticles.playgroundCache.lifetimeSubtraction[p] || playgroundParticles.playgroundCache.noForce[p]) continue; // Playground Plane colliders (never exceed these) for (int c = 0; c<playgroundParticles.colliders.Count; c++) { if (playgroundParticles.colliders[c].enabled && playgroundParticles.colliders[c].transform && !playgroundParticles.colliders[c].plane.GetSide(playgroundParticles.playgroundCache.position[p])) { // Set particle to location ray.origin = playgroundParticles.playgroundCache.position[p]; ray.direction = playgroundParticles.colliders[c].plane.normal; if (playgroundParticles.colliders[c].plane.Raycast(ray, out distance)) playgroundParticles.playgroundCache.position[p] = ray.GetPoint(distance); // Store velocity before collision preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p]; // Reflect particle playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.colliders[c].plane.normal+RandomVector3(playgroundParticles.internalRandom01, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness; // Apply lifetime loss if (playgroundParticles.lifetimeLoss>0) { playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss); playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true; } // Send event if (hasEvents) playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, playgroundParticles.colliders[c].transform); } } // Colliders in scene if (playgroundParticles.playgroundCache.velocity[p].magnitude>PlaygroundC.collisionSleepVelocity) { // Collide by checking for potential passed collider in the direction of this particle's velocity from the previous frame if (is3d) { if (Physics.Raycast( playgroundParticles.playgroundCache.collisionParticlePosition[p], (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p]).normalized, out hitInfo, (Vector3.SqrMagnitude(playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p])+playgroundParticles.collisionRadius)*2f, playgroundParticles.collisionMask)) { // Check that this object isn't excluded if (hasCollisionExclusion) { if (playgroundParticles.collisionExclusion.Contains(hitInfo.transform)) continue; } // Set particle to location playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.collisionParticlePosition[p]; // Store velocity before collision preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p]; // Reflect particle playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], hitInfo.normal+RandomVector3(playgroundParticles.internalRandom01, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness; // Apply lifetime loss if (playgroundParticles.lifetimeLoss>0) { playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss); playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true; } // Add force to rigidbody if (playgroundParticles.affectRigidbodies && hitInfo.rigidbody) hitInfo.rigidbody.AddForceAtPosition((playgroundParticles.inverseRigidbodyCollision?-preCollisionVelocity:preCollisionVelocity)*playgroundParticles.mass, playgroundParticles.playgroundCache.position[p]); // Send event if (hasEvents) playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, hitInfo.transform, hitInfo.collider); if (playgroundParticles.hasEventManipulatorLocal) { for (int i = 0; i<playgroundParticles.manipulators.Count; i++) { if (playgroundParticles.manipulators[i].trackParticles && playgroundParticles.manipulators[i].sendEventCollision && playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], playgroundParticles.manipulators[i].transform.position)) { playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p); playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider = hitInfo.collider; playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo.point; playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo.transform; playgroundParticles.manipulators[i].SendParticleEventCollision(); } } } if (playgroundParticles.hasEventManipulatorGlobal) { for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) { if (PlaygroundC.reference.manipulators[i].trackParticles && PlaygroundC.reference.manipulators[i].sendEventCollision && PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], PlaygroundC.reference.manipulators[i].transform.position)) { playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p); playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider = hitInfo.collider; playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo.point; playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo.transform; PlaygroundC.reference.manipulators[i].SendParticleEventCollision(); } } } } } else { hitInfo2D = Physics2D.Raycast( playgroundParticles.playgroundCache.collisionParticlePosition[p], (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p]).normalized, (Vector3.SqrMagnitude(playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p])+playgroundParticles.collisionRadius)*2f, playgroundParticles.collisionMask, playgroundParticles.minCollisionDepth, playgroundParticles.maxCollisionDepth ); if (hitInfo2D.collider!=null) { // Check that this object isn't excluded if (hasCollisionExclusion) { if (playgroundParticles.collisionExclusion.Contains(hitInfo2D.transform)) continue; } // Set particle to location playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.collisionParticlePosition[p]; // Store velocity before collision preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p]; // Reflect particle playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], (Vector3)hitInfo2D.normal+RandomVector3(playgroundParticles.internalRandom01, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness; // Apply lifetime loss if (playgroundParticles.lifetimeLoss>0) { playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss); playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true; } // Add force to rigidbody if (playgroundParticles.affectRigidbodies && hitInfo2D.rigidbody) hitInfo2D.rigidbody.AddForceAtPosition((playgroundParticles.inverseRigidbodyCollision?-preCollisionVelocity:preCollisionVelocity)*playgroundParticles.mass, playgroundParticles.playgroundCache.position[p]); // Send event if (hasEvents) playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, hitInfo2D.transform, hitInfo2D.collider); if (playgroundParticles.hasEventManipulatorLocal) { for (int i = 0; i<playgroundParticles.manipulators.Count; i++) { if (playgroundParticles.manipulators[i].trackParticles && playgroundParticles.manipulators[i].sendEventCollision && playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], playgroundParticles.manipulators[i].transform.position)) { playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p); playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider2D = hitInfo2D.collider; playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo2D.point; playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo2D.transform; playgroundParticles.manipulators[i].SendParticleEventCollision(); } } } if (playgroundParticles.hasEventManipulatorGlobal) { for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) { if (PlaygroundC.reference.manipulators[i].trackParticles && PlaygroundC.reference.manipulators[i].sendEventCollision && PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], PlaygroundC.reference.manipulators[i].transform.position)) { playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p); playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider2D = hitInfo2D.collider; playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo2D.point; playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo2D.transform; PlaygroundC.reference.manipulators[i].SendParticleEventCollision(); } } } } } } else { playgroundParticles.playgroundCache.velocity[p] = Vector3.zero; } } } }
// Rebirth of a specified particle public static void Rebirth(PlaygroundParticlesC playgroundParticles, int p, System.Random random) { if (!playgroundParticles.hasActiveParticles) return; // Set initial values playgroundParticles.playgroundCache.rebirth[p] = playgroundParticles.source==SOURCEC.Script?true:(playgroundParticles.emit && (playgroundParticles.loop || playgroundParticles.playgroundCache.isNonBirthed[p]) && playgroundParticles.playgroundCache.emission[p]); playgroundParticles.playgroundCache.isFirstLoop[p] = playgroundParticles.playgroundCache.isNonBirthed[p]; playgroundParticles.playgroundCache.isNonBirthed[p] = false; playgroundParticles.playgroundCache.life[p] = 0f; playgroundParticles.playgroundCache.birth[p] = playgroundParticles.playgroundCache.death[p]; playgroundParticles.playgroundCache.death[p] += playgroundParticles.lifetime; playgroundParticles.playgroundCache.velocity[p] = Vector3.zero; playgroundParticles.playgroundCache.noForce[p] = false; // Reset manipulators influence playgroundParticles.playgroundCache.changedByProperty[p] = false; playgroundParticles.playgroundCache.changedByPropertyColor[p] = false; playgroundParticles.playgroundCache.changedByPropertyColorLerp[p] = false; playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p] = false; playgroundParticles.playgroundCache.changedByPropertySize[p] = false; playgroundParticles.playgroundCache.changedByPropertyTarget[p] = false; playgroundParticles.playgroundCache.changedByPropertyDeath[p] = false; playgroundParticles.playgroundCache.propertyTarget[p] = 0; playgroundParticles.playgroundCache.propertyId[p] = 0; playgroundParticles.playgroundCache.propertyColorId[p] = 0; playgroundParticles.playgroundCache.manipulatorId[p] = 0; // Set new random size if (playgroundParticles.applyRandomSizeOnRebirth) playgroundParticles.playgroundCache.initialSize[p] = RandomRange(random, playgroundParticles.sizeMin, playgroundParticles.sizeMax); // Initial velocity if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning) { // Initial global velocity if (playgroundParticles.applyInitialVelocity) { if (playgroundParticles.applyRandomInitialVelocityOnRebirth) { if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.Spherical) playgroundParticles.playgroundCache.initialVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialVelocityMin.x, playgroundParticles.initialVelocityMax.x); else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.SphericalLinear) playgroundParticles.playgroundCache.initialVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialVelocityMin.x, playgroundParticles.initialVelocityMax.x, (p*1f)/(playgroundParticles.particleCount*1f)); else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.RectangularLinear) playgroundParticles.playgroundCache.initialVelocity[p] = Vector3.Lerp (playgroundParticles.initialVelocityMin, playgroundParticles.initialVelocityMax, (p*1f)/(playgroundParticles.particleCount*1f)); //else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.SphericalSector) // playgroundParticles.playgroundCache.initialVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialVelocityMin.x, playgroundParticles.initialVelocityMax.x, playgroundParticles.initialVelocityMin.y, playgroundParticles.initialVelocityMax.y); //else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.SphericalSectorLinear) // playgroundParticles.playgroundCache.initialVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialVelocityMin.x, playgroundParticles.initialVelocityMax.x, playgroundParticles.initialVelocityMin.y, playgroundParticles.initialVelocityMax.y, (p*1f)/(playgroundParticles.particleCount*1f)); else playgroundParticles.playgroundCache.initialVelocity[p] = RandomRange(random, playgroundParticles.initialVelocityMin, playgroundParticles.initialVelocityMax); } playgroundParticles.playgroundCache.velocity[p] = playgroundParticles.playgroundCache.initialVelocity[p]; // Give this spawning particle its velocity shape if (playgroundParticles.applyInitialVelocityShape) playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f), playgroundParticles.initialVelocityShapeScale)); } // Initial local velocity if (playgroundParticles.applyInitialLocalVelocity && playgroundParticles.source!=SOURCEC.Script) { if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.Spherical) playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialLocalVelocityMin.x, playgroundParticles.initialLocalVelocityMax.x); else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.SphericalLinear) playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialLocalVelocityMin.x, playgroundParticles.initialLocalVelocityMax.x, (p*1f)/(playgroundParticles.particleCount*1f)); else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.RectangularLinear) playgroundParticles.playgroundCache.initialLocalVelocity[p] = Vector3.Lerp (playgroundParticles.initialLocalVelocityMin, playgroundParticles.initialLocalVelocityMax, (p*1f)/(playgroundParticles.particleCount*1f)); //else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.SphericalSector) // playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialLocalVelocityMin.x, playgroundParticles.initialLocalVelocityMax.x, playgroundParticles.initialLocalVelocityMin.y, playgroundParticles.initialLocalVelocityMax.y); //else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.SphericalSectorLinear) // playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRangeSpherical(random, playgroundParticles.initialLocalVelocityMin.x, playgroundParticles.initialLocalVelocityMax.x, playgroundParticles.initialLocalVelocityMin.y, playgroundParticles.initialLocalVelocityMax.y, (p*1f)/(playgroundParticles.particleCount*1f)); else playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRange(random, playgroundParticles.initialLocalVelocityMin, playgroundParticles.initialLocalVelocityMax); playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.playgroundCache.targetDirection[p]; // Give this spawning particle its local velocity shape if (playgroundParticles.applyInitialVelocityShape) playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f), playgroundParticles.initialVelocityShapeScale)); } // Initial stretch playgroundParticles.particleCache[p].velocity = playgroundParticles.renderModeStretch&&playgroundParticles.applyStretchStartDirection?playgroundParticles.stretchStartDirection:Vector3.zero; } if (playgroundParticles.source==SOURCEC.Script) { // Velocity for script mode if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.scriptedEmissionVelocity; playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.scriptedEmissionPosition; playgroundParticles.particleCache[p].velocity = playgroundParticles.renderModeStretch&&playgroundParticles.applyStretchStartDirection?playgroundParticles.stretchStartDirection:Vector3.zero; } // Set new random lifetime if (playgroundParticles.scriptedLifetime==0) { if (playgroundParticles.applyRandomLifetimeOnRebirth) { if (playgroundParticles.lifetimeValueMethod==VALUEMETHOD.Constant) { playgroundParticles.playgroundCache.lifetimeSubtraction[p] = 0; } else { playgroundParticles.playgroundCache.lifetimeSubtraction[p] = playgroundParticles.lifetime-RandomRange (random, playgroundParticles.lifetimeMin, playgroundParticles.lifetime); } } // Set shuriken particles lifetime if (!playgroundParticles.syncPositionsOnMainThread) playgroundParticles.particleCache[p].lifetime = playgroundParticles.lifetime; playgroundParticles.particleCache[p].startLifetime = playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p]; } else { playgroundParticles.playgroundCache.lifetimeSubtraction[p] = 0; if (!playgroundParticles.syncPositionsOnMainThread) playgroundParticles.particleCache[p].lifetime = playgroundParticles.scriptedLifetime; playgroundParticles.particleCache[p].startLifetime = playgroundParticles.scriptedLifetime; } if (playgroundParticles.playgroundCache.rebirth[p]) { playgroundParticles.particleCache[p].color = playgroundParticles.GetParticleColor(p, 0f, (p*1f)/(playgroundParticles.particleCount*1f)); // Source Scattering if (playgroundParticles.applySourceScatter && playgroundParticles.source!=SOURCEC.Script) { if (playgroundParticles.playgroundCache.scatterPosition[p]==Vector3.zero || playgroundParticles.applyRandomScatterOnRebirth) { if (playgroundParticles.sourceScatterMethod==MINMAXVECTOR3METHOD.Rectangular) playgroundParticles.playgroundCache.scatterPosition[p] = RandomRange(random, playgroundParticles.sourceScatterMin, playgroundParticles.sourceScatterMax); else if (playgroundParticles.sourceScatterMethod==MINMAXVECTOR3METHOD.RectangularLinear) playgroundParticles.playgroundCache.scatterPosition[p] = Vector3.Lerp (playgroundParticles.sourceScatterMin, playgroundParticles.sourceScatterMax, (p*1f)/(playgroundParticles.particleCount*1f)); else if (playgroundParticles.sourceScatterMethod==MINMAXVECTOR3METHOD.SphericalLinear) playgroundParticles.playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, playgroundParticles.sourceScatterMin.x, playgroundParticles.sourceScatterMax.x, (p*1f)/(playgroundParticles.particleCount*1f)); //else if (playgroundParticles.sourceScatterMethod==MINMAXVECTOR3METHOD.SphericalSector) // playgroundParticles.playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, playgroundParticles.sourceScatterMin.x, playgroundParticles.sourceScatterMax.x, playgroundParticles.sourceScatterMin.y, playgroundParticles.sourceScatterMax.y); //else if (playgroundParticles.sourceScatterMethod==MINMAXVECTOR3METHOD.SphericalSectorLinear) // playgroundParticles.playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, playgroundParticles.sourceScatterMin.x, playgroundParticles.sourceScatterMax.x, playgroundParticles.sourceScatterMin.y, playgroundParticles.sourceScatterMax.y, (p*1f)/(playgroundParticles.particleCount*1f)); else playgroundParticles.playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, playgroundParticles.sourceScatterMin.x, playgroundParticles.sourceScatterMax.x); } } else playgroundParticles.playgroundCache.scatterPosition[p] = Vector3.zero; if (!playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (!playgroundParticles.syncPositionsOnMainThread) playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; } else if (!playgroundParticles.onlySourcePositioning) { // Lifetime Positioning by Vector3 Animation Curve if (playgroundParticles.lifetimePositioningUsesSourceDirection && playgroundParticles.source!=SOURCEC.Script) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+(playgroundParticles.playgroundCache.targetDirection[p]); } else { if (!playgroundParticles.applyLifetimePositioningPositionScale) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+ playgroundParticles.lifetimePositioning.Evaluate(0, playgroundParticles.lifetimePositioningScale); } else { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+ playgroundParticles.lifetimePositioning.Evaluate(0, playgroundParticles.lifetimePositioningScale)* playgroundParticles.lifetimePositioningPositionScale.Evaluate(0); } } if (!playgroundParticles.syncPositionsOnMainThread) playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; } if (playgroundParticles.applyInitialColorOnRebirth) { playgroundParticles.particleCache[p].color = playgroundParticles.playgroundCache.initialColor[p]; playgroundParticles.playgroundCache.color[p] = playgroundParticles.playgroundCache.initialColor[p]; } } else { playgroundParticles.particleCache[p].position = PlaygroundC.initialTargetPosition; } // Set new random rotation if (playgroundParticles.applyRandomRotationOnRebirth && !playgroundParticles.rotateTowardsDirection) playgroundParticles.playgroundCache.initialRotation[p] = RandomRange(random, playgroundParticles.initialRotationMin, playgroundParticles.initialRotationMax); if (!playgroundParticles.rotateTowardsDirection) playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]; else { Vector3 particleDir; if (!playgroundParticles.onlySourcePositioning&&playgroundParticles.onlyLifetimePositioning) particleDir = (playgroundParticles.playgroundCache.position[p]+playgroundParticles.lifetimePositioning.Evaluate(.01f, playgroundParticles.lifetimePositioningScale))-playgroundParticles.playgroundCache.position[p]; else particleDir = playgroundParticles.playgroundCache.velocity[p]; playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]+SignedAngle( Vector3.up, particleDir, playgroundParticles.rotationNormal ); } if (!playgroundParticles.syncPositionsOnMainThread) playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p]; // Set size if (playgroundParticles.applyLifetimeSize && !playgroundParticles.applyParticleArraySize) playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(0)*playgroundParticles.scale; else if (playgroundParticles.applyLifetimeSize && playgroundParticles.applyParticleArraySize) playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(0)*playgroundParticles.particleArraySize.Evaluate((p*1f)/(playgroundParticles.particleCount*1f))*playgroundParticles.scale; else if (playgroundParticles.applyParticleArraySize) playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.particleArraySize.Evaluate((p*1f)/(playgroundParticles.particleCount*1f))*playgroundParticles.scale; else playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.scale; if (!playgroundParticles.syncPositionsOnMainThread) playgroundParticles.particleCache[p].size = playgroundParticles.playgroundCache.maskAlpha[p]>0?playgroundParticles.playgroundCache.size[p]:0; // Set color gradient id if (playgroundParticles.colorSource==COLORSOURCEC.LifetimeColors && playgroundParticles.lifetimeColors.Count>0) { playgroundParticles.lifetimeColorId++;playgroundParticles.lifetimeColorId=playgroundParticles.lifetimeColorId%playgroundParticles.lifetimeColors.Count; playgroundParticles.playgroundCache.lifetimeColorId[p] = playgroundParticles.lifetimeColorId; } // Local Manipulators if (playgroundParticles.calculateManipulatorOnRebirth) { for (int m = 0; m<playgroundParticles.manipulators.Count; m++) { if (playgroundParticles.manipulators[m].transform!=null) { CalculateManipulator(playgroundParticles, playgroundParticles.manipulators[m], p, playgroundParticles.t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], (playgroundParticles.localSpace?playgroundParticles.manipulators[m].transform.localPosition:playgroundParticles.manipulators[m].transform.position)+playgroundParticles.manipulatorFix, playgroundParticles.localSpace); } } } // Send birth event if (playgroundParticles.events.Count>0 && playgroundParticles.playgroundCache.rebirth[p]) playgroundParticles.SendEvent(EVENTTYPEC.Birth, p); if (playgroundParticles.hasEventManipulatorLocal) { for (int i = 0; i<playgroundParticles.manipulators.Count; i++) { if (playgroundParticles.manipulators[i].trackParticles && playgroundParticles.manipulators[i].sendEventBirth && playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.targetPosition[p], playgroundParticles.manipulators[i].transform.position)) { playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p); playgroundParticles.manipulators[i].SendParticleEventBirth(); } } } if (playgroundParticles.hasEventManipulatorGlobal) { for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) { if (PlaygroundC.reference.manipulators[i].trackParticles && PlaygroundC.reference.manipulators[i].sendEventBirth && PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.targetPosition[p], PlaygroundC.reference.manipulators[i].transform.position)) { playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p); PlaygroundC.reference.manipulators[i].SendParticleEventBirth(); } } } }
// Rebirth of a specified particle public static void Rebirth (PlaygroundParticlesC playgroundParticles, int p, System.Random random) { // Set initial values playgroundParticles.playgroundCache.birthDelay[p] = 0f; playgroundParticles.playgroundCache.life[p] = 0f; playgroundParticles.playgroundCache.birth[p] = playgroundParticles.playgroundCache.death[p]; playgroundParticles.playgroundCache.death[p] += playgroundParticles.lifetime; playgroundParticles.playgroundCache.rebirth[p] = playgroundParticles.source==SOURCEC.Script?true:(playgroundParticles.emit && playgroundParticles.playgroundCache.emission[p]); playgroundParticles.playgroundCache.velocity[p] = Vector3.zero; // Set new random size if (playgroundParticles.applyRandomSizeOnRebirth) playgroundParticles.playgroundCache.initialSize[p] = RandomRange(random, playgroundParticles.sizeMin, playgroundParticles.sizeMax); // Initial velocity if (!playgroundParticles.onlySourcePositioning) { // Initial global velocity if (playgroundParticles.applyInitialVelocity) { if (playgroundParticles.applyRandomInitialVelocityOnRebirth) playgroundParticles.playgroundCache.initialVelocity[p] = RandomRange(random, playgroundParticles.initialVelocityMin, playgroundParticles.initialVelocityMax); playgroundParticles.playgroundCache.velocity[p] = playgroundParticles.playgroundCache.initialVelocity[p]; // Give this spawning particle its velocity shape if (playgroundParticles.applyInitialVelocityShape) playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f))); } // Initial local velocity if (playgroundParticles.applyInitialLocalVelocity && playgroundParticles.source!=SOURCEC.Script) { playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRange(random, playgroundParticles.initialLocalVelocityMin, playgroundParticles.initialLocalVelocityMax); playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.playgroundCache.targetDirection[p]; // Give this spawning particle its local velocity shape if (playgroundParticles.applyInitialVelocityShape) playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f))); } // Initial stretch if (playgroundParticles.renderModeStretch) { if (playgroundParticles.playgroundCache.velocity[p]!=Vector3.zero) playgroundParticles.particleCache[p].velocity = playgroundParticles.playgroundCache.velocity[p]; else playgroundParticles.particleCache[p].velocity = playgroundParticles.stretchStartDirection; } else playgroundParticles.particleCache[p].velocity = Vector3.zero; } if (playgroundParticles.source==SOURCEC.Script) { // Velocity for script mode if (!playgroundParticles.onlySourcePositioning) playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.scriptedEmissionVelocity; playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.scriptedEmissionPosition; } if (playgroundParticles.playgroundCache.rebirth[p]) { // Set new random rotation if (playgroundParticles.playgroundCache.initialRotation.Length!=playgroundParticles.particleCount) return; if (playgroundParticles.applyRandomRotationOnRebirth) playgroundParticles.playgroundCache.initialRotation[p] = RandomRange(random, playgroundParticles.initialRotationMin, playgroundParticles.initialRotationMax); playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]; playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p]; // Source Scattering if (playgroundParticles.applySourceScatter && playgroundParticles.source!=SOURCEC.Script) { if (playgroundParticles.playgroundCache.scatterPosition[p]==Vector3.zero || playgroundParticles.applyRandomScatterOnRebirth) playgroundParticles.playgroundCache.scatterPosition[p] = RandomRange(random, playgroundParticles.sourceScatterMin, playgroundParticles.sourceScatterMax); } else playgroundParticles.playgroundCache.scatterPosition[p] = Vector3.zero; playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialColorOnRebirth) { playgroundParticles.particleCache[p].color = playgroundParticles.playgroundCache.initialColor[p]; playgroundParticles.playgroundCache.color[p] = playgroundParticles.playgroundCache.initialColor[p]; } } else playgroundParticles.particleCache[p].position = PlaygroundC.initialTargetPosition; // Set color gradient id if (playgroundParticles.colorSource==COLORSOURCEC.LifetimeColors && playgroundParticles.lifetimeColors.Count>0) { playgroundParticles.lifetimeColorId++;playgroundParticles.lifetimeColorId=playgroundParticles.lifetimeColorId%playgroundParticles.lifetimeColors.Count; playgroundParticles.playgroundCache.lifetimeColorId[p] = playgroundParticles.lifetimeColorId; } // Reset manipulators influence playgroundParticles.playgroundCache.changedByProperty[p] = false; playgroundParticles.playgroundCache.changedByPropertyColor[p] = false; playgroundParticles.playgroundCache.changedByPropertyColorLerp[p] = false; playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p] = false; playgroundParticles.playgroundCache.changedByPropertySize[p] = false; playgroundParticles.playgroundCache.changedByPropertyTarget[p] = false; playgroundParticles.playgroundCache.changedByPropertyDeath[p] = false; playgroundParticles.playgroundCache.propertyTarget[p] = 0; playgroundParticles.playgroundCache.propertyId[p] = 0; playgroundParticles.playgroundCache.propertyColorId[p] = 0; // Send birth event if (playgroundParticles.events.Count>0) playgroundParticles.SendEvent(EVENTTYPEC.Birth, p); }
public static void Collisions (PlaygroundParticlesC playgroundParticles) { // Particle collisions (must currently run on main-thread due to the Physics.Raycast dependency) if (!playgroundParticles.onlySourcePositioning && playgroundParticles.collisionRadius>0) { Ray ray = new Ray(); float distance; bool is3d = playgroundParticles.collisionType==COLLISIONTYPEC.Physics3D; RaycastHit hitInfo; RaycastHit2D hitInfo2D; bool hasEvents = playgroundParticles.events.Count>0; Vector3 preCollisionVelocity; // Prepare the infinite collision planes if (playgroundParticles.collision && playgroundParticles.collisionRadius>0 && playgroundParticles.colliders.Count>0) { for (int c = 0; c<playgroundParticles.colliders.Count; c++) { playgroundParticles.colliders[c].UpdatePlane(); } } for (int p = 0; p<playgroundParticles.particleCount; p++) { if (playgroundParticles.playgroundCache.life[p]==0 || playgroundParticles.playgroundCache.life[p]>=playgroundParticles.lifetime) continue; // Playground Plane colliders (never exceed these) for (int c = 0; c<playgroundParticles.colliders.Count; c++) { if (playgroundParticles.colliders[c].enabled && playgroundParticles.colliders[c].transform && !playgroundParticles.colliders[c].plane.GetSide(playgroundParticles.playgroundCache.position[p])) { // Set particle to location ray.origin = playgroundParticles.playgroundCache.position[p]; ray.direction = playgroundParticles.colliders[c].plane.normal; if (playgroundParticles.colliders[c].plane.Raycast(ray, out distance)) playgroundParticles.playgroundCache.position[p] = ray.GetPoint(distance); // Store velocity before collision preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p]; // Reflect particle playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.colliders[c].plane.normal+RandomVector3(playgroundParticles.internalRandom03, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness; // Apply lifetime loss if (playgroundParticles.lifetimeLoss>0) { playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss); playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true; } // Send event if (hasEvents) playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, playgroundParticles.colliders[c].transform); } } // Colliders in scene if (playgroundParticles.playgroundCache.velocity[p].magnitude>PlaygroundC.collisionSleepVelocity) { // Collide by checking for potential passed collider in the direction of this particle's velocity from the previous frame if (is3d) { if (Physics.Raycast( playgroundParticles.playgroundCache.collisionParticlePosition[p], (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p]).normalized, out hitInfo, Vector3.Distance(playgroundParticles.playgroundCache.collisionParticlePosition[p], playgroundParticles.playgroundCache.position[p])+playgroundParticles.collisionRadius, playgroundParticles.collisionMask)) { // Set particle to location playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.collisionParticlePosition[p]; // Store velocity before collision preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p]; // Reflect particle playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], hitInfo.normal+RandomVector3(playgroundParticles.internalRandom03, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness; // Apply lifetime loss if (playgroundParticles.lifetimeLoss>0) { playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss); playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true; } // Add force to rigidbody if (playgroundParticles.affectRigidbodies && hitInfo.rigidbody) hitInfo.rigidbody.AddForceAtPosition(playgroundParticles.playgroundCache.velocity[p]*playgroundParticles.mass, playgroundParticles.playgroundCache.position[p]); // Send event if (hasEvents) playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, hitInfo.transform, hitInfo.collider); } } else { hitInfo2D = Physics2D.Raycast( playgroundParticles.playgroundCache.collisionParticlePosition[p], (playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p]).normalized, Vector3.Distance(playgroundParticles.playgroundCache.collisionParticlePosition[p], playgroundParticles.playgroundCache.position[p])+playgroundParticles.collisionRadius, playgroundParticles.collisionMask, playgroundParticles.minCollisionDepth, playgroundParticles.maxCollisionDepth ); if (hitInfo2D.collider!=null) { // Set particle to location playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.collisionParticlePosition[p]; // Store velocity before collision preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p]; // Reflect particle playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], (Vector3)hitInfo2D.normal+RandomVector3(playgroundParticles.internalRandom03, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax))*playgroundParticles.bounciness; // Apply lifetime loss if (playgroundParticles.lifetimeLoss>0) { playgroundParticles.playgroundCache.birth[p] -= playgroundParticles.playgroundCache.life[p]/(1f-playgroundParticles.lifetimeLoss); playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true; } // Add force to rigidbody if (playgroundParticles.affectRigidbodies && hitInfo2D.rigidbody) hitInfo2D.rigidbody.AddForceAtPosition(playgroundParticles.playgroundCache.velocity[p]*playgroundParticles.mass, playgroundParticles.playgroundCache.position[p]); // Send event if (hasEvents) playgroundParticles.SendEvent(EVENTTYPEC.Collision, p, preCollisionVelocity, hitInfo2D.transform, hitInfo2D.collider); } } } else { playgroundParticles.playgroundCache.velocity[p] = Vector3.zero; } } } }
public static void ThreadedCalculations (PlaygroundParticlesC playgroundParticles) { // Component disabled? if (!playgroundParticles.enabled || !playgroundParticles.isDoneThread) return; // Has the calculation been paused previously? if (playgroundParticles.cameFromNonCalculatedFrame) { playgroundParticles.Start(); playgroundParticles.cameFromNonCalculatedFrame = false; return; } // Apply locked position if (playgroundParticles.applyLockPosition) { if (playgroundParticles.lockPositionIsLocal) playgroundParticles.particleSystemTransform.localPosition = playgroundParticles.lockPosition; else playgroundParticles.particleSystemTransform.position = playgroundParticles.lockPosition; } // Apply locked rotation if (playgroundParticles.applyLockRotation) { if (playgroundParticles.lockRotationIsLocal) playgroundParticles.particleSystemTransform.localRotation = Quaternion.Euler(playgroundParticles.lockRotation); else playgroundParticles.particleSystemTransform.rotation = Quaternion.Euler(playgroundParticles.lockRotation); } // Apply locked scale if (playgroundParticles.applyLockScale) playgroundParticles.particleSystemTransform.localScale = playgroundParticles.lockScale; // Set delta time playgroundParticles.localDeltaTime = PlaygroundC.globalTime-playgroundParticles.lastTimeUpdated; playgroundParticles.lastTimeUpdated = PlaygroundC.globalTime; float t = (playgroundParticles.localDeltaTime*playgroundParticles.particleTimescale); // Prepare Source positions Vector3 stPos = Vector3.zero; Quaternion stRot = Quaternion.identity; Vector3 stSca = new Vector3(1f, 1f, 1f); Vector3 stDir = new Vector3(); bool localSpace = (playgroundParticles.shurikenParticleSystem.simulationSpace == ParticleSystemSimulationSpace.Local); bool overflow = (playgroundParticles.overflowOffset!=Vector3.zero); bool skinnedWorldObjectReady = false; playgroundParticles.renderModeStretch = playgroundParticles.particleSystemRenderer2.renderMode==ParticleSystemRenderMode.Stretch; if (playgroundParticles.emit) { switch (playgroundParticles.source) { case SOURCEC.Script: break; case SOURCEC.State: if (playgroundParticles.states.Count>0) { if (playgroundParticles.states[playgroundParticles.activeState].stateTransform!=null) { if (!playgroundParticles.states[playgroundParticles.activeState].initialized) playgroundParticles.states[playgroundParticles.activeState].Initialize (); stPos = playgroundParticles.states[playgroundParticles.activeState].stateTransform.position; stRot = playgroundParticles.states[playgroundParticles.activeState].stateTransform.rotation; stSca = playgroundParticles.states[playgroundParticles.activeState].stateTransform.localScale; if (localSpace && (playgroundParticles.states[playgroundParticles.activeState].stateTransform.parent==playgroundParticles.particleSystemTransform || playgroundParticles.states[playgroundParticles.activeState].stateTransform==playgroundParticles.particleSystemTransform)) { stPos = Vector3.zero; stRot = Quaternion.Euler (Vector3.zero); } } } else return; break; case SOURCEC.Transform: stPos = playgroundParticles.sourceTransform.position; stRot = playgroundParticles.sourceTransform.rotation; stSca = playgroundParticles.sourceTransform.localScale; if (localSpace && playgroundParticles.sourceTransform==playgroundParticles.particleSystemTransform) { stPos = Vector3.zero; stRot = Quaternion.Euler (Vector3.zero); } break; case SOURCEC.WorldObject: // Handle vertex data in active World Object if (playgroundParticles.worldObject.gameObject!=null) { if (playgroundParticles.worldObject.gameObject.GetInstanceID()!=playgroundParticles.worldObject.cachedId) playgroundParticles.worldObject = NewWorldObject(playgroundParticles.worldObject.gameObject.transform); if (playgroundParticles.worldObject.mesh!=null) { stPos = playgroundParticles.worldObject.transform.position; stRot = playgroundParticles.worldObject.transform.rotation; stSca = playgroundParticles.worldObject.transform.localScale; if (localSpace) { stPos = Vector3.zero; stRot = Quaternion.Euler (Vector3.zero); } playgroundParticles.worldObject.updateNormals = playgroundParticles.worldObjectUpdateNormals; if (playgroundParticles.worldObjectUpdateVertices) playgroundParticles.worldObject.Update (); } else return; } else return; break; case SOURCEC.SkinnedWorldObject: // Handle vertex data in active Skinned World Object if (playgroundParticles.skinnedWorldObject.gameObject!=null) { if (playgroundParticles.skinnedWorldObject.gameObject.GetInstanceID()!=playgroundParticles.skinnedWorldObject.cachedId) playgroundParticles.skinnedWorldObject = NewSkinnedWorldObject(playgroundParticles.skinnedWorldObject.gameObject.transform, playgroundParticles.skinnedWorldObject.downResolution); } skinnedWorldObjectReady = playgroundParticles.skinnedWorldObject.gameObject!=null && playgroundParticles.skinnedWorldObject.mesh!=null; if (skinnedWorldObjectReady) { stPos = playgroundParticles.skinnedWorldObject.transform.position; stRot = playgroundParticles.skinnedWorldObject.transform.rotation; stSca = playgroundParticles.skinnedWorldObject.transform.localScale; stDir = playgroundParticles.skinnedWorldObject.transform.TransformDirection (playgroundParticles.overflowOffset); playgroundParticles.skinnedWorldObject.updateNormals = playgroundParticles.worldObjectUpdateNormals; if (Time.frameCount%PlaygroundC.skinnedUpdateRate==0) { if (playgroundParticles.worldObjectUpdateVertices) playgroundParticles.skinnedWorldObject.MeshUpdate(); playgroundParticles.skinnedWorldObject.BoneUpdate(); } } else return; break; case SOURCEC.Paint: if (playgroundParticles.paint.initialized) { stPos = playgroundParticles.particleSystemTransform.position; stRot = playgroundParticles.particleSystemTransform.rotation; stSca = playgroundParticles.particleSystemTransform.localScale; if (playgroundParticles.paint.positionLength>0) { for (int p = 0; p<playgroundParticles.particleCache.Length; p++) { playgroundParticles.paint.Update(p); } } else return; } else { playgroundParticles.paint.Initialize (); return; } break; case SOURCEC.Projection: if (playgroundParticles.projection.projectionTexture!=null && playgroundParticles.projection.projectionTransform!=null) { if (!playgroundParticles.projection.initialized) playgroundParticles.projection.Initialize(); stPos = playgroundParticles.projection.projectionTransform.position; stRot = playgroundParticles.projection.projectionTransform.rotation; stSca = playgroundParticles.projection.projectionTransform.localScale; if (localSpace) playgroundParticles.shurikenParticleSystem.simulationSpace = ParticleSystemSimulationSpace.World; if (playgroundParticles.projection.liveUpdate || !playgroundParticles.projection.hasRefreshed) { playgroundParticles.projection.UpdateSource(); playgroundParticles.projection.Update(); stDir = playgroundParticles.projection.projectionTransform.TransformDirection (playgroundParticles.overflowOffset); playgroundParticles.projection.hasRefreshed = true; } } else return; break; } } // Collision detection (runs on main-thread) if (playgroundParticles.collision) Collisions(playgroundParticles); // Sync positions to main-thread if (playgroundParticles.syncPositionsOnMainThread) { for (int p = 0; p<playgroundParticles.particleCount; p++) { playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.position[p]; } } // Main-thread preparations // Prepare Particle colors bool stateReadyForTextureColor = (playgroundParticles.source==SOURCEC.State && playgroundParticles.states[playgroundParticles.activeState].stateTexture!=null); // Prepare local manipulators for (int m = 0; m<playgroundParticles.manipulators.Count; m++) { playgroundParticles.manipulators[m].Update(); playgroundParticles.manipulators[m].transform.SetLocalPosition(playgroundParticles.particleSystemTransform); playgroundParticles.manipulators[m].SetLocalTargetsPosition(playgroundParticles.particleSystemTransform); playgroundParticles.manipulators[m].willAffect = true; } // Prepare global manipulators from this local space for (int m = 0; m<PlaygroundC.reference.manipulators.Count; m++) { PlaygroundC.reference.manipulators[m].willAffect = ((PlaygroundC.reference.manipulators[m].affects.value & 1<<playgroundParticles.particleSystemGameObject.layer)!=0); PlaygroundC.reference.manipulators[m].transform.SetLocalPosition(playgroundParticles.particleSystemTransform); } // Prepare events bool hasEvent = playgroundParticles.events.Count>0; bool hasTimerEvent = false; if (hasEvent) { for (int i = 0; i<playgroundParticles.events.Count; i++) { playgroundParticles.events[i].Initialize(); if (playgroundParticles.events[i].initializedTarget && playgroundParticles.events[i].broadcastType!=EVENTBROADCASTC.EventListeners) if (!playgroundParticles.events[i].target.eventControlledBy.Contains (playgroundParticles)) playgroundParticles.events[i].target.eventControlledBy.Add (playgroundParticles); if (playgroundParticles.events[i].eventType==EVENTTYPEC.Time) { hasTimerEvent = playgroundParticles.events[i].UpdateTime(); } } } // Calculate all thread-safe data on a new thread if (!playgroundParticles.isDoneThread) return; playgroundParticles.isDoneThread = false; PlaygroundC.RunAsync(()=>{ if (playgroundParticles.isDoneThread) return; lock (playgroundParticles.locker) { // Prepare variables for particle source positions Matrix4x4 stMx = new Matrix4x4(); Matrix4x4 fMx = new Matrix4x4(); stMx.SetTRS(stPos, stRot, stSca); fMx.SetTRS(Vector3.zero, stRot, new Vector3(1f,1f,1f)); int downResolution = playgroundParticles.skinnedWorldObject.downResolution; int downResolutionP; // Prepare variables for lifetime, velocity & manipulators int m = 0; float evaluatedLife; Vector3 deltaVelocity; Vector3 manipulatorPosition; float manipulatorDistance; Vector3 zero = Vector3.zero; Vector3 up = Vector3.up; // Prepare variables for colors Color lifetimeColor = new Color(); bool hasLifetimeColors = (playgroundParticles.lifetimeColors.Count>0); // Update skinned mesh vertices if (skinnedWorldObjectReady) playgroundParticles.skinnedWorldObject.Update(); int pCount = playgroundParticles.particleCache.Length; bool applyMask = false; // Check that cache is correct if (playgroundParticles.playgroundCache.maskAlpha.Length!=pCount) { playgroundParticles.playgroundCache.maskAlpha = new float[pCount]; playgroundParticles.playgroundCache.isMasked = new bool[pCount]; } // Calculation loop for (int p = 0; p<playgroundParticles.particleCache.Length; p++) { // Check that particle count is correct if (pCount != playgroundParticles.particleCache.Length) { playgroundParticles.cameFromNonEmissionFrame = false; playgroundParticles.isDoneThread = true; return; } // Apply particle mask if (p<playgroundParticles.particleMask) { if (playgroundParticles.playgroundCache.maskAlpha[p]<=0 || playgroundParticles.particleMaskTime<=0) { playgroundParticles.playgroundCache.isMasked[p] = true; playgroundParticles.playgroundCache.maskAlpha[p] = 0; playgroundParticles.particleCache[p].size = 0; applyMask = true; } else { applyMask = false; playgroundParticles.playgroundCache.maskAlpha[p] -= (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime; } } else { applyMask = false; if (playgroundParticles.playgroundCache.maskAlpha[p]<1f && playgroundParticles.particleMaskTime>0) { playgroundParticles.playgroundCache.maskAlpha[p] += (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime; } else { playgroundParticles.playgroundCache.maskAlpha[p] = 1f; playgroundParticles.playgroundCache.isMasked[p] = false; } } // Get this particle's color if (!playgroundParticles.playgroundCache.changedByPropertyColor[p] && !playgroundParticles.playgroundCache.changedByPropertyColorLerp[p]) { switch (playgroundParticles.colorSource) { case COLORSOURCEC.Source: switch (playgroundParticles.source) { case SOURCEC.Script: lifetimeColor = playgroundParticles.playgroundCache.scriptedColor[p]; break; case SOURCEC.State: if (stateReadyForTextureColor) lifetimeColor = playgroundParticles.states[playgroundParticles.activeState].GetColor(p); else lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)); break; case SOURCEC.Projection: lifetimeColor = playgroundParticles.projection.GetColor(p); break; case SOURCEC.Paint: lifetimeColor = playgroundParticles.paint.GetColor(p); break; default: lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)); break; } if (playgroundParticles.sourceUsesLifetimeAlpha) lifetimeColor.a = Mathf.Clamp(playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)).a, 0, lifetimeColor.a); break; case COLORSOURCEC.LifetimeColor: lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)); break; case COLORSOURCEC.LifetimeColors: if (hasLifetimeColors) lifetimeColor = playgroundParticles.lifetimeColors[playgroundParticles.playgroundCache.lifetimeColorId[p]%playgroundParticles.lifetimeColors.Count].gradient.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)); else lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)); break; } } else if (playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p]) { lifetimeColor = playgroundParticles.particleCache[p].color; lifetimeColor.a = Mathf.Clamp(playgroundParticles.lifetimeColor.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)).a, 0, lifetimeColor.a); } // Assign color to particle lifetimeColor*=playgroundParticles.playgroundCache.maskAlpha[p]; playgroundParticles.particleCache[p].color = lifetimeColor; // Give Playground Cache its color value playgroundParticles.playgroundCache.color[p] = lifetimeColor; // Source positions if (playgroundParticles.emit) { switch (playgroundParticles.source) { case SOURCEC.State: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity) playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.states[playgroundParticles.activeState].GetNormal(p))); if (!overflow) { playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p))+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.states[playgroundParticles.activeState].positionLength))+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p))+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.states[playgroundParticles.activeState].positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.states[playgroundParticles.activeState].GetNormal(p), p, playgroundParticles.states[playgroundParticles.activeState].positionLength))+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } // Local space compensation calculation if (localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; case SOURCEC.Transform: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity) playgroundParticles.playgroundCache.targetDirection[p] = stRot*playgroundParticles.playgroundCache.initialLocalVelocity[p]; if (!overflow) { playgroundParticles.playgroundCache.targetPosition[p] = stPos+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(GetOverflow2(playgroundParticles.overflowOffset, p, 1))+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = stPos+GetOverflow2(playgroundParticles.overflowOffset, p, 1)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(GetOverflow2(playgroundParticles.overflowOffset, p, 1))+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } // Local space compensation calculation if (localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; case SOURCEC.WorldObject: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity) playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.vertexPositions.Length])); if (!overflow) { playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4( playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length] )+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4( playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.worldObject.vertexPositions.Length) )+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4( playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length] )+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.worldObject.vertexPositions.Length)+ playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4( playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]+GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length], p, playgroundParticles.worldObject.vertexPositions.Length) )+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } // Local space compensation calculation if (localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; case SOURCEC.SkinnedWorldObject: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity) playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.skinnedWorldObject.normals[p%playgroundParticles.skinnedWorldObject.normals.Length])); downResolutionP = Mathf.RoundToInt(p*downResolution)%playgroundParticles.skinnedWorldObject.vertexPositions.Length; if (!overflow) { playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+ GetOverflow2( stDir, p, playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution )+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+ GetOverflow2( playgroundParticles.overflowOffset, p, playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution )+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+ GetOverflow2( playgroundParticles.overflowOffset, playgroundParticles.skinnedWorldObject.normals[downResolutionP], p, playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution )+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } // Local space compensation calculation if (localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; case SOURCEC.Projection: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity) playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.projection.GetNormal(p), playgroundParticles.playgroundCache.initialLocalVelocity[p]); if (!overflow) { playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(stDir, p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(Vector3Scale(stDir, playgroundParticles.projection.GetNormal(p)), p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } } break; case SOURCEC.Paint: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity) playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.paint.GetRotation(p)*Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.paint.GetNormal(p)); if (!overflow) { playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+GetOverflow2(playgroundParticles.paint.GetRotation(p)*playgroundParticles.overflowOffset, p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+playgroundParticles.paint.GetRotation(p)*GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.paint.GetNormal(p), p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } // Local space compensation calculation if (localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; } } // Set initial particle values if life is 0 if (playgroundParticles.playgroundCache.life[p]==0) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.playgroundCache.initialColor[p] = lifetimeColor; } // Particle lifetime, velocity and manipulators if (playgroundParticles.playgroundCache.rebirth[p]) { // Particle is alive if (playgroundParticles.playgroundCache.life[p]<=PlaygroundC.globalTime+playgroundParticles.lifetime) { // Reset particle velocity //playgroundParticles.particleCache[p].velocity = Vector3.zero; // Lifetime size if (!playgroundParticles.playgroundCache.changedByPropertySize[p]) { if (playgroundParticles.applyLifetimeSize) playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime))*playgroundParticles.scale; else playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.scale; } playgroundParticles.particleCache[p].size = !applyMask?playgroundParticles.playgroundCache.size[p]:0; // Local Manipulators for (m = 0; m<playgroundParticles.manipulators.Count; m++) { if (playgroundParticles.manipulators[m].transform!=null) { manipulatorPosition = localSpace?playgroundParticles.manipulators[m].transform.localPosition:playgroundParticles.manipulators[m].transform.position; manipulatorDistance = playgroundParticles.manipulators[m].strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/playgroundParticles.manipulators[m].strengthDistanceEffect:10f; CalculateManipulator(playgroundParticles, playgroundParticles.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], manipulatorPosition, manipulatorDistance, localSpace); } } // Global Manipulators for (m = 0; m<PlaygroundC.reference.manipulators.Count; m++) { if (PlaygroundC.reference.manipulators[m].transform!=null) { manipulatorPosition = localSpace?PlaygroundC.reference.manipulators[m].transform.localPosition:PlaygroundC.reference.manipulators[m].transform.position; manipulatorDistance = PlaygroundC.reference.manipulators[m].strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/PlaygroundC.reference.manipulators[m].strengthDistanceEffect:10f; CalculateManipulator(playgroundParticles, PlaygroundC.reference.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], manipulatorPosition, manipulatorDistance, localSpace); } } if (!playgroundParticles.onlySourcePositioning) { // Velocity bending if (playgroundParticles.applyVelocityBending) { if (playgroundParticles.velocityBendingType==VELOCITYBENDINGTYPEC.SourcePosition) { playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect( new Vector3( playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x, playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y, playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z ), (playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.position[p]).normalized )*t; } else { playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect( new Vector3( playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x, playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y, playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z ), (playgroundParticles.playgroundCache.previousParticlePosition[p]-playgroundParticles.playgroundCache.position[p]).normalized )*t; } } // Delta velocity if (!playgroundParticles.cameFromNonEmissionFrame && playgroundParticles.calculateDeltaMovement && playgroundParticles.playgroundCache.life[p]==0 && playgroundParticles.source!=SOURCEC.Script) { deltaVelocity = playgroundParticles.playgroundCache.targetPosition[p]-(playgroundParticles.playgroundCache.previousTargetPosition[p]); playgroundParticles.playgroundCache.velocity[p] += deltaVelocity*playgroundParticles.deltaMovementStrength; } // Set previous target position (used by delta velocity & local space movement compensation) playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; // Gravity playgroundParticles.playgroundCache.velocity[p] -= playgroundParticles.gravity*t; // Lifetime additive velocity if (playgroundParticles.applyLifetimeVelocity) playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.lifetimeVelocity.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime))*t; // Damping, max velocity, constraints and final positioning if (playgroundParticles.playgroundCache.velocity[p]!=zero) { // Max velocity if (playgroundParticles.playgroundCache.velocity[p].sqrMagnitude>playgroundParticles.maxVelocity) playgroundParticles.playgroundCache.velocity[p] = Vector3.ClampMagnitude(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.maxVelocity); // Damping if (playgroundParticles.damping>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], zero, playgroundParticles.damping*t); // Axis constraints if (playgroundParticles.axisConstraints.x) playgroundParticles.playgroundCache.velocity[p].x = 0; if (playgroundParticles.axisConstraints.y) playgroundParticles.playgroundCache.velocity[p].y = 0; if (playgroundParticles.axisConstraints.z) playgroundParticles.playgroundCache.velocity[p].z = 0; // Set calculated collision position playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.previousParticlePosition[p]; // Relocate playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.velocity[p]*t; if (playgroundParticles.applyLocalSpaceMovementCompensation) { if (!playgroundParticles.applyMovementCompensationLifetimeStrength) playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p]; else playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p]*playgroundParticles.movementCompensationLifetimeStrength.Evaluate(Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)); } // Set particle velocity to be able to stretch towards movement if (playgroundParticles.renderModeStretch) { if (playgroundParticles.applyLifetimeStretching) playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime); else playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], t*playgroundParticles.stretchSpeed); } } } else { // Only Source Positioning // Set particle velocity to be able to stretch towards movement if (playgroundParticles.renderModeStretch) { if (playgroundParticles.applyLifetimeStretching) playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p], t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime); else playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p], t*playgroundParticles.stretchSpeed); } playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.source!=SOURCEC.Script) playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]; } // Rotation if (!playgroundParticles.rotateTowardsDirection) playgroundParticles.playgroundCache.rotation[p] += playgroundParticles.playgroundCache.rotationSpeed[p]*t; else { playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]+SignedAngle( up, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], playgroundParticles.rotationNormal ); } playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p]; // Set previous particle position playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.position[p]; // Send timed event if (hasTimerEvent) playgroundParticles.SendEvent(EVENTTYPEC.Time, p); } // Calculate lifetime evaluatedLife = (PlaygroundC.globalTime-playgroundParticles.playgroundCache.birth[p])/playgroundParticles.lifetime; // Lifetime if (playgroundParticles.playgroundCache.life[p]<playgroundParticles.lifetime) { playgroundParticles.playgroundCache.life[p] = playgroundParticles.lifetime*evaluatedLife; playgroundParticles.particleCache[p].lifetime = Mathf.Clamp(playgroundParticles.lifetime - playgroundParticles.playgroundCache.life[p], .1f, playgroundParticles.lifetime); } else { // Loop exceeded if (!playgroundParticles.loop && PlaygroundC.globalTime>playgroundParticles.simulationStarted+playgroundParticles.lifetime-.01f) { playgroundParticles.loopExceeded = true; if (playgroundParticles.disableOnDone && playgroundParticles.loopExceededOnParticle==p && evaluatedLife>2) playgroundParticles.queueEmissionHalt = true; if (playgroundParticles.loopExceededOnParticle==-1) playgroundParticles.loopExceededOnParticle = p; } // Send death event if (hasEvent) { playgroundParticles.SendEvent(EVENTTYPEC.Death, p); } // New cycle begins if (PlaygroundC.globalTime>=playgroundParticles.playgroundCache.birth[p]+playgroundParticles.playgroundCache.birthDelay[p] && !playgroundParticles.loopExceeded && playgroundParticles.source!=SOURCEC.Script) { if (!playgroundParticles.playgroundCache.changedByPropertyDeath[p] || playgroundParticles.playgroundCache.changedByPropertyDeath[p] && PlaygroundC.globalTime>playgroundParticles.playgroundCache.death[p]) Rebirth(playgroundParticles, p, playgroundParticles.internalRandom02); else { playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; } } else { playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; } } } else { // Particle is set to not rebirth playgroundParticles.playgroundCache.targetPosition[p] = PlaygroundC.initialTargetPosition; playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; } // Set particle position if no sync if (!playgroundParticles.syncPositionsOnMainThread) playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.position[p]; } } playgroundParticles.cameFromNonEmissionFrame = false; playgroundParticles.isDoneThread = true; }); // Turbulence if (!playgroundParticles.onlySourcePositioning && playgroundParticles.turbulenceStrength>0 && playgroundParticles.turbulenceType!=TURBULENCETYPE.None) { float strengthMultiplier = 1f; float zeroFixX = 1f; float zeroFixY = 2f; float zeroFixZ = 3f; // Simplex if (playgroundParticles.turbulenceType==TURBULENCETYPE.Simplex) { if (playgroundParticles.turbulenceSimplex==null) playgroundParticles.turbulenceSimplex = new SimplexNoise(); PlaygroundC.RunAsync(()=>{ for (int p = 0; p<playgroundParticles.particleCount; p++) { if (playgroundParticles.playgroundCache.rebirth[p]) { if (playgroundParticles.turbulenceTimeScale>0) { if (playgroundParticles.turbulenceApplyLifetimeStrength) strengthMultiplier = playgroundParticles.turbulenceLifetimeStrength.Evaluate (Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)); if (strengthMultiplier>0) { if (!playgroundParticles.axisConstraints.x) playgroundParticles.playgroundCache.velocity[p].x += (float)playgroundParticles.turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale, PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale )*playgroundParticles.turbulenceStrength*t*strengthMultiplier; if (!playgroundParticles.axisConstraints.y) playgroundParticles.playgroundCache.velocity[p].y += (float)playgroundParticles.turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale, PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale )*playgroundParticles.turbulenceStrength*t*strengthMultiplier; if (!playgroundParticles.axisConstraints.z) playgroundParticles.playgroundCache.velocity[p].z += (float)playgroundParticles.turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale, PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale )*playgroundParticles.turbulenceStrength*t*strengthMultiplier; } } else { if (strengthMultiplier>0) { if (!playgroundParticles.axisConstraints.x) playgroundParticles.playgroundCache.velocity[p].x += (float)playgroundParticles.turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale )*playgroundParticles.turbulenceStrength*t*strengthMultiplier; if (!playgroundParticles.axisConstraints.y) playgroundParticles.playgroundCache.velocity[p].y += (float)playgroundParticles.turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale )*playgroundParticles.turbulenceStrength*t*strengthMultiplier; if (!playgroundParticles.axisConstraints.z) playgroundParticles.playgroundCache.velocity[p].z += (float)playgroundParticles.turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*playgroundParticles.turbulenceScale, (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*playgroundParticles.turbulenceScale )*playgroundParticles.turbulenceStrength*t*strengthMultiplier; } } } } }); } // Perlin if (playgroundParticles.turbulenceType==TURBULENCETYPE.Perlin) { PlaygroundC.RunAsync(()=>{ for (int p = 0; p<playgroundParticles.particleCount; p++) { if (playgroundParticles.playgroundCache.rebirth[p]) { if (playgroundParticles.turbulenceStrength>0) { if (playgroundParticles.turbulenceApplyLifetimeStrength) strengthMultiplier = playgroundParticles.turbulenceLifetimeStrength.Evaluate (Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime)); if (strengthMultiplier>0) { if (!playgroundParticles.axisConstraints.x) playgroundParticles.playgroundCache.velocity[p].x += (Mathf.PerlinNoise ( PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale, playgroundParticles.playgroundCache.position[p].z*playgroundParticles.turbulenceScale )-.5f)*playgroundParticles.turbulenceStrength*t*strengthMultiplier; if (!playgroundParticles.axisConstraints.y) playgroundParticles.playgroundCache.velocity[p].y += (Mathf.PerlinNoise ( PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale, playgroundParticles.playgroundCache.position[p].x*playgroundParticles.turbulenceScale )-.5f)*playgroundParticles.turbulenceStrength*t*strengthMultiplier; if (!playgroundParticles.axisConstraints.z) playgroundParticles.playgroundCache.velocity[p].z += (Mathf.PerlinNoise ( PlaygroundC.globalTime*playgroundParticles.turbulenceTimeScale, playgroundParticles.playgroundCache.position[p].y*playgroundParticles.turbulenceScale )-.5f)*playgroundParticles.turbulenceStrength*t*strengthMultiplier; } } } } }); } } }
public static void ThreadedCalculations(PlaygroundParticlesC playgroundParticles) { playgroundParticles.lastTimeUpdated = PlaygroundC.globalTime; if (playgroundParticles.particleCount==0 || playgroundParticles.playgroundCache.color.Length!=playgroundParticles.particleCount || playgroundParticles.playgroundCache.targetPosition.Length!=playgroundParticles.particleCount || playgroundParticles.playgroundCache.targetDirection.Length!=playgroundParticles.particleCount || playgroundParticles.isYieldRefreshing || !PlaygroundC.IsReady()) { playgroundParticles.isDoneThread = true; playgroundParticles.threadHadNoActiveParticles = false; return; } if (PlaygroundC.reference.calculate && playgroundParticles.calculate && !playgroundParticles.inTransition && playgroundParticles.hasActiveParticles) {} else if (playgroundParticles.source!=SOURCEC.Script) { playgroundParticles.isDoneThread = true; playgroundParticles.cameFromNonCalculatedFrame = true; return; } else { playgroundParticles.isDoneThread = true; return; } float t = playgroundParticles.t; // Prepare variables for particle source positions Matrix4x4 stMx = new Matrix4x4(); Matrix4x4 fMx = new Matrix4x4(); stMx.SetTRS(playgroundParticles.stPos, playgroundParticles.stRot, playgroundParticles.stSca); fMx.SetTRS(Vector3.zero, playgroundParticles.stRot, new Vector3(1f,1f,1f)); int downResolution = playgroundParticles.skinnedWorldObject.downResolution; int downResolutionP; bool noActiveParticles = true; // Update skinned mesh vertices if (playgroundParticles.source==SOURCEC.SkinnedWorldObject && playgroundParticles.skinnedWorldObjectReady && PlaygroundC.reference.skinnedMeshThreadMethod==ThreadMethodComponent.InsideParticleCalculation) { playgroundParticles.skinnedWorldObject.Update(); } // Misc int pCount = playgroundParticles.particleCache.Length; bool applyMask = false; // Check that cache is correct if (playgroundParticles.playgroundCache.lifetimeSubtraction.Length!=pCount) SetLifetimeSubtraction(playgroundParticles); if (playgroundParticles.playgroundCache.maskAlpha.Length!=pCount) { playgroundParticles.playgroundCache.maskAlpha = new float[pCount]; playgroundParticles.playgroundCache.isMasked = new bool[pCount]; } if (playgroundParticles.playgroundCache.manipulatorId.Length!=pCount) playgroundParticles.playgroundCache.manipulatorId = new int[pCount]; if (playgroundParticles.playgroundCache.excludeFromManipulatorId.Length!=pCount) playgroundParticles.playgroundCache.excludeFromManipulatorId = new int[pCount]; if (playgroundParticles.playgroundCache.noForce.Length!=pCount) playgroundParticles.playgroundCache.noForce = new bool[pCount]; if (playgroundParticles.playgroundCache.isNonBirthed.Length!=pCount) playgroundParticles.playgroundCache.isNonBirthed = new bool[pCount]; if (playgroundParticles.playgroundCache.isFirstLoop.Length!=pCount) playgroundParticles.playgroundCache.isFirstLoop = new bool[pCount]; if (playgroundParticles.playgroundCache.simulate.Length!=pCount) { playgroundParticles.playgroundCache.simulate = new bool[pCount]; for (int p = 0; p<pCount; p++) playgroundParticles.playgroundCache.simulate[p] = true; } Color lifetimeColor = new Color(); Vector3 zero = Vector3.zero; Vector3 up = Vector3.up; // Calculation loop for (int p = 0; p<playgroundParticles.particleCount; p++) { // Check that particle count is correct if (pCount != playgroundParticles.particleCache.Length || playgroundParticles.isYieldRefreshing) { playgroundParticles.cameFromNonEmissionFrame = false; playgroundParticles.isDoneThread = true; playgroundParticles.threadHadNoActiveParticles = false; return; } // Check simulation if (!playgroundParticles.playgroundCache.simulate[p]) { if (playgroundParticles.playgroundCache.rebirth[p] && playgroundParticles.loop) { playgroundParticles.InactivateParticle(p); } continue; } else { noActiveParticles = false; } // Prepare variables inside scope bool hasLifetimeColors = (playgroundParticles.lifetimeColors.Count>0); float manipulatorDistance; Vector3 deltaVelocity; Vector3 manipulatorPosition; float normalizedLife = Mathf.Clamp01 (playgroundParticles.playgroundCache.life[p]/(playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p])); float evaluatedLife; float lifetimePositioningTimeScale = 1f; if (playgroundParticles.applyLifetimePositioningTimeScale) lifetimePositioningTimeScale = playgroundParticles.lifetimePositioningTimeScale.Evaluate(normalizedLife); // Apply particle mask if (p<playgroundParticles.particleMask) { if (playgroundParticles.playgroundCache.maskAlpha[p]<=0 || playgroundParticles.particleMaskTime<=0) { playgroundParticles.playgroundCache.isMasked[p] = true; playgroundParticles.playgroundCache.maskAlpha[p] = 0; playgroundParticles.particleCache[p].size = 0; applyMask = true; } else { applyMask = false; playgroundParticles.playgroundCache.maskAlpha[p] -= (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime; } } else { applyMask = false; if (playgroundParticles.playgroundCache.maskAlpha[p]<1f && playgroundParticles.particleMaskTime>0) { playgroundParticles.playgroundCache.maskAlpha[p] += (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime; } else { playgroundParticles.playgroundCache.maskAlpha[p] = 1f; playgroundParticles.playgroundCache.isMasked[p] = false; } } // Get this particle's color if (!playgroundParticles.playgroundCache.changedByPropertyColor[p] && !playgroundParticles.playgroundCache.changedByPropertyColorLerp[p]) { switch (playgroundParticles.colorSource) { case COLORSOURCEC.Source: switch (playgroundParticles.source) { case SOURCEC.Script: lifetimeColor = playgroundParticles.playgroundCache.scriptedColor[p]; break; case SOURCEC.State: if (playgroundParticles.stateReadyForTextureColor) lifetimeColor = playgroundParticles.states[playgroundParticles.activeState].GetColor(p); else lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(normalizedLife); break; case SOURCEC.Projection: lifetimeColor = playgroundParticles.projection.GetColor(p); break; case SOURCEC.Paint: lifetimeColor = playgroundParticles.paint.GetColor(p); break; default: lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(normalizedLife); break; } if (playgroundParticles.sourceUsesLifetimeAlpha) lifetimeColor.a = Mathf.Clamp(playgroundParticles.lifetimeColor.Evaluate(normalizedLife).a, 0, lifetimeColor.a); break; case COLORSOURCEC.LifetimeColor: lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(normalizedLife); break; case COLORSOURCEC.LifetimeColors: if (hasLifetimeColors) lifetimeColor = playgroundParticles.lifetimeColors[playgroundParticles.playgroundCache.lifetimeColorId[p]%playgroundParticles.lifetimeColors.Count].gradient.Evaluate(normalizedLife); else lifetimeColor = playgroundParticles.lifetimeColor.Evaluate(normalizedLife); break; } } else if (playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p] || playgroundParticles.playgroundCache.changedByPropertyColorLerp[p]) { lifetimeColor = playgroundParticles.particleCache[p].color; lifetimeColor.a = Mathf.Clamp(playgroundParticles.lifetimeColor.Evaluate(normalizedLife).a, 0, lifetimeColor.a); } // Assign color to particle lifetimeColor*=playgroundParticles.playgroundCache.maskAlpha[p]; playgroundParticles.particleCache[p].color = lifetimeColor; // Give Playground Cache its color value playgroundParticles.playgroundCache.color[p] = lifetimeColor; // Source positions if (playgroundParticles.emit) { switch (playgroundParticles.source) { case SOURCEC.State: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.states[playgroundParticles.activeState].GetNormal(p))); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.states[playgroundParticles.activeState].GetNormal(p))); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife); } if (!playgroundParticles.overflow) { playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p))+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.states[playgroundParticles.activeState].positionLength))+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p))+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.states[playgroundParticles.activeState].positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(playgroundParticles.states[playgroundParticles.activeState].GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.states[playgroundParticles.activeState].GetNormal(p), p, playgroundParticles.states[playgroundParticles.activeState].positionLength))+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; case SOURCEC.Transform: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.stRot*playgroundParticles.playgroundCache.initialLocalVelocity[p]; if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.stRot*playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife*lifetimePositioningTimeScale); } if (!playgroundParticles.overflow) { playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.stPos+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(GetOverflow2(playgroundParticles.overflowOffset, p, 1))+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.stPos+GetOverflow2(playgroundParticles.overflowOffset, p, 1)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4(GetOverflow2(playgroundParticles.overflowOffset, p, 1))+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; case SOURCEC.WorldObject: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length])); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length])); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife); } if (!playgroundParticles.overflow) { playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4( playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length] )+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4( playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.worldObject.vertexPositions.Length) )+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4( playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length] )+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.worldObject.vertexPositions.Length)+ playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = stMx.MultiplyPoint3x4( playgroundParticles.worldObject.vertexPositions[p%playgroundParticles.worldObject.vertexPositions.Length]+GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.worldObject.normals[p%playgroundParticles.worldObject.normals.Length], p, playgroundParticles.worldObject.vertexPositions.Length) )+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; case SOURCEC.SkinnedWorldObject: if (playgroundParticles.skinnedWorldObjectReady) { if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.skinnedWorldObject.normals[p%playgroundParticles.skinnedWorldObject.normals.Length])); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = fMx.MultiplyPoint3x4(Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.skinnedWorldObject.normals[p%playgroundParticles.skinnedWorldObject.normals.Length])); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife); } downResolutionP = Mathf.RoundToInt(p*downResolution)%playgroundParticles.skinnedWorldObject.vertexPositions.Length; if (!playgroundParticles.overflow) { playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+ GetOverflow2( playgroundParticles.stDir, p, playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution )+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+ GetOverflow2( playgroundParticles.overflowOffset, p, playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution )+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.skinnedWorldObject.vertexPositions[downResolutionP]+ GetOverflow2( playgroundParticles.overflowOffset, playgroundParticles.skinnedWorldObject.normals[downResolutionP], p, playgroundParticles.skinnedWorldObject.vertexPositions.Length/downResolution )+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } } break; case SOURCEC.Projection: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.projection.GetNormal(p), playgroundParticles.playgroundCache.initialLocalVelocity[p]); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = Vector3Scale(playgroundParticles.projection.GetNormal(p), playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale)); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife); } if (!playgroundParticles.overflow) { playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(playgroundParticles.stDir, p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.projection.GetPosition(p)+GetOverflow2(Vector3Scale(playgroundParticles.stDir, playgroundParticles.projection.GetNormal(p)), p, playgroundParticles.projection.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } } break; case SOURCEC.Paint: if (playgroundParticles.playgroundCache.rebirth[p] || playgroundParticles.onlySourcePositioning || playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; if (playgroundParticles.applyInitialLocalVelocity && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.paint.GetRotation(p)*Vector3Scale(playgroundParticles.playgroundCache.initialLocalVelocity[p], playgroundParticles.paint.GetNormal(p)); if (playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.targetDirection[p] = playgroundParticles.paint.GetRotation(p)*Vector3Scale(playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale), playgroundParticles.paint.GetNormal(p)); if (playgroundParticles.applyLifetimePositioningPositionScale) playgroundParticles.playgroundCache.targetDirection[p] *= playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife); } if (!playgroundParticles.overflow) { playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+playgroundParticles.playgroundCache.scatterPosition[p]; } else { switch (playgroundParticles.overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+GetOverflow2(playgroundParticles.paint.GetRotation(p)*playgroundParticles.overflowOffset, p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.World: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+GetOverflow2(playgroundParticles.overflowOffset, p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; case OVERFLOWMODEC.SourcePoint: playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.paint.GetPosition(p)+playgroundParticles.paint.GetRotation(p)*GetOverflow2(playgroundParticles.overflowOffset, playgroundParticles.paint.GetNormal(p), p, playgroundParticles.paint.positionLength)+playgroundParticles.playgroundCache.scatterPosition[p]; break; } } // Local space compensation calculation if (playgroundParticles.localSpace && playgroundParticles.applyLocalSpaceMovementCompensation) playgroundParticles.playgroundCache.localSpaceMovementCompensation[p] = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; } break; } } // Set initial particle values if life is 0 if (playgroundParticles.playgroundCache.life[p]==0) { if (!playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.playgroundCache.initialColor[p] = lifetimeColor; } // Particle lifetime, velocity and manipulators if (playgroundParticles.playgroundCache.rebirth[p]) { // Particle is alive if (playgroundParticles.playgroundCache.birth[p]+playgroundParticles.playgroundCache.life[p]<=PlaygroundC.globalTime+(playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p]) && (!playgroundParticles.playgroundCache.isNonBirthed[p] || playgroundParticles.onlyLifetimePositioning || playgroundParticles.onlySourcePositioning)) { // Lifetime size if (!playgroundParticles.playgroundCache.changedByPropertySize[p]) { if (playgroundParticles.applyLifetimeSize) playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(normalizedLife)*playgroundParticles.scale; else playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.scale; } // Local Manipulators for (int m = 0; m<playgroundParticles.manipulators.Count; m++) { if (playgroundParticles.manipulators[m].transform!=null) { manipulatorPosition = playgroundParticles.localSpace?playgroundParticles.manipulators[m].transform.localPosition:playgroundParticles.manipulators[m].transform.position; manipulatorDistance = playgroundParticles.manipulators[m].strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/playgroundParticles.manipulators[m].strengthDistanceEffect:10f; CalculateManipulator(playgroundParticles, playgroundParticles.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], manipulatorPosition, manipulatorDistance, playgroundParticles.localSpace); } } // Set particle size playgroundParticles.particleCache[p].size = !applyMask?playgroundParticles.playgroundCache.size[p]:0; if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning) { if (!playgroundParticles.playgroundCache.noForce[p] && playgroundParticles.playgroundCache.life[p]!=0) { // Velocity bending if (playgroundParticles.applyVelocityBending) { if (playgroundParticles.velocityBendingType==VELOCITYBENDINGTYPEC.SourcePosition) { playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect( new Vector3( playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x, playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y, playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z ), (playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.position[p]).normalized )*t; } else { playgroundParticles.playgroundCache.velocity[p] += Vector3.Reflect( new Vector3( playgroundParticles.playgroundCache.velocity[p].x*playgroundParticles.velocityBending.x, playgroundParticles.playgroundCache.velocity[p].y*playgroundParticles.velocityBending.y, playgroundParticles.playgroundCache.velocity[p].z*playgroundParticles.velocityBending.z ), (playgroundParticles.playgroundCache.previousParticlePosition[p]-playgroundParticles.playgroundCache.position[p]).normalized )*t; } } // Delta velocity if (!playgroundParticles.cameFromNonEmissionFrame && playgroundParticles.calculateDeltaMovement && playgroundParticles.playgroundCache.life[p]==0 && playgroundParticles.source!=SOURCEC.Script) { deltaVelocity = playgroundParticles.playgroundCache.targetPosition[p]-(playgroundParticles.playgroundCache.previousTargetPosition[p]); playgroundParticles.playgroundCache.velocity[p] += deltaVelocity*playgroundParticles.deltaMovementStrength; } // Set previous target position (used by delta velocity & local space movement compensation) playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; // Gravity playgroundParticles.playgroundCache.velocity[p] -= playgroundParticles.gravity*t; // Lifetime additive velocity if (playgroundParticles.applyLifetimeVelocity) playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.lifetimeVelocity.Evaluate(normalizedLife, playgroundParticles.lifetimeVelocityScale)*t; // Turbulence inside the calculation loop if (playgroundParticles.HasTurbulence() && PlaygroundC.reference.turbulenceThreadMethod==ThreadMethodComponent.InsideParticleCalculation) { if (!playgroundParticles.playgroundCache.noForce[p]) Turbulence (playgroundParticles, playgroundParticles.turbulenceSimplex, p, playgroundParticles.t, playgroundParticles.turbulenceType, playgroundParticles.turbulenceTimeScale, playgroundParticles.turbulenceScale/playgroundParticles.velocityScale, playgroundParticles.turbulenceStrength, playgroundParticles.turbulenceApplyLifetimeStrength, playgroundParticles.turbulenceLifetimeStrength); } // Damping, max velocity, constraints and final positioning if (playgroundParticles.playgroundCache.velocity[p]!=zero) { // Max velocity if (playgroundParticles.playgroundCache.velocity[p].sqrMagnitude>playgroundParticles.maxVelocity) playgroundParticles.playgroundCache.velocity[p] = Vector3.ClampMagnitude(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.maxVelocity); // Damping if (playgroundParticles.damping>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], zero, playgroundParticles.damping*t); // Axis constraints if (playgroundParticles.axisConstraints.x) playgroundParticles.playgroundCache.velocity[p].x = 0; if (playgroundParticles.axisConstraints.y) playgroundParticles.playgroundCache.velocity[p].y = 0; if (playgroundParticles.axisConstraints.z) playgroundParticles.playgroundCache.velocity[p].z = 0; // Set calculated collision position playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.previousParticlePosition[p]; // Relocate playgroundParticles.playgroundCache.position[p] += (playgroundParticles.playgroundCache.velocity[p]*playgroundParticles.velocityScale)*t; if (playgroundParticles.applyLocalSpaceMovementCompensation) { if (!playgroundParticles.applyMovementCompensationLifetimeStrength) playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p]; else playgroundParticles.playgroundCache.position[p] += playgroundParticles.playgroundCache.localSpaceMovementCompensation[p]*playgroundParticles.movementCompensationLifetimeStrength.Evaluate(normalizedLife); } // Set particle velocity to be able to stretch towards movement if (playgroundParticles.renderModeStretch) { if (playgroundParticles.applyLifetimeStretching) playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime); else playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], t*playgroundParticles.stretchSpeed); } } } else playgroundParticles.particleCache[p].velocity = Vector3.zero; } else { // Only Source Positioning / Lifetime Positioning // Set particle velocity to be able to stretch towards movement if (playgroundParticles.renderModeStretch) { if (playgroundParticles.applyLifetimeStretching) playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p], t*playgroundParticles.stretchSpeed)*playgroundParticles.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime); else playgroundParticles.particleCache[p].velocity = Vector3.Slerp (playgroundParticles.particleCache[p].velocity, playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p], t*playgroundParticles.stretchSpeed); } if (playgroundParticles.onlyLifetimePositioning && !playgroundParticles.onlySourcePositioning) { if (!playgroundParticles.playgroundCache.changedByPropertyTarget[p]) { if (playgroundParticles.lifetimePositioningUsesSourceDirection && playgroundParticles.source!=SOURCEC.Script) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+(playgroundParticles.playgroundCache.targetDirection[p]); } else { if (!playgroundParticles.applyLifetimePositioningPositionScale) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+ playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale); } else { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+ playgroundParticles.lifetimePositioning.Evaluate(normalizedLife*lifetimePositioningTimeScale, playgroundParticles.lifetimePositioningScale)* playgroundParticles.lifetimePositioningPositionScale.Evaluate(normalizedLife*lifetimePositioningTimeScale); } } } } else if (playgroundParticles.source!=SOURCEC.Script) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]; } playgroundParticles.playgroundCache.previousTargetPosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; } // Rotation if (!playgroundParticles.rotateTowardsDirection) playgroundParticles.playgroundCache.rotation[p] += playgroundParticles.playgroundCache.rotationSpeed[p]*t; else if (playgroundParticles.playgroundCache.life[p]!=0) { playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]+SignedAngle( up, playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p], playgroundParticles.rotationNormal ); } if (playgroundParticles.playgroundCache.life[p]>0) playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p]; // Set previous particle position playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.position[p]; // Send timed event if (playgroundParticles.hasTimerEvent) playgroundParticles.SendEvent(EVENTTYPEC.Time, p); } else { playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; playgroundParticles.particleCache[p].size = 0; } // Calculate lifetime evaluatedLife = (PlaygroundC.globalTime-playgroundParticles.playgroundCache.birth[p])/(playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p]); // Lifetime if (playgroundParticles.playgroundCache.life[p]<playgroundParticles.lifetime) { playgroundParticles.playgroundCache.life[p] = (playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p])*evaluatedLife; playgroundParticles.particleCache[p].lifetime = Mathf.Clamp(playgroundParticles.lifetime - playgroundParticles.playgroundCache.life[p], .1f, playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p]); if (playgroundParticles.lifetimeValueMethod==VALUEMETHOD.RandomBetweenTwoValues && playgroundParticles.playgroundCache.life[p]>playgroundParticles.lifetime-playgroundParticles.playgroundCache.lifetimeSubtraction[p]) { // Send death event for particles with lifetime subtraction if (playgroundParticles.hasEvent && !playgroundParticles.playgroundCache.isNonBirthed[p]) SendDeathEvents(playgroundParticles, p); playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; playgroundParticles.particleCache[p].size = 0; } } else { // Loop exceeded if (!playgroundParticles.loop && PlaygroundC.globalTime>playgroundParticles.simulationStarted+playgroundParticles.lifetime-.01f) { playgroundParticles.loopExceeded = true; if (playgroundParticles.loopExceededOnParticle==p && evaluatedLife>2f) { if (playgroundParticles.disableOnDone) playgroundParticles.queueEmissionHalt = true; playgroundParticles.threadHadNoActiveParticles = true; playgroundParticles.hasActiveParticles = false; } if (playgroundParticles.loopExceededOnParticle==-1) playgroundParticles.loopExceededOnParticle = p; } // Send death event for particles with full lifetime length if (playgroundParticles.hasEvent && !playgroundParticles.playgroundCache.isNonBirthed[p]) SendDeathEvents(playgroundParticles, p); // New cycle begins if (PlaygroundC.globalTime>=playgroundParticles.playgroundCache.birth[p]+playgroundParticles.playgroundCache.birthDelay[p] && !playgroundParticles.loopExceeded && playgroundParticles.source!=SOURCEC.Script && playgroundParticles.emit) { if (!playgroundParticles.playgroundCache.changedByPropertyDeath[p] || playgroundParticles.playgroundCache.changedByPropertyDeath[p] && PlaygroundC.globalTime>playgroundParticles.playgroundCache.death[p]) { Rebirth(playgroundParticles, p, playgroundParticles.internalRandom01); } else { playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; playgroundParticles.particleCache[p].size = 0; } } else { playgroundParticles.InactivateParticle(p); } } } else { // Particle is set to not rebirth playgroundParticles.InactivateParticle(p); } // Set particle position if no sync if (!playgroundParticles.syncPositionsOnMainThread) { playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.position[p]; } }// <- Particle loop ends here // Reset for next frame playgroundParticles.threadHadNoActiveParticles = noActiveParticles; playgroundParticles.cameFromNonEmissionFrame = false; playgroundParticles.isDoneThread = true; // Turbulence calculated here when using PlaygroundC.turbulenceThreadMethod of ThreadMethodComponent.OnePerSystem if (playgroundParticles.HasTurbulence() && PlaygroundC.reference.turbulenceThreadMethod==ThreadMethodComponent.OnePerSystem) { PlaygroundC.RunAsync(()=>{ for (int p = 0; p<playgroundParticles.particleCount; p++) { if (!playgroundParticles.playgroundCache.noForce[p]) Turbulence (playgroundParticles, playgroundParticles.turbulenceSimplex, p, playgroundParticles.t, playgroundParticles.turbulenceType, playgroundParticles.turbulenceTimeScale, playgroundParticles.turbulenceScale/playgroundParticles.velocityScale, playgroundParticles.turbulenceStrength, playgroundParticles.turbulenceApplyLifetimeStrength, playgroundParticles.turbulenceLifetimeStrength); } }); } }
// Rebirth of a specified particle public static void Rebirth(PlaygroundParticlesC playgroundParticles, int p, System.Random random) { if (!playgroundParticles.hasActiveParticles) return; // Set initial values playgroundParticles.playgroundCache.rebirth[p] = playgroundParticles.source==SOURCEC.Script?true:(playgroundParticles.emit && (playgroundParticles.loop || playgroundParticles.playgroundCache.isNonBirthed[p]) && playgroundParticles.playgroundCache.emission[p]); playgroundParticles.playgroundCache.isFirstLoop[p] = playgroundParticles.playgroundCache.isNonBirthed[p]; playgroundParticles.playgroundCache.isNonBirthed[p] = false; playgroundParticles.playgroundCache.birthDelay[p] = 0f; playgroundParticles.playgroundCache.life[p] = 0f; playgroundParticles.playgroundCache.birth[p] = playgroundParticles.playgroundCache.death[p]; playgroundParticles.playgroundCache.death[p] += playgroundParticles.lifetime; playgroundParticles.playgroundCache.velocity[p] = Vector3.zero; playgroundParticles.playgroundCache.noForce[p] = false; // Set new random lifetime if (playgroundParticles.applyRandomLifetimeOnRebirth) { if (playgroundParticles.lifetimeValueMethod==VALUEMETHOD.Constant) { playgroundParticles.playgroundCache.lifetimeSubtraction[p] = 0; } else { playgroundParticles.playgroundCache.lifetimeSubtraction[p] = playgroundParticles.lifetime-RandomRange (random, playgroundParticles.lifetimeMin, playgroundParticles.lifetime); } } // Set new random size if (playgroundParticles.applyRandomSizeOnRebirth) playgroundParticles.playgroundCache.initialSize[p] = RandomRange(random, playgroundParticles.sizeMin, playgroundParticles.sizeMax); // Initial velocity if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning) { // Initial global velocity if (playgroundParticles.applyInitialVelocity) { if (playgroundParticles.applyRandomInitialVelocityOnRebirth) playgroundParticles.playgroundCache.initialVelocity[p] = RandomRange(random, playgroundParticles.initialVelocityMin, playgroundParticles.initialVelocityMax); playgroundParticles.playgroundCache.velocity[p] = playgroundParticles.playgroundCache.initialVelocity[p]; // Give this spawning particle its velocity shape if (playgroundParticles.applyInitialVelocityShape) playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f), playgroundParticles.initialVelocityShapeScale)); } // Initial local velocity if (playgroundParticles.applyInitialLocalVelocity && playgroundParticles.source!=SOURCEC.Script) { playgroundParticles.playgroundCache.initialLocalVelocity[p] = RandomRange(random, playgroundParticles.initialLocalVelocityMin, playgroundParticles.initialLocalVelocityMax); playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.playgroundCache.targetDirection[p]; // Give this spawning particle its local velocity shape if (playgroundParticles.applyInitialVelocityShape) playgroundParticles.playgroundCache.velocity[p] = Vector3.Scale(playgroundParticles.playgroundCache.velocity[p], playgroundParticles.initialVelocityShape.Evaluate((p*1f)/(playgroundParticles.particleCount*1f), playgroundParticles.initialVelocityShapeScale)); } // Initial stretch if (playgroundParticles.renderModeStretch) { if (playgroundParticles.playgroundCache.velocity[p]!=Vector3.zero) playgroundParticles.particleCache[p].velocity = playgroundParticles.playgroundCache.velocity[p]; else playgroundParticles.particleCache[p].velocity = playgroundParticles.stretchStartDirection; } else playgroundParticles.particleCache[p].velocity = Vector3.zero; } if (playgroundParticles.source==SOURCEC.Script) { // Velocity for script mode if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning) playgroundParticles.playgroundCache.velocity[p] += playgroundParticles.scriptedEmissionVelocity; playgroundParticles.playgroundCache.targetPosition[p] = playgroundParticles.scriptedEmissionPosition; playgroundParticles.particleCache[p].velocity = playgroundParticles.playgroundCache.velocity[p]; } if (playgroundParticles.playgroundCache.rebirth[p]) { // Source Scattering if (playgroundParticles.applySourceScatter && playgroundParticles.source!=SOURCEC.Script) { if (playgroundParticles.playgroundCache.scatterPosition[p]==Vector3.zero || playgroundParticles.applyRandomScatterOnRebirth) playgroundParticles.playgroundCache.scatterPosition[p] = RandomRange(random, playgroundParticles.sourceScatterMin, playgroundParticles.sourceScatterMax); } else playgroundParticles.playgroundCache.scatterPosition[p] = Vector3.zero; if (!playgroundParticles.onlyLifetimePositioning) { playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]; } else if (!playgroundParticles.onlySourcePositioning) { Vector3 evalLifetimePosition = playgroundParticles.lifetimePositioning.Evaluate(0f, playgroundParticles.lifetimePositioningScale); if (playgroundParticles.lifetimePositioningUsesSourceDirection) evalLifetimePosition = playgroundParticles.playgroundCache.targetDirection[p]; playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.targetPosition[p]+evalLifetimePosition; playgroundParticles.particleCache[p].position = playgroundParticles.playgroundCache.targetPosition[p]+evalLifetimePosition; playgroundParticles.playgroundCache.previousParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]+evalLifetimePosition; playgroundParticles.playgroundCache.collisionParticlePosition[p] = playgroundParticles.playgroundCache.targetPosition[p]+evalLifetimePosition; } if (playgroundParticles.applyInitialColorOnRebirth) { playgroundParticles.particleCache[p].color = playgroundParticles.playgroundCache.initialColor[p]; playgroundParticles.playgroundCache.color[p] = playgroundParticles.playgroundCache.initialColor[p]; } } else { playgroundParticles.particleCache[p].position = PlaygroundC.initialTargetPosition; } // Set new random rotation if (playgroundParticles.applyRandomRotationOnRebirth && !playgroundParticles.rotateTowardsDirection) playgroundParticles.playgroundCache.initialRotation[p] = RandomRange(random, playgroundParticles.initialRotationMin, playgroundParticles.initialRotationMax); if (!playgroundParticles.rotateTowardsDirection) playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]; else { Vector3 particleDir; if (!playgroundParticles.onlySourcePositioning&&playgroundParticles.onlyLifetimePositioning) particleDir = (playgroundParticles.playgroundCache.position[p]+playgroundParticles.lifetimePositioning.Evaluate(.01f, playgroundParticles.lifetimePositioningScale))-playgroundParticles.playgroundCache.position[p]; else particleDir = playgroundParticles.playgroundCache.velocity[p]; playgroundParticles.playgroundCache.rotation[p] = playgroundParticles.playgroundCache.initialRotation[p]+SignedAngle( Vector3.up, particleDir, playgroundParticles.rotationNormal ); } playgroundParticles.particleCache[p].rotation = playgroundParticles.playgroundCache.rotation[p]; // Set size if (playgroundParticles.applyLifetimeSize) { //playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.lifetimeSize.Evaluate(0f)*playgroundParticles.scale; } else playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p]*playgroundParticles.scale; playgroundParticles.particleCache[p].size = p>=playgroundParticles.particleMask?playgroundParticles.playgroundCache.size[p]:0; // Set color gradient id if (playgroundParticles.colorSource==COLORSOURCEC.LifetimeColors && playgroundParticles.lifetimeColors.Count>0) { playgroundParticles.lifetimeColorId++;playgroundParticles.lifetimeColorId=playgroundParticles.lifetimeColorId%playgroundParticles.lifetimeColors.Count; playgroundParticles.playgroundCache.lifetimeColorId[p] = playgroundParticles.lifetimeColorId; } // Reset manipulators influence playgroundParticles.playgroundCache.changedByProperty[p] = false; playgroundParticles.playgroundCache.changedByPropertyColor[p] = false; playgroundParticles.playgroundCache.changedByPropertyColorLerp[p] = false; playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p] = false; playgroundParticles.playgroundCache.changedByPropertySize[p] = false; playgroundParticles.playgroundCache.changedByPropertyTarget[p] = false; playgroundParticles.playgroundCache.changedByPropertyDeath[p] = false; playgroundParticles.playgroundCache.propertyTarget[p] = 0; playgroundParticles.playgroundCache.propertyId[p] = 0; playgroundParticles.playgroundCache.propertyColorId[p] = 0; playgroundParticles.playgroundCache.manipulatorId[p] = 0; // Send birth event if (playgroundParticles.events.Count>0 && playgroundParticles.playgroundCache.rebirth[p]) playgroundParticles.SendEvent(EVENTTYPEC.Birth, p); if (playgroundParticles.hasEventManipulatorLocal) { for (int i = 0; i<playgroundParticles.manipulators.Count; i++) { if (playgroundParticles.manipulators[i].trackParticles && playgroundParticles.manipulators[i].sendEventBirth && playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.targetPosition[p], playgroundParticles.manipulators[i].transform.position)) { playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p); playgroundParticles.manipulators[i].SendParticleEventBirth(); } } } if (playgroundParticles.hasEventManipulatorGlobal) { for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) { if (PlaygroundC.reference.manipulators[i].trackParticles && PlaygroundC.reference.manipulators[i].sendEventBirth && PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.targetPosition[p], PlaygroundC.reference.manipulators[i].transform.position)) { playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p); PlaygroundC.reference.manipulators[i].SendParticleEventBirth(); } } } }