/// <summary> /// Prepares all values for calculation which is not thread-safe. /// </summary> /// <returns><c>true</c>, if threaded calculations was prepared, <c>false</c> otherwise.</returns> public bool PrepareThreadedCalculations() { // Component disabled? if (!enabled) return false; // Auto-Pause (start/stop calculation when particle system is out of view. if (pauseCalculationWhenInvisible) { bool skipFrustumCheck = false; #if UNITY_EDITOR if (!UnityEditor.EditorApplication.isPlaying) { calculate = true; skipFrustumCheck = true; } #endif if (!skipFrustumCheck && Camera.main!=null && PlaygroundC.frustumPlanes!=null) { bool shouldRender = GeometryUtility.TestPlanesAABB(PlaygroundC.frustumPlanes, new Bounds(calculationTriggerTransform==null?particleSystemTransform.position+calculationTriggerOffset:calculationTriggerTransform.position+calculationTriggerOffset, calculationTriggerSize)); if (!shouldRender && calculate && !particleSystemRenderer.isVisible) { calculate = false; #if UNITY_EDITOR if (!didHierarchyRepaint) UnityEditor.EditorApplication.RepaintHierarchyWindow(); didHierarchyRepaint = true; #endif return false; } else if (shouldRender && !calculate) { calculate = true; #if UNITY_EDITOR if (!didHierarchyRepaint) UnityEditor.EditorApplication.RepaintHierarchyWindow(); didHierarchyRepaint = true; #endif if (prewarm) {Start(); return false;} else SetParticleTimeNow (thisInstance); } } } else if (!calculate) { calculate = true; #if UNITY_EDITOR if (!didHierarchyRepaint) UnityEditor.EditorApplication.RepaintHierarchyWindow(); didHierarchyRepaint = true; #endif if (prewarm) {Start(); return false;} else SetParticleTimeNow (thisInstance); } if (!calculate) return false; // Has the calculation been paused previously? if (cameFromNonCalculatedFrame && hasActiveParticles) { StartCoroutine(Boot()); cameFromNonCalculatedFrame = false; return false; } // Apply locked position if (applyLockPosition) { if (lockPositionIsLocal) particleSystemTransform.localPosition = lockPosition; else particleSystemTransform.position = lockPosition; } // Apply locked rotation if (applyLockRotation) { if (lockRotationIsLocal) particleSystemTransform.localRotation = Quaternion.Euler(lockRotation); else particleSystemTransform.rotation = Quaternion.Euler(lockRotation); } // Apply locked scale if (applyLockScale) particleSystemTransform.localScale = lockScale; // Update active particle check hasActiveParticles = !threadHadNoActiveParticles || emit&&loop&&particleCount>0; if (!hasActiveParticles) { if (disableOnDone) queueEmissionHalt = true; } realSimulationTime = particleTimescale*Time.timeScale; cancelDeltaPositioningOnSync = false; // Prepare Source positions if (PlaygroundC.reference.IsDoneThread() && isDoneThread) { stRot = Quaternion.identity; stDir = new Vector3(); } localSpace = (shurikenParticleSystem.simulationSpace == ParticleSystemSimulationSpace.Local); overflow = (overflowOffset!=Vector3.zero); skinnedWorldObjectReady = false; renderModeStretch = particleSystemRenderer2.renderMode==ParticleSystemRenderMode.Stretch; particleSystemRotation = particleSystemTransform.rotation; particleSystemInverseRotation = particleSystemRotation; particleSystemInverseRotation.x = -particleSystemInverseRotation.x; particleSystemInverseRotation.y = -particleSystemInverseRotation.y; particleSystemInverseRotation.z = -particleSystemInverseRotation.z; particleSystemInverseRotation.w = -particleSystemInverseRotation.w; // Prepare turbulence if (!onlySourcePositioning && !onlyLifetimePositioning && turbulenceType!=TURBULENCETYPE.None) { if (turbulenceType==TURBULENCETYPE.Simplex && turbulenceSimplex==null) turbulenceSimplex = new SimplexNoise(); } if (emit) { switch (source) { case SOURCEC.Script: break; case SOURCEC.State: if (states.Count>0) { if (activeState>states.Count-1) activeState = states.Count-1; if (activeState<0) activeState = 0; if (states[activeState].stateTransform!=null) { if (isDoneThread) { if (!states[activeState].initialized) states[activeState].Initialize(); stRot = states[activeState].stateTransform.rotation; states[activeState].UpdateMatrix(localSpace); } if (localSpace && (states[activeState].stateTransform.parent==particleSystemTransform || states[activeState].stateTransform==particleSystemTransform) && isDoneThread) { cancelDeltaPositioningOnSync = true; stRot = Quaternion.Euler (Vector3.zero); } } } else return false; break; case SOURCEC.Transform: // Handle the availability of Playground Transforms (list of transform wrapper classes). // The single sourceTransform is an old method which needs to be copied over if available. if (sourceTransforms==null) { sourceTransforms = new List<PlaygroundTransformC>(); } if (sourceTransforms.Count==0) { sourceTransforms.Add(new PlaygroundTransformC()); sourceTransforms[0].transform = sourceTransform!=null?sourceTransform:particleSystemTransform; } if (isDoneThread) { for (int i = 0; i<sourceTransforms.Count; i++) { if (!sourceTransforms[i].Update()) return false; if (IsLocalSpace()) { psTransformNum = -1; sourceTransforms[i].SetLocalPosition(particleSystemTransform); sourceTransforms[i].SetPostitionAsLocal(); if (sourceTransforms[i].IsSameAs (particleSystemTransform)) { sourceTransforms[i].SetZeroRotation(); psTransformNum = i; } } } } if (isDoneThread) { stRot = sourceTransform.rotation; } if (localSpace && sourceTransform==particleSystemTransform && isDoneThread) { cancelDeltaPositioningOnSync = true; stRot = Quaternion.Euler (Vector3.zero); } break; case SOURCEC.WorldObject: // Handle vertex data in active World Object if (worldObject.gameObject!=null) { if (worldObject.gameObject.GetInstanceID()!=worldObject.cachedId || !worldObject.initialized) worldObject = NewWorldObject(worldObject.gameObject.transform); if (worldObject.mesh!=null) { if (isDoneThread) { stRot = worldObject.transform.transform.rotation; } if (localSpace && isDoneThread) { cancelDeltaPositioningOnSync = true; stRot = Quaternion.Euler (Vector3.zero); } worldObject.UpdateMatrix(localSpace); worldObject.updateNormals = worldObjectUpdateNormals; if (worldObjectUpdateVertices) worldObject.Update(); } else return false; } else return false; break; case SOURCEC.SkinnedWorldObject: // Handle vertex data in active Skinned World Object if (skinnedWorldObject.gameObject!=null) { if (skinnedWorldObject.gameObject.GetInstanceID()!=skinnedWorldObject.cachedId) { skinnedWorldObject = NewSkinnedWorldObject(skinnedWorldObject.gameObject.transform, skinnedWorldObject.downResolution); } } skinnedWorldObjectReady = skinnedWorldObject.gameObject!=null && skinnedWorldObject.mesh!=null; if (PlaygroundC.reference.skinnedMeshThreadMethod==ThreadMethodComponent.OneForAll && PlaygroundC.reference.IsFirstUnsafeAutomaticFrames() && !PlaygroundC.reference.IsDoneThreadSkinnedMeshes()) return false; if (skinnedWorldObjectReady) { if (isDoneThread) { stRot = skinnedWorldObject.transform.rotation; stDir = skinnedWorldObject.transform.TransformDirection (overflowOffset); } skinnedWorldObject.updateNormals = worldObjectUpdateNormals; if (Time.frameCount%PlaygroundC.skinnedUpdateRate==0) { if (worldObjectUpdateVertices) skinnedWorldObject.MeshUpdate(); skinnedWorldObject.BoneUpdate(); // Forced update for skinned mesh vertices on main-thread if (forceSkinnedMeshUpdateOnMainThread) { skinnedWorldObject.Update(); } } } else return false; break; case SOURCEC.Paint: if (paint.initialized) { if (isDoneThread) { stRot = particleSystemTransform.rotation; } if (paint.positionLength>0) { for (int p = 0; p<particleCache.Length; p++) { paint.Update(p); } } else return false; } else { paint.Initialize (); return false; } break; case SOURCEC.Projection: if (projection.projectionTexture!=null && projection.projectionTransform!=null) { if (!projection.initialized) projection.Initialize(); stRot = projection.projectionTransform.rotation; if (localSpace) shurikenParticleSystem.simulationSpace = ParticleSystemSimulationSpace.World; if (projection.liveUpdate || !projection.hasRefreshed) { projection.UpdateSource(); projection.Update(); stDir = projection.projectionTransform.TransformDirection (overflowOffset); projection.hasRefreshed = true; } } else return false; break; case SOURCEC.Spline: if (splines.Count>0) { for (int i = 0; i<splines.Count; i++) if (splines[i]==null || !splines[i].enabled || !splines[i].IsReady()) return false; if (isDoneThread) { stRot = particleSystemTransform.rotation; if (localSpace) { cancelDeltaPositioningOnSync = true; stRot = Quaternion.Euler (Vector3.zero); } } } else return false; break; } } // Collision detection (runs on main-thread) if (collision) Collisions(thisInstance); // Prepare Particle colors stateReadyForTextureColor = (source==SOURCEC.State && states[activeState].stateTexture!=null && states[activeState].colorLength>0); hasEventManipulatorLocal = false; hasEventManipulatorGlobal = false; hasGlobalAffectingManipulators = false; manipulatorEventCount = 0; thisLayer = particleSystemGameObject.layer; // Prepare Local Manipulators for (int m = 0; m<manipulators.Count; m++) { manipulators[m].Update(); manipulators[m].transform.SetLocalPosition(particleSystemTransform); manipulators[m].SetLocalTargetsPosition(particleSystemTransform); if (manipulators[m].trackParticles) { hasEventManipulatorLocal = true; manipulatorEventCount++; } } // Prepare Global Manipulators from this local space, bundle the thread call in case any Global Manipulator is tracking particles if (PlaygroundC.reference.HasEnabledGlobalManipulators()) { for (int m = 0; m<PlaygroundC.reference.manipulators.Count; m++) { if ((PlaygroundC.reference.manipulators[m].affects.value & 1<<GetLayer())!=0) { hasGlobalAffectingManipulators = true; PlaygroundC.reference.manipulators[m].transform.SetLocalPosition(particleSystemTransform); if (PlaygroundC.reference.manipulators[m].trackParticles && PlaygroundC.reference.threadMethod!=ThreadMethod.NoThreads) { threadMethod = ThreadMethodLocal.OneForAll; hasEventManipulatorGlobal = true; manipulatorEventCount++; } } } } hasSeveralManipulatorEvents = (manipulatorEventCount>1); // Prepare events hasEvent = events.Count>0; hasTimerEvent = false; if (hasEvent) { for (int i = 0; i<events.Count; i++) { events[i].Initialize(); if (events[i].initializedTarget && events[i].broadcastType!=EVENTBROADCASTC.EventListeners) if (!events[i].target.eventControlledBy.Contains (thisInstance)) events[i].target.eventControlledBy.Add (thisInstance); if (events[i].eventType==EVENTTYPEC.Time && ((events[i].broadcastType!=EVENTBROADCASTC.EventListeners && events[i].initializedTarget) || events[i].broadcastType==EVENTBROADCASTC.EventListeners && events[i].initializedEvent)) { hasTimerEvent = events[i].UpdateTime(); } } } // All good! Next step is to send all particles to calculation. return true; }
public IEnumerator Boot() { if (particleSystemGameObject.activeInHierarchy && particleSystemGameObject.activeSelf && !isLoading && enabled) { while (PlaygroundC.reference==null) yield return null; // Check if particle system must be combined on threaded calls to cope with particle tracking if (PlaygroundC.reference.HasEnabledGlobalManipulators()) { for (int m = 0; m<PlaygroundC.reference.manipulators.Count; m++) { if (PlaygroundC.reference.manipulators[m].trackParticles && (PlaygroundC.reference.manipulators[m].affects.value & 1<<GetLayer())!=0 && PlaygroundC.reference.threadMethod!=ThreadMethod.NoThreads) threadMethod = ThreadMethodLocal.OneForAll; } } // Update time localTime = PlaygroundC.globalTime; lastTimeUpdated = localTime; // Update id if needed for (int i = 0; i<PlaygroundC.reference.particleSystems.Count; i++) { if (PlaygroundC.reference.particleSystems[i].particleSystemId==particleSystemId && PlaygroundC.reference.particleSystems[i]!=this) { particleSystemId = PlaygroundC.reference.particleSystems.Count-1; break; } } // Refresh if (!isYieldRefreshing) StartCoroutine(YieldedRefresh()); } }