using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using PlaygroundSplines; namespace ParticlePlayground { [RequireComponent (typeof(ParticleSystem))] /// /// The PlaygroundParticlesC class is a Particle Playground system driven by the Playground Manager (PlaygroundC). A Particle Playground system contains settings and data for altering a Shuriken component. /// [ExecuteInEditMode()] public class PlaygroundParticlesC : MonoBehaviour { /************************************************************************************************************************************************* PlaygroundParticlesC variables *************************************************************************************************************************************************/ // Particle Playground settings /// /// The particle source method for distributing particles upon birth. /// [HideInInspector] public SOURCEC source; /// /// Current active state (when using state as source). /// [HideInInspector] public int activeState; /// /// If emission of particles is active on this PlaygroundParticles. /// [HideInInspector] public bool emit = true; /// /// Should a particle re-emit when reaching the end of its lifetime? /// [HideInInspector] public bool loop = true; /// /// Should particles be removed instantly when you set emit to false? /// [HideInInspector] public bool clearParticlesOnEmissionStop = false; /// /// Should the GameObject of this PlaygroundParticlesC disable when not looping? /// [HideInInspector] public bool disableOnDone = false; /// /// The routine that should run when particle simulation has ended. /// [HideInInspector] public ONDONE disableOnDoneRoutine; /// /// The rate to update this PlaygroundParticles. /// [HideInInspector] public int updateRate = 1; /// /// Determins if this particle system should calculate (can be overrided by PlaygroundC.calculate). This will automatically enable/disable when pauseCalculationWhenInvisible is set to true. /// [HideInInspector] public bool calculate = true; /// /// Calculate the delta movement force of this particle system. /// [HideInInspector] public bool calculateDeltaMovement = true; /// /// The strength to multiply delta movement with. /// [HideInInspector] public float deltaMovementStrength = 10f; /// /// The minimum strength to multiply delta movement with. This will apply when using deltaMovementStrengthValueMethod of RandomBetweenTwoValues. /// [HideInInspector] public float minDeltaMovementStrength = 0; [HideInInspector] public VALUEMETHOD deltaMovementStrengthValueMethod; /// /// The current world object will change its vertices over time. This produces memory garbage with quantity based upon the mesh vertices. /// [HideInInspector] public bool worldObjectUpdateVertices = false; /// /// The current world object will change its normals over time. This produces memory garbage with quantity based upon the mesh normals. /// [HideInInspector] public bool worldObjectUpdateNormals = false; /// /// The method to set the Lifetime Sorting: Nearest Neighbor/Reversed with. /// [HideInInspector] public NEARESTNEIGHBORORIGINMETHOD nearestNeighborOriginMethod; /// /// The initial source position when using lifetime sorting of Nearest Neighbor/Reversed and nearestNeighborOriginMethod of NEARESTNEIGHBORORIGINMETHOD.SourcePoint. /// This will let you sort the lifetime from a generated particle birth position. /// [HideInInspector] public int nearestNeighborOrigin = 0; /// /// The initial source position when using lifetime sorting of Nearest Neighbor/Reversed and nearestNeighborOriginMethod of NEARESTNEIGHBORORIGINMETHOD.Vector3. /// This will let you sort the lifetime from a Vector3 in world space. /// [HideInInspector] public Vector3 nearestNeighborOriginVector3; /// /// The initial source position when using lifetime sorting of Nearest Neighbor/Reversed and nearestNeighborOriginMethod of NEARESTNEIGHBORORIGINMETHOD.Transform. /// This will let you sort the lifetime from a Transform's position in world space. /// [HideInInspector] public Transform nearestNeighborOriginTransform; /// /// The amount of particles within this PlaygroundParticlesC object. /// [HideInInspector] public int particleCount; /// /// The percentage to emit of particleCount in bursts from this PlaygroundParticles. /// [HideInInspector] public float emissionRate = 1f; /// /// The method to calculate Overflow Offset with. /// [HideInInspector] public OVERFLOWMODEC overflowMode = OVERFLOWMODEC.SourceTransform; /// /// The offset direction and magnitude when particle count exceeds source count. /// [HideInInspector] public Vector3 overflowOffset; /// /// Should source position scattering be applied? /// [HideInInspector] public bool applySourceScatter = false; /// /// The minimum spread of source position scattering. /// [HideInInspector] public Vector3 sourceScatterMin; /// /// The maximum spread of source position scattering. /// [HideInInspector] public Vector3 sourceScatterMax; /// /// The scale of source scatter. This can be changed over time to move the scattering seamlessly. /// [HideInInspector] public Vector3 scatterScale = new Vector3(1f,1f,1f); [HideInInspector] public MINMAXVECTOR3METHOD sourceScatterMethod; /// /// Sort mode for particle lifetime. /// [HideInInspector] public SORTINGC sorting = SORTINGC.Scrambled; /// /// Custom sorting for particle lifetime (when sorting is set to Custom). /// [HideInInspector] public AnimationCurve lifetimeSorting; /// /// Minimum size of particles. /// [HideInInspector] public float sizeMin = 1f; /// /// Maximum size of particles. /// [HideInInspector] public float sizeMax = 1f; /// /// The scale of minimum and maximum particle size. /// [HideInInspector] public float scale = 1f; /// /// Minimum initial particle rotation. /// [HideInInspector] public float initialRotationMin; /// /// Maximum initial particle rotation. /// [HideInInspector] public float initialRotationMax; /// /// Minimum amount to rotate a particle over time. /// [HideInInspector] public float rotationSpeedMin; /// /// Maximum amount to rotate a particle over time. /// [HideInInspector] public float rotationSpeedMax; /// /// Should the particles rotate towards their movement direction. The rotationNormal will determine from which angle the rotation is based on. /// [HideInInspector] public bool rotateTowardsDirection = false; /// /// The rotation direction normal when rotating towards direction (always normalized value). /// [HideInInspector] public Vector3 rotationNormal = -Vector3.forward; /// /// The method to apply lifetime values. /// [HideInInspector] public VALUEMETHOD lifetimeValueMethod; /// /// The maximum life of a particle in seconds. /// [HideInInspector] public float lifetime; /// /// The minimum life of a particle when using lifetimeValueMethod of RandomBetweenTwoValues. /// [HideInInspector] public float lifetimeMin = 0; /// /// The offset of lifetime in this particle system. /// [HideInInspector] public float lifetimeOffset; /// /// The emission during lifetime. This will compress the Lifetime Sorting pattern over the total lifetime. /// This can be used to have particles live longer than the otherwise obvious repeating pattern. /// [HideInInspector] public float lifetimeEmission = 1f; /// /// The minimum Shuriken lifetime clamps the lifetime value for each particle. /// When a Shuriken particle reaches 0 it will result in being removed from screen, where a noticable flicker will occur if the particle has the same birth as death position. /// Upon using Texture Sheet Animation you may want this to be set to 0 if your particles has a short lifetime (below 1) to get all tiles into the animation. /// [HideInInspector] public float minShurikenLifetime = .08f; /// /// Should lifetime size affect each particle? /// [HideInInspector] public bool applyLifetimeSize = true; /// /// The size over lifetime of each particle. /// [HideInInspector] public AnimationCurve lifetimeSize; /// /// Should particle array size affect each particle? This will multiply the size of each particle depending on its position in the particle array. /// [HideInInspector] public bool applyParticleArraySize; /// /// The size over particle array of each particle. This will multiply the size of each particle depending on its position in the particle array. /// [HideInInspector] public AnimationCurve particleArraySize; /// /// Should the particles transition back to their source position during their lifetime? Use transitionBackToSourceAmount to set the strength by a normalized AnimationCurve. /// [HideInInspector] public bool transitionBackToSource = false; /// /// The amount to transition back to the source position by a normalized AnimationCurve. /// [HideInInspector] public AnimationCurve transitionBackToSourceAmount; /// /// Should the particles only position on their source (and not apply any forces)? /// [HideInInspector] public bool onlySourcePositioning = false; /// /// Should the particles only position by lifetime positioning Vector3AnimationCurves? /// [HideInInspector] public bool onlyLifetimePositioning = false; /// /// The lifetime positioning of particles using a Vector3AnimationCurveC. This will annihilate any forces and only move particles on the X, Y and Z Animation Curves. /// [HideInInspector] public Vector3AnimationCurveC lifetimePositioning; /// /// Should scale over time of lifetime positioning apply? /// [HideInInspector] public bool applyLifetimePositioningTimeScale = false; /// /// Should scale of position of lifetime positioning apply? /// [HideInInspector] public bool applyLifetimePositioningPositionScale = false; /// /// The scale of time for lifetime positioning. /// [HideInInspector] public AnimationCurve lifetimePositioningTimeScale; /// /// The scale of positioning for lifetime positioning. /// [HideInInspector] public AnimationCurve lifetimePositioningPositionScale; /// /// The overall scale of lifetime positioning. /// [HideInInspector] public float lifetimePositioningScale = 1f; /// /// Should lifetime positioning use the direction normal of the source? /// [HideInInspector] public bool lifetimePositioningUsesSourceDirection = false; /// /// Should lifetime velocity affect particles? /// [HideInInspector] public bool applyLifetimeVelocity = false; /// /// The velocity over lifetime of each particle. /// [HideInInspector] public Vector3AnimationCurveC lifetimeVelocity; /// /// The lifetime velocity scale. /// [HideInInspector] public float lifetimeVelocityScale = 1f; /// /// Should initial velocity affect particles? /// [HideInInspector] public bool applyInitialVelocity = false; /// /// The minimum starting velocity of each particle. /// [HideInInspector] public Vector3 initialVelocityMin; /// /// The maximum starting velocity of each particle. /// [HideInInspector] public Vector3 initialVelocityMax; /// /// Determines if the value of initial velocity should be rectangular or spherical. /// [HideInInspector] public MINMAXVECTOR3METHOD initialVelocityMethod; /// /// Should initial local velocity affect particles? /// [HideInInspector] public bool applyInitialLocalVelocity = false; /// /// The minimum starting velocity of each particle with normal or transform direction. /// [HideInInspector] public Vector3 initialLocalVelocityMin; /// /// The maximum starting velocity of each particle with normal or transform direction. /// [HideInInspector] public Vector3 initialLocalVelocityMax; /// /// Determines if the value of initial local velocity should be rectangular or spherical. /// [HideInInspector] public MINMAXVECTOR3METHOD initialLocalVelocityMethod; /// /// Should the initial velocity shape be applied on particle re/birth? /// [HideInInspector] public bool applyInitialVelocityShape = false; /// /// The amount of velocity to apply of the spawning particle's initial/local velocity in form of a Vector3AnimationCurve. /// [HideInInspector] public Vector3AnimationCurveC initialVelocityShape; /// /// The scale of initial velocity shape. /// [HideInInspector] public float initialVelocityShapeScale = 1f; /// /// Should bending affect particles velocity? /// [HideInInspector] public bool applyVelocityBending; /// /// The amount to bend velocity of each particle. /// [HideInInspector] public Vector3 velocityBending; /// /// The type of velocity bending. /// [HideInInspector] public VELOCITYBENDINGTYPEC velocityBendingType; /// /// The constant force towards gravitational vector. /// [HideInInspector] public Vector3 gravity; /// /// The maximum positive- and negative velocity of each particle. /// [HideInInspector] public float maxVelocity = 100f; /// /// The force axis constraints of each particle. /// [HideInInspector] public PlaygroundAxisConstraintsC axisConstraints = new PlaygroundAxisConstraintsC(); /// /// Particles inertia over time. /// [HideInInspector] public float damping; /// /// The overall scale of velocity. /// [HideInInspector] public float velocityScale = 1f; /// /// The color over lifetime. /// [HideInInspector] public Gradient lifetimeColor; /// /// The colors over lifetime (if Color Source is set to LifetimeColors). /// [HideInInspector] public List lifetimeColors = new List(); /// /// The source to read color from (fallback on Lifetime Color if no source color is available). /// [HideInInspector] public COLORSOURCEC colorSource = COLORSOURCEC.Source; /// /// Should the source color use alpha from Lifetime Color instead of the source's original alpha? /// [HideInInspector] public bool sourceUsesLifetimeAlpha; /// /// The method to color particles with. Either evaluated by their lifetime or by their position in the ParticleCache array. /// [HideInInspector] public COLORMETHOD colorMethod; /// /// Determines if particles should get a secondary alpha applied from the arrayColorAlpha gradient when using colorMethod of COLORMETHOD.ParticleArray. /// [HideInInspector] public bool arrayColorUsesAlpha; /// /// The array color alpha. /// [HideInInspector] public Gradient arrayColorAlpha; /// /// Should the movement of the particle system transform when in local simulation space be compensated for? /// [HideInInspector] public bool applyLocalSpaceMovementCompensation = true; /// /// Should particles get a new random size upon rebirth? /// [HideInInspector] public bool applyRandomSizeOnRebirth = true; /// /// Should particles get a new random velocity upon rebirth? /// [HideInInspector] public bool applyRandomInitialVelocityOnRebirth = true; /// /// Should particles get a new random rotation upon rebirth? /// [HideInInspector] public bool applyRandomRotationOnRebirth = true; /// /// Should particles get a new scatter position upon rebirth? /// [HideInInspector] public bool applyRandomScatterOnRebirth = false; /// /// Should particles get their initial calculated color upon rebirth? (Can resolve flickering upon rebirth.) /// [HideInInspector] public bool applyInitialColorOnRebirth = false; /// /// Should particles get a new random lifetime upon rebirth? /// [HideInInspector] public bool applyRandomLifetimeOnRebirth = true; /// /// Should particle birth position be adjusted with the delta time from birth to current? This will make the particles appear linearly. /// [HideInInspector] public bool applyDeltaOnRebirth = true; /// /// Should each birthing particle calculate the local manipulators immediately? /// [HideInInspector] public bool calculateManipulatorOnRebirth = false; /// /// Should the particle system pause calculation upon becoming invisible? /// [HideInInspector] public bool pauseCalculationWhenInvisible = false; /// /// Should the calculation trigger size gizmo be visible in Scene View? /// [HideInInspector] public bool calculationTriggerSizeGizmo = false; /// /// The calculation trigger transform determines the origin of the rendered particle area. This will affect the calculation trigger when pauseCalculationWhenInvisible is set to true. /// [HideInInspector] public Transform calculationTriggerTransform; /// /// The calculation trigger size determines the size of the rendered particle area. This will affect the calculation trigger when pauseCalculationWhenInvisible is set to true. /// [HideInInspector] public Vector3 calculationTriggerSize = new Vector3(1f,1f,1f); /// /// The calculation trigger offset determines the offset from the calculationTriggerTransform position. This will affect the calculation trigger when pauseCalculationWhenInvisible is set to true. /// [HideInInspector] public Vector3 calculationTriggerOffset = new Vector3(); /// /// Should the particle system force Play() when GameObject is outside of camera view? (Fix for Shuriken stop rendering.) /// [HideInInspector] public bool forceVisibilityWhenOutOfFrustrum = true; /// /// Should each particle's position be synced with main-threaad? Use this when dealing with moving source objects or if you experience a laggy particle movement. /// [HideInInspector] public bool syncPositionsOnMainThread = false; /// /// Should the particle system force itself to remain in lockPosition? /// [HideInInspector] public bool applyLockPosition = false; /// /// Should the particle system force itself to remain in lockRotation? /// [HideInInspector] public bool applyLockRotation = false; /// /// Should the particle system force itself to remain in lockScale? /// [HideInInspector] public bool applyLockScale = false; /// /// The locked position is considered local. /// [HideInInspector] public bool lockPositionIsLocal = false; /// /// The locked rotation is considered local. /// [HideInInspector] public bool lockRotationIsLocal = false; /// /// The locked position. /// [HideInInspector] public Vector3 lockPosition = Vector3.zero; /// /// The locked rotation. /// [HideInInspector] public Vector3 lockRotation = Vector3.zero; /// /// The locked scale. /// [HideInInspector] public Vector3 lockScale = new Vector3(1f,1f,1f); /// /// Should the movementCompensationLifetimeStrength affect local space movement compensation? /// [HideInInspector] public bool applyMovementCompensationLifetimeStrength = false; /// /// The strength of movement compensation over particles lifetime /// [HideInInspector] public AnimationCurve movementCompensationLifetimeStrength; /// /// Determines if masking should be applied to the particles. /// [HideInInspector] public bool applyParticleMask; /// /// The masked amount of particles. The particleMaskTime will determine if the particles should fade in/out. /// [HideInInspector] public int particleMask = 0; /// /// The time it takes to mask in/out particles when using particleMask. /// [HideInInspector] public float particleMaskTime = 0f; /// /// The method to distribute the sorting mask when particleMask is above 0. /// [HideInInspector] public MASKSORTINGC particleMaskSorting; /// /// The speed of stretching to reach full effect. /// [HideInInspector] public float stretchSpeed = 1f; /// /// Should the start direction of particle stretching be applied? /// [HideInInspector] public bool applyStretchStartDirection = false; /// /// The starting direction of stretching if all initial velocity is zero. /// [HideInInspector] public Vector3 stretchStartDirection = Vector3.zero; /// /// Should lifetime stretching be applied? /// [HideInInspector] public bool applyLifetimeStretching = false; /// /// The lifetime stretching of stretched particles. /// [HideInInspector] public AnimationCurve stretchLifetime; /// /// The multithreading method how this particle system should calculate. Use this to bypass the Playground Manager's threadMethod. /// [HideInInspector] public ThreadMethodLocal threadMethod; /// /// Determines if multithreading should be used to initialize all caches for the particle system. /// The tradeoff of sparing the main thread from initializing all caches is that it can result in the particle system taking a couple of frames before it's ready to start emission. /// [HideInInspector] public bool multithreadedStartup = true; // Source Script variables /// /// When using Emit() the index will point to the next particle in pool to emit. /// [HideInInspector] public int scriptedEmissionIndex; /// /// When using Emit() the passed in position will determine the position for this particle. /// [HideInInspector] public Vector3 scriptedEmissionPosition; /// /// When using Emit() the passed in velocity will determine the speed and direction for this particle. /// [HideInInspector] public Vector3 scriptedEmissionVelocity; /// /// When using Emit() the passed in color will decide the color for this particle if colorSource is set to COLORSOURCEC.Source. /// [HideInInspector] public Color scriptedEmissionColor = Color.white; /// /// When using Emit() the passed in scriptedLifetime will determine the lifetime of the particle. /// [HideInInspector] float scriptedLifetime = 0; // Collision detection /// /// Determines if particles can collide. Enable this if you want particles to continuously look for colliders of type collisionType (2D/3D). Particle collision will run on main-thread. /// [HideInInspector] public bool collision = false; /// /// Should particles affect rigidbodies? The mass determines how much they will affect the rigidobdy. /// [HideInInspector] public bool affectRigidbodies = true; [HideInInspector] public bool inverseRigidbodyCollision = false; /// /// The mass of a particle (calculated in collision with rigidbodies). /// [HideInInspector] public float mass = .01f; /// /// The spherical radius of a particle used upon collision. /// [HideInInspector] public float collisionRadius = 1f; /// /// The layers these particles will collide with. /// [HideInInspector] public LayerMask collisionMask; [HideInInspector] public List collisionExclusion; /// /// The amount a particle will loose of its lifetime on collision. /// [HideInInspector] public float lifetimeLoss = 0f; /// /// The amount a particle will bounce on collision. /// [HideInInspector] public float bounciness = .5f; /// /// The minimum amount of random bounciness (seen as negative offset from the collided surface's normal direction). /// [HideInInspector] public Vector3 bounceRandomMin; /// /// The maximum amount of random bounciness (seen as positive offset from the collided surface's normal direction). /// [HideInInspector] public Vector3 bounceRandomMax; /// /// The Playground Colliders of this particle system. A Playground Collider is an infinite collision plane based on a Transform in the scene. /// [HideInInspector] public List colliders; /// /// The type of collision. This determines if 2D- or 3D raycasting should be used. /// [HideInInspector] public COLLISIONTYPEC collisionType; /// /// Minimum collision depth of Raycast2D. /// [HideInInspector] public float minCollisionDepth = 0f; /// /// Maximum collision depth of Raycast2D. /// [HideInInspector] public float maxCollisionDepth = 0f; /// /// Determines if particles should stick to their collided surface. /// [HideInInspector] public bool stickyCollisions = false; /// /// The sticky collisions offset from the collided surface normal. /// [HideInInspector] public float stickyCollisionsSurfaceOffset = 0; [HideInInspector] public LayerMask stickyCollisionsMask = -1; /// /// Determines if collisions should be calculated using Vector3.Distance (if set to true) or Vector3.SqrMagnitude (if set to false). /// [HideInInspector] public bool collisionPrecision = false; /// /// Determines if collisions should be cached even if stickyCollisions isn't set to true. Enable this if you want to extend your scripts upon the collision information for the particles when not using sticky collisions. /// Having this disabled will minimize transform component lookups during collisions. /// [HideInInspector] public bool forceCollisionCaching = false; [HideInInspector] public bool maskedParticlesBypassCollision = true; // States (source) /// /// The list of States for this PlaygroundParticles. A State is Source data from a texture or mesh which determines where particles will be positioned upon birth. /// public List states = new List(); // Splines /// /// The splines used as Source. Particle birth positions will populate along the spline using the list of particles as normalized time on the curves of the spline. To offset the time use splineTimeOffset. /// [HideInInspector] public List splines; [HideInInspector] public float splineTimeOffset; [HideInInspector] public bool treatAsOneSpline; int splineIndex = 0; // Scene objects (source) /// /// A mesh as source calculated within the scene. /// [HideInInspector] public WorldObject worldObject = new WorldObject(); /// /// A skinned mesh as source calculated within the scene. /// [HideInInspector] public SkinnedWorldObject skinnedWorldObject = new SkinnedWorldObject(); [HideInInspector] public bool forceSkinnedMeshUpdateOnMainThread = false; /// /// A transform calculated within the scene. /// [HideInInspector] public Transform sourceTransform; [HideInInspector] public List sourceTransforms; [HideInInspector] public bool treatAsOneTransform; int transformIndex = 0; [HideInInspector] public PlaygroundParticlesC otherParticleSource; [HideInInspector] public SOURCEBIRTHMETHOD otherParticleSourceMethod; // Paint /// /// The paint source of this PlaygroundParticles. /// [HideInInspector] public PaintObjectC paint; // Projection /// /// The projection source of this PlaygroundParticles. /// [HideInInspector] public ParticleProjectionC projection; // Manipulators /// /// The list of Local Manipulator Objects handled by this PlaygroundParticlesC object. /// public List manipulators; // Events /// /// List of event objects handled by this PlaygroundParticlesC object. /// [HideInInspector] public List events; // Cache /// /// Data for each particle. /// [NonSerialized] public PlaygroundCache playgroundCache = new PlaygroundCache(); /// /// The particle pool. /// [NonSerialized] public ParticleSystem.Particle[] particleCache; [NonSerialized] public CollisionCache collisionCache; // Snapshots /// /// Saved data of properties (positions, velocities, colors etc.). /// [HideInInspector] public List snapshots = new List(); /// /// Should the particle system load stored data from start? /// [HideInInspector] public bool loadFromStart = false; /// /// Which data should be loaded (if loadFromStart is true). /// [HideInInspector] public int loadFrom = 0; /// /// Should a transition occur whenever a Load is issued? /// [HideInInspector] public bool loadTransition = false; /// /// The type of transition to occur whenever a Load is issued. /// [HideInInspector] public TRANSITIONTYPEC loadTransitionType; /// /// The time for load transition in seconds. /// [HideInInspector] public float loadTransitionTime = 1f; /// /// The storage of position data if this is a snapshot. /// [HideInInspector] public PlaygroundCache snapshotData; /// /// The global time the snapshot was made. /// [HideInInspector] public float timeOfSnapshot = 0; /// /// Is this particle system a snapshot? /// public bool isSnapshot = false; // Components /// /// This ParticleSystem (Shuriken) component. /// [HideInInspector] public ParticleSystem shurikenParticleSystem; /// /// The id of this PlaygroundParticlesC object. /// [HideInInspector] public int particleSystemId; /// /// The GameObject of a PlaygroundParticlesC object. /// [HideInInspector] public GameObject particleSystemGameObject; /// /// The Transform of a PlaygroundParticlesC object. /// [HideInInspector] public Transform particleSystemTransform; /// /// The Renderer of a PlaygroundParticlesC object. /// [HideInInspector] public Renderer particleSystemRenderer; /// /// The ParticleSystemRenderer of a PlaygroundParticlesC object. /// [HideInInspector] public ParticleSystemRenderer particleSystemRenderer2; /// /// The PlaygroundParticlesC that is controlling this particle system. /// [HideInInspector] public List eventControlledBy = new List(); // Turbulence /// /// The Simplex Turbulence object. /// SimplexNoise turbulenceSimplex; /// /// The type of turbulence. /// [HideInInspector] public TURBULENCETYPE turbulenceType = TURBULENCETYPE.None; /// /// The turbulence strength. /// [HideInInspector] public float turbulenceStrength = 10f; /// /// The turbulence resolution scale. A higher value will generate a more dense grid. /// [HideInInspector] public float turbulenceScale = 1f; /// /// The turbulence time scale. /// [HideInInspector] public float turbulenceTimeScale = 1f; /// /// Should Turbulence Lifetime Strength apply? /// [HideInInspector] public bool turbulenceApplyLifetimeStrength = false; /// /// The Turbulence Lifetime Strength. Use this to control how much turbulence will affect the particle over its lifetime. /// [HideInInspector] public AnimationCurve turbulenceLifetimeStrength; [HideInInspector] public bool prewarm = false; [HideInInspector] public float prewarmTime = 1f; [HideInInspector] public int prewarmCycles = 16; /// /// The simulation time scale for all particles within this system. Set time scale to 0 to pause a particle system. /// [HideInInspector] public float particleTimescale = 1f; // Internally used variables int thisLayer; bool isPrewarming; bool inTransition = false; int previousParticleCount = -1; float previousEmissionRate = 1f; bool renderModeStretch = false; float previousSizeMin; float previousSizeMax; float previousInitialRotationMin; float previousInitialRotationMax; float previousRotationSpeedMin; float previousRotationSpeedMax; Vector3 previousVelocityMin; Vector3 previousVelocityMax; Vector3 previousLocalVelocityMin; Vector3 previousLocalVelocityMax; MINMAXVECTOR3METHOD previousLocalVelocityMethod; MINMAXVECTOR3METHOD previousVelocityMethod; SORTINGC previousSorting; int previousNearestNeighborOrigin; NEARESTNEIGHBORORIGINMETHOD previousNearestNeighborOriginMethod; Vector3 previousNearestNeighborOriginVector3; Transform previousNearestNeighborOriginTransform; Vector3 nearestNeighborOriginTransformPosition; MASKSORTINGC previousMaskSorting; VALUEMETHOD previousLifetimeValueMethod; float previousLifetime; float previousLifetimeMin; float previousLifetimeEmission; bool previousEmission = true; bool previousLoop; float emissionStopped = 0f; bool queueEmissionHalt = false; bool hasEmitted = false; int lifetimeColorId = 0; System.Random internalRandom01; float lastTimeUpdated = 0f; PlaygroundEventParticle eventParticle = new PlaygroundEventParticle(); [NonSerialized] public bool cameFromNonCalculatedFrame = false; [NonSerialized] public bool cameFromNonEmissionFrame = true; [NonSerialized] public float localTime = 0f; [NonSerialized] public float localDeltaTime = 0f; [NonSerialized] public int previousActiveState; [NonSerialized] public float simulationStarted; [NonSerialized] public bool inPlayback = false; [NonSerialized] public bool loopExceeded = false; [NonSerialized] public int loopExceededOnParticle; [NonSerialized] public bool isReadyForThreadedCalculations = false; [NonSerialized] public bool hasActiveParticles = true; [NonSerialized] public bool isDoneThread = true; [NonSerialized] public bool threadHadNoActiveParticles = false; bool hasEventManipulatorLocal = false; bool hasEventManipulatorGlobal = false; bool hasSeveralManipulatorEvents = false; bool hasGlobalAffectingManipulators = false; Quaternion particleSystemRotation; Quaternion particleSystemInverseRotation; int psTransformNum = -1; Vector3 manipulatorFix = new Vector3(0,.0001f,0); PlaygroundParticlesC thisInstance; float t; bool cancelDeltaPositioningOnSync; Quaternion stCompensationRot; Quaternion stCompensationRotPrev; Quaternion stRot; Vector3 stDir; bool localSpace; bool overflow; bool skinnedWorldObjectReady; bool stateReadyForTextureColor; int manipulatorEventCount; bool hasEvent; bool hasTimerEvent; bool hasCollisionCache = false; /************************************************************************************************************************************************* PlaygroundParticlesC functions *************************************************************************************************************************************************/ /// /// Clones the settings of this Particle Playground system into the passed reference. Note that you additionally need to use CopySaveDataTo() if you want to clone the Snapshots. /// /// Playground particles. public void CopyTo (PlaygroundParticlesC playgroundParticles) { // Playground variables playgroundParticles.source = source; playgroundParticles.activeState = activeState; playgroundParticles.emit = emit; playgroundParticles.loop = loop; playgroundParticles.clearParticlesOnEmissionStop = clearParticlesOnEmissionStop; playgroundParticles.disableOnDone = disableOnDone; playgroundParticles.disableOnDoneRoutine = disableOnDoneRoutine; playgroundParticles.updateRate = updateRate; playgroundParticles.calculate = calculate; playgroundParticles.calculateDeltaMovement = calculateDeltaMovement; playgroundParticles.deltaMovementStrength = deltaMovementStrength; playgroundParticles.minDeltaMovementStrength = minDeltaMovementStrength; playgroundParticles.deltaMovementStrengthValueMethod = deltaMovementStrengthValueMethod; playgroundParticles.worldObjectUpdateVertices = worldObjectUpdateVertices; playgroundParticles.worldObjectUpdateNormals = worldObjectUpdateNormals; playgroundParticles.nearestNeighborOrigin = nearestNeighborOrigin; playgroundParticles.nearestNeighborOriginMethod = nearestNeighborOriginMethod; playgroundParticles.nearestNeighborOriginTransform = nearestNeighborOriginTransform; playgroundParticles.nearestNeighborOriginVector3 = nearestNeighborOriginVector3; playgroundParticles.particleCount = particleCount; playgroundParticles.emissionRate = emissionRate; playgroundParticles.overflowMode = overflowMode; playgroundParticles.overflowOffset = overflowOffset; playgroundParticles.applySourceScatter = applySourceScatter; playgroundParticles.scatterScale = scatterScale; playgroundParticles.sourceScatterMin = sourceScatterMin; playgroundParticles.sourceScatterMax = sourceScatterMax; playgroundParticles.sourceScatterMethod = sourceScatterMethod; playgroundParticles.sorting = sorting; playgroundParticles.lifetimeSorting = new AnimationCurve(lifetimeSorting.keys); playgroundParticles.sizeMin = sizeMin; playgroundParticles.sizeMax = sizeMax; playgroundParticles.scale = scale; playgroundParticles.initialRotationMin = initialRotationMin; playgroundParticles.initialRotationMax = initialRotationMax; playgroundParticles.rotationSpeedMin = rotationSpeedMin; playgroundParticles.rotationSpeedMax = rotationSpeedMax; playgroundParticles.rotateTowardsDirection = rotateTowardsDirection; playgroundParticles.rotationNormal = rotationNormal; playgroundParticles.lifetime = lifetime; playgroundParticles.lifetimeValueMethod = lifetimeValueMethod; playgroundParticles.lifetimeMin = lifetimeMin; playgroundParticles.lifetimeEmission = lifetimeEmission; playgroundParticles.lifetimeOffset = lifetimeOffset; playgroundParticles.minShurikenLifetime = minShurikenLifetime; playgroundParticles.applyLifetimeSize = applyLifetimeSize; playgroundParticles.lifetimeSize = new AnimationCurve(lifetimeSize.keys); playgroundParticles.applyParticleArraySize = applyParticleArraySize; playgroundParticles.particleArraySize = new AnimationCurve(particleArraySize.keys); playgroundParticles.transitionBackToSource = transitionBackToSource; playgroundParticles.transitionBackToSourceAmount = new AnimationCurve(transitionBackToSourceAmount.keys); playgroundParticles.onlySourcePositioning = onlySourcePositioning; playgroundParticles.onlyLifetimePositioning = onlyLifetimePositioning; playgroundParticles.lifetimePositioning = lifetimePositioning.Clone(); playgroundParticles.lifetimePositioningScale = lifetimePositioningScale; playgroundParticles.lifetimePositioningUsesSourceDirection = lifetimePositioningUsesSourceDirection; playgroundParticles.lifetimePositioningTimeScale = new AnimationCurve(lifetimePositioningTimeScale.keys); playgroundParticles.lifetimePositioningPositionScale = new AnimationCurve(lifetimePositioningPositionScale.keys); playgroundParticles.applyLifetimePositioningTimeScale = applyLifetimePositioningTimeScale; playgroundParticles.applyLifetimePositioningPositionScale = applyLifetimePositioningPositionScale; playgroundParticles.axisConstraints = axisConstraints.Clone(); playgroundParticles.applyLifetimeVelocity = applyLifetimeVelocity; playgroundParticles.lifetimeVelocity = lifetimeVelocity.Clone(); playgroundParticles.lifetimeVelocityScale = lifetimeVelocityScale; playgroundParticles.applyInitialVelocity = applyInitialVelocity; playgroundParticles.initialVelocityMin = initialVelocityMin; playgroundParticles.initialVelocityMax = initialVelocityMax; playgroundParticles.initialVelocityMethod = initialVelocityMethod; playgroundParticles.applyInitialLocalVelocity = applyInitialLocalVelocity; playgroundParticles.initialLocalVelocityMin = initialLocalVelocityMin; playgroundParticles.initialLocalVelocityMax = initialLocalVelocityMax; playgroundParticles.initialLocalVelocityMethod = initialLocalVelocityMethod; playgroundParticles.applyVelocityBending = applyVelocityBending; playgroundParticles.velocityBending = velocityBending; playgroundParticles.velocityBendingType = velocityBendingType; playgroundParticles.applyInitialVelocityShape = applyInitialVelocityShape; playgroundParticles.initialVelocityShape = initialVelocityShape.Clone(); playgroundParticles.initialVelocityShapeScale = initialVelocityShapeScale; playgroundParticles.gravity = gravity; playgroundParticles.damping = damping; playgroundParticles.velocityScale = velocityScale; playgroundParticles.maxVelocity = maxVelocity; playgroundParticles.lifetimeColor.SetKeys (lifetimeColor.colorKeys, lifetimeColor.alphaKeys); playgroundParticles.colorSource = colorSource; playgroundParticles.sourceUsesLifetimeAlpha = sourceUsesLifetimeAlpha; playgroundParticles.colorMethod = colorMethod; playgroundParticles.arrayColorUsesAlpha = arrayColorUsesAlpha; playgroundParticles.arrayColorAlpha.SetKeys (arrayColorAlpha.colorKeys, arrayColorAlpha.alphaKeys); playgroundParticles.applyLocalSpaceMovementCompensation = applyLocalSpaceMovementCompensation; playgroundParticles.applyRandomSizeOnRebirth = applyRandomSizeOnRebirth; playgroundParticles.applyRandomInitialVelocityOnRebirth = applyRandomInitialVelocityOnRebirth; playgroundParticles.applyRandomRotationOnRebirth = applyRandomRotationOnRebirth; playgroundParticles.applyRandomScatterOnRebirth = applyRandomScatterOnRebirth; playgroundParticles.applyInitialColorOnRebirth = applyInitialColorOnRebirth; playgroundParticles.applyRandomLifetimeOnRebirth = applyRandomLifetimeOnRebirth; playgroundParticles.applyDeltaOnRebirth = applyDeltaOnRebirth; playgroundParticles.calculateManipulatorOnRebirth = calculateManipulatorOnRebirth; playgroundParticles.pauseCalculationWhenInvisible = pauseCalculationWhenInvisible; playgroundParticles.calculationTriggerSize = calculationTriggerSize; playgroundParticles.calculationTriggerOffset = calculationTriggerOffset; playgroundParticles.calculationTriggerTransform = calculationTriggerTransform; playgroundParticles.calculationTriggerSizeGizmo = calculationTriggerSizeGizmo; playgroundParticles.forceVisibilityWhenOutOfFrustrum = forceVisibilityWhenOutOfFrustrum; playgroundParticles.syncPositionsOnMainThread = syncPositionsOnMainThread; playgroundParticles.applyLockPosition = applyLockPosition; playgroundParticles.applyLockRotation = applyLockRotation; playgroundParticles.applyLockScale = applyLockScale; playgroundParticles.lockPositionIsLocal = lockPositionIsLocal; playgroundParticles.lockRotationIsLocal = lockRotationIsLocal; playgroundParticles.lockPosition = lockPosition; playgroundParticles.lockRotation = lockRotation; playgroundParticles.lockScale = lockScale; playgroundParticles.applyMovementCompensationLifetimeStrength = applyMovementCompensationLifetimeStrength; playgroundParticles.movementCompensationLifetimeStrength = new AnimationCurve(movementCompensationLifetimeStrength.keys); playgroundParticles.applyParticleMask = applyParticleMask; playgroundParticles.particleMask = particleMask; playgroundParticles.particleMaskTime = particleMaskTime; playgroundParticles.particleMaskSorting = particleMaskSorting; playgroundParticles.stretchSpeed = stretchSpeed; playgroundParticles.applyStretchStartDirection = applyStretchStartDirection; playgroundParticles.applyLifetimeStretching = applyLifetimeStretching; playgroundParticles.stretchLifetime = new AnimationCurve(stretchLifetime.keys); playgroundParticles.threadMethod = threadMethod; playgroundParticles.multithreadedStartup = multithreadedStartup; // Scripted source variables playgroundParticles.scriptedEmissionIndex = scriptedEmissionIndex; playgroundParticles.scriptedEmissionPosition = scriptedEmissionPosition; playgroundParticles.scriptedEmissionVelocity = scriptedEmissionVelocity; playgroundParticles.scriptedEmissionColor = scriptedEmissionColor; // Collision detection playgroundParticles.collision = collision; playgroundParticles.affectRigidbodies = affectRigidbodies; playgroundParticles.inverseRigidbodyCollision = inverseRigidbodyCollision; playgroundParticles.mass = mass; playgroundParticles.collisionRadius = collisionRadius; playgroundParticles.collisionMask = collisionMask; playgroundParticles.bounciness = bounciness; playgroundParticles.lifetimeLoss = lifetimeLoss; playgroundParticles.bounceRandomMin = bounceRandomMin; playgroundParticles.bounceRandomMax = bounceRandomMax; playgroundParticles.collisionType = collisionType; playgroundParticles.minCollisionDepth = minCollisionDepth; playgroundParticles.maxCollisionDepth = maxCollisionDepth; playgroundParticles.stickyCollisions = stickyCollisions; playgroundParticles.stickyCollisionsSurfaceOffset = stickyCollisionsSurfaceOffset; playgroundParticles.stickyCollisionsMask = stickyCollisionsMask; playgroundParticles.collisionPrecision = collisionPrecision; playgroundParticles.forceCollisionCaching = forceCollisionCaching; playgroundParticles.maskedParticlesBypassCollision = maskedParticlesBypassCollision; playgroundParticles.collisionExclusion = new List(); for (int i = 0; i(); for (int i = 0; i(); for (int i = 0; i(); for (int i = 0; i(); for (int i = 0; i(); for (int i = 0; i(); for (int i = 0; i(); for (int i = 0; i /// Copies stored data of Snapshots into a particle system (separated from CopyTo() to solve Save/Load overwrite paradox). /// /// Playground particles. public void CopySaveDataTo (PlaygroundParticlesC playgroundParticles) { playgroundParticles.snapshots = new List(); for (int i = 0; i /// Sets emission On or Off. /// /// If set to true then emit particles. public void Emit (bool setEmission) { emit = setEmission; if (emit) { simulationStarted = localTime; calculate = true; hasActiveParticles = true; threadHadNoActiveParticles = false; loopExceeded = false; loopExceededOnParticle = -1; if (thisInstance==null) thisInstance = this; particleSystemGameObject.SetActive(true); Emission(thisInstance, true, true); } else { emissionStopped = localTime; if (clearParticlesOnEmissionStop) InactivateParticles(); } previousEmission = setEmission; } /// /// Emits a single particle at previously set scripted emission position, velocity and color. /// public int Emit () { source = SOURCEC.Script; int returnIndex = scriptedEmissionIndex; EmitProcedure(scriptedEmissionPosition, scriptedEmissionVelocity, scriptedEmissionColor); return returnIndex; } /// /// Emits a single particle at position with previously set scripted emission velocity and color. /// /// Position. public int Emit (Vector3 givePosition) { source = SOURCEC.Script; int returnIndex = scriptedEmissionIndex; EmitProcedure(givePosition, scriptedEmissionVelocity, scriptedEmissionColor); return returnIndex; } /// /// Emits a single particle at position with velocity, the color will be set from the previous scripted emission color. /// /// Position. /// Velocity. public int Emit (Vector3 givePosition, Vector3 giveVelocity) { source = SOURCEC.Script; int returnIndex = scriptedEmissionIndex; EmitProcedure(givePosition, giveVelocity, scriptedEmissionColor); return returnIndex; } /// /// Emits a single particle at position with velocity and color (Source Mode SOURCEC.Script will be automatically set). /// /// Position. /// Velocity. /// Color. public int Emit (Vector3 givePosition, Vector3 giveVelocity, Color32 giveColor) { source = SOURCEC.Script; int returnIndex = scriptedEmissionIndex; EmitProcedure(givePosition, giveVelocity, giveColor); return returnIndex; } /// /// Emits number of particles set by quantity. All other values will be set from the previous scripted emission call (or as set in Inspector). /// /// Quantity. public void Emit (int quantity) { source = SOURCEC.Script; for (int i = 0; i /// Emits number of particles set by quantity, position and minimum - maximum random velocity. /// /// Quantity. /// Position. /// Random minimum velocity. /// Random maximum velocity. /// Color. public void Emit (int quantity, Vector3 givePosition, Vector3 randomVelocityMin, Vector3 randomVelocityMax, Color32 giveColor) { source = SOURCEC.Script; for (int i = 0; i /// Emits number of particles set by quantity, minimum - maximum random position and velocity. /// /// Quantity. /// Random position minimum. /// Random position max. /// Random velocity minimum. /// Random velocity max. /// Color. public void Emit (int quantity, Vector3 randomPositionMin, Vector3 randomPositionMax, Vector3 randomVelocityMin, Vector3 randomVelocityMax, Color32 giveColor) { source = SOURCEC.Script; for (int i = 0; i /// Emits a single particle with specified lifetime. This will set particle position, velocity and color from previously called emission (as set in the Source tab). /// /// Lifetime. public int Emit (float giveLifetime) { source = SOURCEC.Script; int returnIndex = scriptedEmissionIndex; scriptedLifetime = giveLifetime; EmitProcedure(scriptedEmissionPosition, scriptedEmissionVelocity, scriptedEmissionColor); scriptedLifetime = 0; return returnIndex; } /// /// Emits a single particle with specified lifetime. This will set particle velocity and color from previously called emission (as set in the Source tab). /// /// Position. /// Lifetime. public int Emit (Vector3 givePosition, float giveLifetime) { source = SOURCEC.Script; int returnIndex = scriptedEmissionIndex; scriptedLifetime = giveLifetime; EmitProcedure(givePosition, scriptedEmissionVelocity, scriptedEmissionColor); scriptedLifetime = 0; return returnIndex; } /// /// Emits a single particle with specified lifetime. This will set particle color from previously called emission (as set in the Source tab). /// /// Position. /// Velocity. /// Lifetime. public int Emit (Vector3 givePosition, Vector3 giveVelocity, float giveLifetime) { source = SOURCEC.Script; int returnIndex = scriptedEmissionIndex; scriptedLifetime = giveLifetime; EmitProcedure(givePosition, giveVelocity, scriptedEmissionColor); scriptedLifetime = 0; return returnIndex; } /// /// Emits a single particle with specified position, velocity, lifetime and color. /// /// Position. /// Velocity. /// Lifetime. /// Color. public int Emit (Vector3 givePosition, Vector3 giveVelocity, float giveLifetime, Color32 giveColor) { source = SOURCEC.Script; int returnIndex = scriptedEmissionIndex; scriptedLifetime = giveLifetime; EmitProcedure(givePosition, giveVelocity, giveColor); scriptedLifetime = 0; return returnIndex; } /// /// Thread-safe version of Emit(). /// /// Position. /// Velocity. /// Color. public void ThreadSafeEmit (Vector3 givePosition, Vector3 giveVelocity, Color32 giveColor) { EmitProcedure(givePosition, giveVelocity, giveColor); } public void ThreadSafeEmit (int quantity, Vector3 givePosition, Vector3 randomVelocityMin, Vector3 randomVelocityMax, Color32 giveColor) { for (int i = 0; i /// Internal emission procedure called upon Emit(). /// /// Give position. /// Give velocity. /// Give color. void EmitProcedure (Vector3 givePosition, Vector3 giveVelocity, Color32 giveColor) { scriptedEmissionIndex=scriptedEmissionIndex%particleCount; scriptedEmissionPosition = givePosition; scriptedEmissionVelocity = giveVelocity; scriptedEmissionColor = giveColor; hasActiveParticles = true; threadHadNoActiveParticles = false; cameFromNonCalculatedFrame = false; cameFromNonEmissionFrame = false; playgroundCache.simulate[scriptedEmissionIndex] = true; Rebirth(thisInstance, scriptedEmissionIndex, internalRandom01); if (playgroundCache.lifetimeOffset.Length!=particleCount) return; playgroundCache.initialColor[scriptedEmissionIndex] = scriptedEmissionColor; playgroundCache.lifetimeOffset[scriptedEmissionIndex] = 0; playgroundCache.life[scriptedEmissionIndex] = 0; playgroundCache.birth[scriptedEmissionIndex] = PlaygroundC.globalTime; if (scriptedLifetime==0) { playgroundCache.death[scriptedEmissionIndex] = playgroundCache.birth[scriptedEmissionIndex]+lifetime; } else { playgroundCache.death[scriptedEmissionIndex] = playgroundCache.birth[scriptedEmissionIndex]+scriptedLifetime; } playgroundCache.emission[scriptedEmissionIndex] = true; playgroundCache.scriptedColor[scriptedEmissionIndex] = giveColor; playgroundCache.isFirstLoop[scriptedEmissionIndex] = true; emit = true; previousEmission = true; simulationStarted = localTime; loopExceeded = false; loopExceededOnParticle = -1; scriptedEmissionIndex++;scriptedEmissionIndex=scriptedEmissionIndex%particleCount; } /// /// Checks if particles are still in simulation. /// /// true if this particle system is alive; otherwise, false. public bool IsAlive () { return calculate && hasActiveParticles; } /// /// Determines whether this particle system is simulated in local space. /// /// true if this particle system is simulated in local space; otherwise, false. public bool IsLocalSpace () { return localSpace; } /// /// Determines if this particle system is in a transition. /// /// true, if transition is active, false otherwise. public bool InTransition () { return inTransition; } /// /// Determines whether this particle system is loading a snapshot. /// /// true if this particle system is loading a snapshot; otherwise, false. public bool IsLoading () { return isLoading; } /// /// Determines whether this particle system is saving a snapshot. /// /// true if this particle system is saving a snapshot; otherwise, false. public bool IsSaving () { return isSaving; } /// /// Check if the particle system is ready. You should wait for IsReady() to become true before calling any scripted emission. This returns the same result as calling Initialized(). /// /// true if the particle system is ready; otherwise, false. public bool IsReady () { return initialized&&!isPrewarming; } /// /// Check if the particle system is ready. You should wait for Initialized() to become true before calling any scripted emission. /// /// true if the particle system is ready; otherwise, false. public bool Initialized () { return initialized&&!isPrewarming; } /// /// Determines whether this particle system is yield refreshing. /// /// true if this particle system is yield refreshing; otherwise, false. public bool IsYieldRefreshing () { return isYieldRefreshing; } public bool IsSettingParticleTime () { return isSettingParticleTime; } public bool IsDoneThread { get {return isDoneThread;} set {isDoneThread = value;} } /// /// Determines whether the skinned world object is ready. /// /// true if this skinned world object is ready; otherwise, false. public bool IsSkinnedWorldObjectReady () { return source==SOURCEC.SkinnedWorldObject&&skinnedWorldObjectReady; } /// /// Determines whether this particle system has turbulence active. /// /// true if this particle system has turbulence; otherwise, false. public bool HasTurbulence () { return calculate && !onlySourcePositioning && !onlyLifetimePositioning && turbulenceStrength>0 && turbulenceType!=TURBULENCETYPE.None; } /// /// Determines whether this particle system has overflow set by Overflow Offset. /// /// true if this particle system has overflow; otherwise, false. public bool HasOverflow () { return overflow; } /// /// Determines whether this particle system may be affected by one or more available Global Manipulators. /// /// true if this particle system will be affected by one or more Global Manipulators; otherwise, false. public bool HasGlobalManipulator () { return hasGlobalAffectingManipulators; } /// /// Determines whether this particle system has an initialized collision cache. /// /// true if this particle system has a collision cache; otherwise, false. public bool HasCollisionCache () { return hasCollisionCache; } /// /// Determines if a particle at specified index has collided during its lifetime. /// /// true if the particle at index has collided during its lifetime; otherwise, false. /// Index. public bool HasCollided (int index) { if (index<0||index>particleCount-1) return false; if (hasCollisionCache) return collisionCache.hasCollided[index]; return false; } /// /// Gets the particle collision position at index. /// /// The particle collision position. /// Index. public Vector3 GetCollisionPosition (int index) { if (!hasCollisionCache || index<0||index>particleCount-1) return Vector3.zero; return collisionCache.collisionPosition[index]; } /// /// Gets the particle collision normal at index. /// /// The particle collision normal. /// Index. public Vector3 GetCollisionNormal (int index) { if (!hasCollisionCache || index<0||index>particleCount-1) return Vector3.zero; return collisionCache.collisionNormal[index]; } /// /// Gets the particle sticky position at index. Note that stickyCollisions must be enabled to return expected values. /// /// The particle sticky position. /// Index. public Vector3 GetStickyPosition (int index) { if (!hasCollisionCache || index<0||index>particleCount-1) return Vector3.zero; return collisionCache.stickyPosition[index]; } /// /// Sets the particle at index to stick onto a parent transform. /// /// Index. /// Position. /// Normal. /// Parent. public void SetSticky (int index, Vector3 position, Vector3 normal, float offset, Transform parent) { if (index<0||index>particleCount-1) return; if (!hasCollisionCache) { collisionCache = new CollisionCache(particleCount); hasCollisionCache = true; } collisionCache.SetSticky(index, position, normal, stickyCollisionsSurfaceOffset, parent); } /// /// Updates the sticky particle position. /// /// Index. public void UpdateSticky (int index) { if (!hasCollisionCache || index<0||index>particleCount-1) return; collisionCache.hasCollided[index] = true; collisionCache.UpdateStickyPosition(index); playgroundCache.position[index] = collisionCache.stickyPosition[index]; particleCache[index].position = playgroundCache.position[index]; } /// /// Clears the collisions. Use this if you for example want to toggle between sticky and non-sticky particle behaviors. /// public void ClearCollisions () { if (!hasCollisionCache) return; collisionCache.ClearCollisions(); } /// /// Clears the collisions at index. Use this if you for example want to toggle between sticky and non-sticky particle behaviors. /// public void ClearCollisions (int index) { if (!hasCollisionCache || index<0||index>particleCount-1) return; collisionCache.Reset(index); } /// /// Gets the particle collision transform at index. /// /// The particle collision transform. /// Index. public Transform GetCollisionTransform (int index) { if (!hasCollisionCache || index<0||index>particleCount-1) return null; return collisionCache.collisionTransform[index]; } /// /// Gets the layer this particle system is within. This is safe to call from another thread. /// /// The layer. public int GetLayer () { return thisLayer; } /// /// Sets the random seed for the internal System.Random. /// /// Seed. public void SetRandomSeed (int seed) { internalRandom01 = new System.Random(seed); } int reportUpdateStepper = 100; bool isReportingBadUpdateRate = false; /// /// Determines whether this particle system is reporting bad update rate. You will also see the "P" icon indicator in the Hierarchy turn red in Play Mode whenever this returns true. /// /// true if this particle system is reporting bad update rate; otherwise, false. public bool IsReportingBadUpdateRate () { if (isPrewarming||inTransition) return false; reportUpdateStepper--; if (reportUpdateStepper==0) { reportUpdateStepper = 100; isReportingBadUpdateRate = localTime>1f && localDeltaTime>.04f; } return isReportingBadUpdateRate; } /// /// Gets the current delta time of a particle system's update loop. /// /// The delta time. public float GetDeltaTime () { return t; } /// /// Gets or sets when this particle system was last time updated. This is done automatically while the particle system is simulated. /// /// The last time updated. public float LastTimeUpdated { get {return lastTimeUpdated;} set {lastTimeUpdated = value;} } /// /// Gets or sets the local delta time of this particle system. This is done automatically while the particle system is simulated. /// /// The local delta time. public float LocalDeltaTime { get {return localDeltaTime;} set { localDeltaTime = value; t = value; } } /// /// Gets the running simplex algorithm. /// /// The running simplex algorithm. public SimplexNoise GetSimplex () { if (turbulenceSimplex==null) turbulenceSimplex = new SimplexNoise(); return turbulenceSimplex; } /// /// Kill the specified particle. /// /// Index of particle. public void Kill (int p) { if (p>=particleCount || p<0) return; playgroundCache.changedByPropertyDeath[p] = true; playgroundCache.life[p] = lifetime; playgroundCache.position[p] = PlaygroundC.initialTargetPosition; particleCache[p].position = playgroundCache.position[p]; playgroundCache.manipulatorId[p] = 0; } /// /// Kill the specified particle and send death events to all Manipulators tracking particles. /// /// Index of particle. public void KillAndSendManipulatorDeathEvents (int p) { if (p>=particleCount || p<0) return; playgroundCache.changedByPropertyDeath[p] = true; playgroundCache.life[p] = lifetime; playgroundCache.position[p] = PlaygroundC.initialTargetPosition; particleCache[p].position = playgroundCache.position[p]; // Check if particle has a connection to a local manipulator to send death event if (playgroundCache.manipulatorId[p]!=0) { for (int m = 0; m /// Sets a particle to no longer respond to forces. /// /// Index of particle. /// If set to true then disable force for the particle at index. public void SetNoForce (int p, bool noForce) { playgroundCache.noForce[p] = noForce; } /// /// Determines if the particle have noForce set to true /// /// true, if force is disabled, false otherwise. /// Index of particle. public bool NoForce (int p) { return playgroundCache.noForce[p]; } /// /// Translate the specified particle /// /// Particle index. /// Translation. public void Translate (int p, Vector3 translation) { p = Mathf.Clamp (p, 0, playgroundCache.position.Length); playgroundCache.position[p] += translation; particleCache[p].position = playgroundCache.position[p]; playgroundCache.isCalculatedThisFrame[p] = true; } /// /// Positions the specified particle. /// /// Particle index. /// Position. public void ParticlePosition (int p, Vector3 position) { p = Mathf.Clamp (p, 0, playgroundCache.position.Length); playgroundCache.position[p] = position; particleCache[p].position = position; playgroundCache.previousParticlePosition[p] = position; playgroundCache.isCalculatedThisFrame[p] = true; } /// /// Sets the color of the specified particle. /// /// Particle index. /// Color. public void ParticleColor (int p, Color32 color) { p = Mathf.Clamp (p, 0, playgroundCache.color.Length); playgroundCache.changedByPropertyColor[p] = true; playgroundCache.color[p] = color; SetParticleColorInternal(p, playgroundCache.color[p]); } void SetParticleColorInternal (int index, Color32 color) { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 particleCache[index].color = color; #else particleCache[index].startColor = color; #endif } Color32 GetParticleColorInternal (int index) { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 return particleCache[index].color; #else return particleCache[index].startColor; #endif } /// /// Sets the size of the specified particle. /// /// Particle index. /// Size. public void ParticleSize (int p, float size) { p = Mathf.Clamp (p, 0, playgroundCache.size.Length); playgroundCache.changedByPropertySize[p] = true; playgroundCache.size[p] = size; #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 particleCache[p].size = size; #else particleCache[p].startSize = size; #endif } /// /// Sets the velocity of the specified particle. /// /// Particle index. /// Velocity. public void ParticleVelocity (int p, Vector3 velocity) { p = Mathf.Clamp (p, 0, playgroundCache.velocity.Length); playgroundCache.velocity[p] = velocity; } /// /// Positions to transform point. /// /// Particle index. /// Position. /// Target transform. public void PositionToTransformPoint (int p, Vector3 position, Transform targetTransform) { playgroundCache.position[p] = targetTransform.TransformPoint (position); } /// /// Positions to inverse transform point. /// /// Particle index. /// Position. /// Target transform. public void PositionToInverseTransformPoint (int p, Vector3 position, Transform targetTransform) { playgroundCache.position[p] = targetTransform.InverseTransformPoint (position); } /// /// Gets the particle position. /// /// The particle position. /// Particle index. public Vector3 GetParticlePosition (int p) { return playgroundCache.position[p]; } public Vector3 GetSourcePosition (int p) { return playgroundCache.targetPosition[p]; } public void SetHasActiveParticles () { hasActiveParticles = true; threadHadNoActiveParticles = false; } /// /// Determines whether this particle system has several manipulator events. /// /// true if this particle system has several manipulator events; otherwise, false. public bool HasSeveralManipulatorEvents () { return hasSeveralManipulatorEvents; } /// /// Protects the particle from specified manipulator. /// /// Particle index. /// Manipulator. public void ProtectParticleFromManipulator (int particle, ManipulatorObjectC manipulator) { if (manipulator.transform.Update()) playgroundCache.excludeFromManipulatorId[particle] = manipulator.transform.instanceID; } /// /// Removes the particle protection. /// /// Particle index. public void RemoveParticleProtection (int particle) { playgroundCache.excludeFromManipulatorId[particle] = 0; } /// /// Determines whether this particle system is currently setting the lifetime. /// As the particle system may run on a second thread this can be used to wait until it is ready to simulate. /// /// true if this particle system is setting the lifetime; otherwise, false. public bool IsSettingLifetime () { return isSettingLifetime; } /// /// Determines whether this particle system is currently prewarming. /// /// true if this particle system is prewarming; otherwise, false. public bool IsPrewarming () { return isPrewarming; } /// /// Determines whether a particle is inside the area of a Manipulator. /// /// true if this particle is inside manipulator; otherwise, false. public bool IsParticleInsideManipulator (int particleId, ManipulatorObjectC manipulator) { return (manipulator.Contains (playgroundCache.position[particleId], (localSpace?manipulator.transform.localPosition:manipulator.transform.position))); } /// /// Gets the index of the source spline which currently is read in simulation. /// /// The spline index. public int GetSplineIndex () { return splineIndex; } /// /// Gets the index of the source transform which currently is read in simulation. /// /// The transform index. public int GetTransformIndex () { return transformIndex; } /// /// Refreshes the mask sorting. If you're using a particleMaskSorting of MASKSORTINGC.Scrambled you can use this to randomize new mask positions. /// public void RefreshMaskSorting () { playgroundCache.maskSorting = new int[particleCount]; switch (particleMaskSorting) { case MASKSORTINGC.Linear: for (int i = 0; i /// Refresh the mask sorting by sending in a custom int[] array. /// The passed in maskSortingArray should contain all numbers from 0 to particleCount-1, it will then apply non-linear masking depending on the order of the numbers in releation to the actual particle array. /// /// Mask sorting array. public void RefreshMaskSorting (int[] maskSortingArray) { playgroundCache.maskSorting = new int[particleCount]; for (int i = 0; i /// Creates a new PlaygroundParticlesC object. /// /// The playground particles. /// Images. /// Name. /// Position. /// Rotation. /// Offset. /// Particle size. /// Scale. /// Material. public static PlaygroundParticlesC CreatePlaygroundParticles (Texture2D[] images, string name, Vector3 position, Quaternion rotation, Vector3 offset, float particleSize, float scale, Material material) { PlaygroundParticlesC playgroundParticles = CreateParticleObject(name,position,rotation,particleSize,material); int[] quantityList = new int[images.Length]; int i = 0; for (; i /// Sets default settings for a PlaygroundParticlesC object. /// /// Playground particles. public static void OnCreatePlaygroundParticles (PlaygroundParticlesC playgroundParticles) { playgroundParticles.playgroundCache = new PlaygroundCache(); playgroundParticles.paint = new PaintObjectC(); playgroundParticles.states = new List(); playgroundParticles.projection = new ParticleProjectionC(); playgroundParticles.colliders = new List(); playgroundParticles.particleSystemId = PlaygroundC.particlesQuantity-1; playgroundParticles.projection.projectionTransform = playgroundParticles.particleSystemTransform; playgroundParticles.playgroundCache.initialSize = new float[playgroundParticles.particleCount]; playgroundParticles.playgroundCache.initialSize = RandomFloat(playgroundParticles.playgroundCache.initialSize.Length, playgroundParticles.sizeMin, playgroundParticles.sizeMax, playgroundParticles.internalRandom01); playgroundParticles.previousParticleCount = playgroundParticles.particleCount; playgroundParticles.lifetimeSize = new AnimationCurve(new Keyframe(0,1), new Keyframe(1,1)); playgroundParticles.shurikenParticleSystem.Emit(playgroundParticles.particleCount); playgroundParticles.shurikenParticleSystem.GetParticles(playgroundParticles.particleCache); for (int p = 0; p /// Creates a Shuriken Particle System. /// /// The particle object. /// Name. /// Position. /// Rotation. /// Particle size. /// Material. public static PlaygroundParticlesC CreateParticleObject (string name, Vector3 position, Quaternion rotation, float particleSize, Material material) { GameObject go = PlaygroundC.ResourceInstantiate("Particle Playground System"); PlaygroundParticlesC playgroundParticles = go.GetComponent(); playgroundParticles.particleSystemGameObject = go; playgroundParticles.particleSystemGameObject.name = name; playgroundParticles.shurikenParticleSystem = playgroundParticles.particleSystemGameObject.GetComponent(); playgroundParticles.particleSystemRenderer = playgroundParticles.shurikenParticleSystem.GetComponent(); playgroundParticles.particleSystemRenderer2 = playgroundParticles.shurikenParticleSystem.GetComponent() as ParticleSystemRenderer; playgroundParticles.particleSystemTransform = playgroundParticles.particleSystemGameObject.transform; playgroundParticles.sourceTransform = playgroundParticles.particleSystemTransform; playgroundParticles.source = SOURCEC.Transform; playgroundParticles.particleSystemTransform.position = position; playgroundParticles.particleSystemTransform.rotation = rotation; if (PlaygroundC.reference.autoGroup && playgroundParticles.particleSystemTransform.parent==null) playgroundParticles.particleSystemTransform.parent = PlaygroundC.referenceTransform; if (playgroundParticles.particleSystemRenderer.sharedMaterial==null) playgroundParticles.particleSystemRenderer.sharedMaterial = material; return playgroundParticles; } /// /// Creates a new WorldObject. /// /// The world object. /// Mesh transform. public static WorldObject NewWorldObject (Transform meshTransform) { WorldObject worldObject = new WorldObject(); if (meshTransform.GetComponentInChildren()) { worldObject.transform = meshTransform; worldObject.Initialize (); } else Debug.Log("Could not find a mesh in "+meshTransform.name+"."); return worldObject; } /// /// Creates a new SkinnedWorldObject. /// /// The skinned world object. /// Mesh transform. public static SkinnedWorldObject NewSkinnedWorldObject (Transform meshTransform) { SkinnedWorldObject skinnedWorldObject = new SkinnedWorldObject(); if (meshTransform.GetComponentInChildren()) { skinnedWorldObject.transform = meshTransform; skinnedWorldObject.Initialize (); } else Debug.Log("Could not find a skinned mesh in "+meshTransform.name+"."); return skinnedWorldObject; } /// /// Creates a new SkinnedWorldObject with pre-set down resolution. /// /// The skinned world object. /// Mesh transform. /// Down resolution. public static SkinnedWorldObject NewSkinnedWorldObject (Transform meshTransform, int downResolution) { SkinnedWorldObject skinnedWorldObject = NewSkinnedWorldObject(meshTransform); skinnedWorldObject.downResolution = downResolution; return skinnedWorldObject; } /// /// Creates a new PaintObject. /// /// The paint object. /// Playground particles. public static PaintObjectC NewPaintObject (PlaygroundParticlesC playgroundParticles) { PaintObjectC paintObject = new PaintObjectC(); playgroundParticles.paint = paintObject; playgroundParticles.paint.Initialize(); return paintObject; } /// /// Creates a new ParticleProjection object. /// /// The projection object. /// Playground particles. public static ParticleProjectionC NewProjectionObject (PlaygroundParticlesC playgroundParticles) { ParticleProjectionC projectionObject = new ParticleProjectionC(); playgroundParticles.projection = projectionObject; playgroundParticles.projection.Initialize(); return projectionObject; } /// /// Creates a new ManipulatorObject and attach to the Playground Manager. /// /// The manipulator object. /// Type. /// Affects. /// Manipulator transform. /// Size. /// Strength. /// Playground particles. public static ManipulatorObjectC NewManipulatorObject (MANIPULATORTYPEC type, LayerMask affects, Transform manipulatorTransform, float size, float strength, PlaygroundParticlesC playgroundParticles) { ManipulatorObjectC manipulatorObject = new ManipulatorObjectC(); manipulatorObject.type = type; manipulatorObject.affects = affects; manipulatorObject.transform.transform = manipulatorTransform; manipulatorObject.size = size; manipulatorObject.strength = strength; manipulatorObject.bounds = new Bounds(Vector3.zero, new Vector3(size, size, size)); manipulatorObject.property = new ManipulatorPropertyC(); manipulatorObject.Update(); // Add this to Playground Manager or the passed in playgroundParticles if (playgroundParticles==null) PlaygroundC.reference.manipulators.Add(manipulatorObject); else playgroundParticles.manipulators.Add(manipulatorObject); return manipulatorObject; } // Get vertices from a skinned world object in a Vector3-array public static void GetPosition (SkinnedWorldObject particleStateWorldObject, bool updateNormals) { if (updateNormals) particleStateWorldObject.normals = particleStateWorldObject.mesh.normals; Vector3[] vertices = particleStateWorldObject.mesh.vertices; BoneWeight[] weights = particleStateWorldObject.mesh.boneWeights; Matrix4x4[] bindPoses = particleStateWorldObject.mesh.bindposes; Matrix4x4[] boneMatrices = new Matrix4x4[particleStateWorldObject.renderer.bones.Length]; int i = 0; for (; i{ Matrix4x4 vertexMatrix = new Matrix4x4(); for (i = 0; i{ SetLifetimeAsyncFriendly (playgroundParticles, sorting, time); }); } else { SetLifetimeAsyncFriendly (playgroundParticles, sorting, time); } } public static void SetLifetimeAsyncFriendly (PlaygroundParticlesC playgroundParticles, SORTINGC sorting, float time) { playgroundParticles.lifetime = time; SetLifetimeSubtraction(playgroundParticles); playgroundParticles.playgroundCache.lifetimeOffset = new float[playgroundParticles.particleCount]; int pCount = playgroundParticles.playgroundCache.lifetimeOffset.Length; if (playgroundParticles.source!=SOURCEC.Script) { float lifetimeEmission = playgroundParticles.lifetimeEmission; if (!playgroundParticles.loop) lifetimeEmission*=.95f; switch (sorting) { case SORTINGC.Scrambled: for (int r = 0; r0; i--) { if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;} int r = playgroundParticles.internalRandom01.Next(0,i); float tmp = playgroundParticles.playgroundCache.lifetimeOffset[i]; playgroundParticles.playgroundCache.lifetimeOffset[i] = playgroundParticles.playgroundCache.lifetimeOffset[r]; playgroundParticles.playgroundCache.lifetimeOffset[r] = tmp; } break; case SORTINGC.Burst: // No action needed for spawning all particles at once break; case SORTINGC.Linear: float lPerc; for (int l = 0; l=0; r--) { if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;} rPerc = (rInc*1f)/(playgroundParticles.particleCount*1f); rInc++; playgroundParticles.playgroundCache.lifetimeOffset[r] = playgroundParticles.lifetime*lifetimeEmission*rPerc; } break; case SORTINGC.NearestNeighborReversed: playgroundParticles.nearestNeighborOrigin = Mathf.Clamp(playgroundParticles.nearestNeighborOrigin, 0, playgroundParticles.particleCount-1); float[] nnDist = new float[playgroundParticles.particleCount]; float nnHighest = 0; Vector3 nnrOrigin = Vector3.zero; switch (playgroundParticles.nearestNeighborOriginMethod) { case NEARESTNEIGHBORORIGINMETHOD.SourcePoint: nnrOrigin = playgroundParticles.playgroundCache.targetPosition[playgroundParticles.nearestNeighborOrigin%playgroundParticles.particleCount]; break; case NEARESTNEIGHBORORIGINMETHOD.Vector3: nnrOrigin = playgroundParticles.nearestNeighborOriginVector3; break; case NEARESTNEIGHBORORIGINMETHOD.Transform: nnrOrigin = playgroundParticles.nearestNeighborOriginTransformPosition; break; } for (int nn = 0; nnnnHighest) nnHighest = nnDist[nn%playgroundParticles.particleCount]; } if (nnHighest>0) { for (int nn = 0; nnnnrHighest) nnrHighest = nnrDist[nnr%playgroundParticles.particleCount]; } if (nnrHighest>0) { for (int nnr = 0; nnr=0; cs--) { if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;} playgroundParticles.playgroundCache.lifetimeOffset[cs] = playgroundParticles.lifetime*playgroundParticles.lifetimeEmission*playgroundParticles.lifetimeSorting.Evaluate(cs*1f/playgroundParticles.particleCount*1f); } break; } } SetParticleTimeNow(playgroundParticles); SetEmissionRate(playgroundParticles); playgroundParticles.previousLifetime = playgroundParticles.lifetime; playgroundParticles.previousLifetimeEmission = playgroundParticles.lifetimeEmission; playgroundParticles.previousNearestNeighborOrigin = playgroundParticles.nearestNeighborOrigin; playgroundParticles.previousNearestNeighborOriginMethod = playgroundParticles.nearestNeighborOriginMethod; playgroundParticles.previousNearestNeighborOriginVector3 = playgroundParticles.nearestNeighborOriginVector3; playgroundParticles.previousNearestNeighborOriginTransform = playgroundParticles.nearestNeighborOriginTransform; playgroundParticles.previousSorting = playgroundParticles.sorting; playgroundParticles.isDoneThread = true; playgroundParticles.localDeltaTime = 0f; playgroundParticles.localTime = PlaygroundC.globalTime; playgroundParticles.lastTimeUpdated = PlaygroundC.globalTime; playgroundParticles.isSettingLifetime = false; } public static void SetLifetimeSubtraction (PlaygroundParticlesC playgroundParticles) { playgroundParticles.playgroundCache.lifetimeSubtraction = new float[playgroundParticles.particleCount]; System.Random random = new System.Random(); for (int p = 0; p=playgroundParticles.lifetime-rateCount && playgroundParticles.emit); } else { playgroundParticles.playgroundCache.emission[p] = (playgroundParticles.emit && playgroundParticles.emissionRate>((p*1f)/(currentCount*1f))); } } else playgroundParticles.playgroundCache.emission[p] = false; if (playgroundParticles.playgroundCache.emission[p]) { playgroundParticles.playgroundCache.rebirth[p] = true; playgroundParticles.playgroundCache.simulate[p] = true; } else if (playgroundParticles.source==SOURCEC.Script) playgroundParticles.playgroundCache.rebirth[p] = false; } playgroundParticles.previousEmissionRate = playgroundParticles.emissionRate; playgroundParticles.hasActiveParticles = true; playgroundParticles.threadHadNoActiveParticles = false; } // Set time of particles with current time public static void SetParticleTimeNow (PlaygroundParticlesC playgroundParticles) { if (playgroundParticles.playgroundCache == null || playgroundParticles.playgroundCache.lifetimeOffset==null || playgroundParticles.playgroundCache.lifetimeOffset.Length!=playgroundParticles.particleCount) return; if (playgroundParticles.playgroundCache.life==null || playgroundParticles.playgroundCache.life.Length!=playgroundParticles.particleCount) return; playgroundParticles.isSettingParticleTime = true; if (playgroundParticles.source!=SOURCEC.Script) { float currentTime = PlaygroundC.globalTime+playgroundParticles.lifetimeOffset; if (currentTime<=0) currentTime = playgroundParticles.localTime+.1f; for (int p = 0; p0) { playgroundParticles.cameFromNonEmissionFrame = true; } else { SetParticleTimeNow(playgroundParticles); return; } for (int p = 0; p{ SetParticleCountAsyncFriendly(playgroundParticles, amount); }, ThreadPoolMethod.ThreadPool); } else SetParticleCountAsyncFriendly(playgroundParticles, amount); } static void SetParticleCountAsyncFriendly (PlaygroundParticlesC playgroundParticles, int amount) { if (!playgroundParticles.isSettingParticleCount) return; if (playgroundParticles.playgroundCache==null) playgroundParticles.playgroundCache = new PlaygroundCache(); // Rebuild Cache playgroundParticles.playgroundCache.size = new float[amount]; playgroundParticles.playgroundCache.birth = new float[amount]; playgroundParticles.playgroundCache.death = new float[amount]; playgroundParticles.playgroundCache.rebirth = new bool[amount]; playgroundParticles.playgroundCache.birthDelay = new float[amount]; playgroundParticles.playgroundCache.life = new float[amount]; playgroundParticles.playgroundCache.lifetimeSubtraction = new float[amount]; playgroundParticles.playgroundCache.rotation = new float[amount]; playgroundParticles.playgroundCache.lifetimeOffset = new float[amount]; playgroundParticles.playgroundCache.emission = new bool[amount]; playgroundParticles.playgroundCache.changedByProperty = new bool[amount]; playgroundParticles.playgroundCache.changedByPropertyColor = new bool[amount]; playgroundParticles.playgroundCache.changedByPropertyColorLerp = new bool[amount]; playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha = new bool[amount]; playgroundParticles.playgroundCache.changedByPropertySize = new bool[amount]; playgroundParticles.playgroundCache.changedByPropertyTarget = new bool[amount]; playgroundParticles.playgroundCache.changedByPropertyDeath = new bool[amount]; playgroundParticles.playgroundCache.propertyTarget = new int[amount]; playgroundParticles.playgroundCache.propertyId = new int[amount]; playgroundParticles.playgroundCache.excludeFromManipulatorId = new int[amount]; playgroundParticles.playgroundCache.propertyColorId = new int[amount]; playgroundParticles.playgroundCache.manipulatorId = new int[amount]; playgroundParticles.playgroundCache.color = new Color32[amount]; playgroundParticles.playgroundCache.scriptedColor = new Color32[amount]; playgroundParticles.playgroundCache.initialColor = new Color32[amount]; playgroundParticles.playgroundCache.lifetimeColorId = new int[amount]; playgroundParticles.playgroundCache.noForce = new bool[amount]; playgroundParticles.playgroundCache.position = new Vector3[amount]; playgroundParticles.playgroundCache.targetPosition = new Vector3[amount]; playgroundParticles.playgroundCache.targetDirection = new Vector3[amount]; playgroundParticles.playgroundCache.previousTargetPosition = new Vector3[amount]; playgroundParticles.playgroundCache.previousParticlePosition = new Vector3[amount]; playgroundParticles.playgroundCache.collisionParticlePosition = new Vector3[amount]; playgroundParticles.playgroundCache.localSpaceMovementCompensation = new Vector3[amount]; playgroundParticles.playgroundCache.scatterPosition = new Vector3[amount]; playgroundParticles.playgroundCache.velocity = new Vector3[amount]; playgroundParticles.playgroundCache.isMasked = new bool[amount]; playgroundParticles.playgroundCache.maskAlpha = new float[amount]; playgroundParticles.playgroundCache.isNonBirthed = new bool[amount]; playgroundParticles.playgroundCache.isFirstLoop = new bool[amount]; playgroundParticles.playgroundCache.simulate = new bool[amount]; playgroundParticles.playgroundCache.isCalculatedThisFrame = new bool[amount]; playgroundParticles.playgroundCache.maskSorting = null; for (int i = 0; i0) { playgroundParticles.lifetimeColorId++;playgroundParticles.lifetimeColorId=playgroundParticles.lifetimeColorId%playgroundParticles.lifetimeColors.Count; playgroundParticles.playgroundCache.lifetimeColorId[i] = playgroundParticles.lifetimeColorId; } } // Set sizes SetSizeRandom(playgroundParticles, playgroundParticles.sizeMin, playgroundParticles.sizeMax); playgroundParticles.previousSizeMin = playgroundParticles.sizeMin; playgroundParticles.previousSizeMax = playgroundParticles.sizeMax; // Set rotations playgroundParticles.playgroundCache.initialRotation = RandomFloat(amount, playgroundParticles.initialRotationMin, playgroundParticles.initialRotationMax, playgroundParticles.internalRandom01); playgroundParticles.playgroundCache.rotationSpeed = RandomFloat(amount, playgroundParticles.rotationSpeedMin, playgroundParticles.rotationSpeedMax, playgroundParticles.internalRandom01); playgroundParticles.previousInitialRotationMin = playgroundParticles.initialRotationMin; playgroundParticles.previousInitialRotationMax = playgroundParticles.initialRotationMax; playgroundParticles.previousRotationSpeedMin = playgroundParticles.rotationSpeedMin; playgroundParticles.previousRotationSpeedMax = playgroundParticles.rotationSpeedMax; // Set velocities SetVelocityRandom(playgroundParticles, playgroundParticles.initialVelocityMin, playgroundParticles.initialVelocityMax); SetLocalVelocityRandom(playgroundParticles, playgroundParticles.initialLocalVelocityMin, playgroundParticles.initialLocalVelocityMax); // Set Emission Emission(playgroundParticles, playgroundParticles.emit, false); // Make sure scatter is available first lifetime cycle if (playgroundParticles.applySourceScatter) playgroundParticles.RefreshScatter(); playgroundParticles.isDoneThread = true; playgroundParticles.isSettingParticleCount = false; } bool prevLoop; SOURCEC prevSource; /// /// Updates a PlaygroundParticlesC object (called each calculation step from PlaygroundC). /// public bool UpdateSystem () { if (isYieldRefreshing || isLoading || playgroundCache==null || thisInstance==null || !particleSystemGameObject.activeInHierarchy || !particleSystemGameObject.activeSelf || !enabled || isSettingLifetime || isSettingParticleCount || isPrewarming || inPlayback) return false; // Emission halt for disabling called from calculation thread if (Application.isPlaying && queueEmissionHalt && !IsAlive() && !loop && loopExceeded && hasEmitted) { if (disableOnDoneRoutine==ONDONE.Inactivate) { particleSystemGameObject.SetActive(false); return false; } else { DestroyImmediate(particleSystemGameObject); return false; } } // Check that particle system has emitted (used for Disable On Done routines) if (emit) hasEmitted = true; // Source if (source!=prevSource) { prevSource=source; return false; } // Particle count if (particleCount!=previousParticleCount) { SetParticleCount(thisInstance, particleCount); isDoneThread = true; if (particleCount>0) StartCoroutine(Boot()); return false; } // Particle emission if (emit!=previousEmission) { Emit(emit); return false; } // Particle loop enabled again if (loop!=previousLoop) { if (emit && loop) Emit(true); previousLoop = loop; return false; } // Particle size if (sizeMin!=previousSizeMin || sizeMax!=previousSizeMax) SetSizeRandom(thisInstance, sizeMin, sizeMax); // Particle rotation if (initialRotationMin!=previousInitialRotationMin || initialRotationMax!=previousInitialRotationMax) SetInitialRotationRandom(thisInstance, initialRotationMin, initialRotationMax); if (rotationSpeedMin!=previousRotationSpeedMin || rotationSpeedMax!=previousRotationSpeedMax) SetRotationRandom(thisInstance, rotationSpeedMin, rotationSpeedMax); // Particle velocity if (applyInitialVelocity) { if (initialVelocityMin!=previousVelocityMin || initialVelocityMax!=previousVelocityMax || initialVelocityMethod!=previousVelocityMethod || playgroundCache.initialVelocity==null || playgroundCache.initialVelocity.Length!=particleCount) { SetVelocityRandom(thisInstance, initialVelocityMin, initialVelocityMax); return false; } } // Particle local velocity if (applyInitialLocalVelocity) { if (initialLocalVelocityMin!=previousLocalVelocityMin || initialLocalVelocityMax!=previousLocalVelocityMax || initialLocalVelocityMethod!=previousLocalVelocityMethod || playgroundCache.initialLocalVelocity==null || playgroundCache.initialLocalVelocity.Length!=particleCount) { SetLocalVelocityRandom(thisInstance, initialLocalVelocityMin, initialLocalVelocityMax); return false; } } // Particle life if (previousLifetime!=lifetime || lifetimeValueMethod==VALUEMETHOD.Constant && previousLifetimeValueMethod!=lifetimeValueMethod) { Start(); return false; } // Lifetime sorting if (previousNearestNeighborOriginMethod!=nearestNeighborOriginMethod || (sorting==SORTINGC.NearestNeighbor||sorting==SORTINGC.NearestNeighborReversed)&& (nearestNeighborOriginMethod==NEARESTNEIGHBORORIGINMETHOD.SourcePoint && previousNearestNeighborOrigin!=nearestNeighborOrigin || nearestNeighborOriginMethod==NEARESTNEIGHBORORIGINMETHOD.Vector3 && previousNearestNeighborOriginVector3!=nearestNeighborOriginVector3 || nearestNeighborOriginMethod==NEARESTNEIGHBORORIGINMETHOD.Transform && previousNearestNeighborOriginTransform!=nearestNeighborOriginTransform) || previousSorting!=sorting || previousLifetimeEmission!=lifetimeEmission) { Start(); return false; } // Particle total lifetime if (lifetimeValueMethod==VALUEMETHOD.RandomBetweenTwoValues) { if (previousLifetimeMin!=lifetimeMin || previousLifetime!=lifetime || previousLifetimeValueMethod!=lifetimeValueMethod) { SetLifetimeSubtraction(thisInstance); previousLifetimeMin=lifetimeMin; } } // Particle emission rate if (previousEmissionRate!=emissionRate && source!=SOURCEC.Script) { SetEmissionRate(thisInstance); } // Particle state change if (source==SOURCEC.State && activeState!=previousActiveState) { if (states[activeState].positionLength>particleCount) SetParticleCount(thisInstance, states[activeState].positionLength); previousActiveState = activeState; } // All good! return true; } /// /// Updates the Shuriken component. This sets all particles previously calculated and syncs particle positioning if syncPositionsOnMainThread is true. /// public void UpdateShuriken () { // Early out if no update should occur if (!isReadyForThreadedCalculations || !calculate || inPlayback || source==SOURCEC.Spline&&splines.Count==0) return; // Collision detection (runs on main-thread) if (collision) Collisions(thisInstance); if (!particleSystemRenderer.enabled) return; // Sync positions to main-thread if (syncPositionsOnMainThread || collision || (forceSkinnedMeshUpdateOnMainThread && source==SOURCEC.SkinnedWorldObject && (onlySourcePositioning||onlyLifetimePositioning))) { if (particleCache.Length!=playgroundCache.position.Length || particleCount != playgroundCache.isCalculatedThisFrame.Length) return; if (playgroundCache.life.Length == particleCount && playgroundCache.simulate.Length == particleCount) { bool setSource = onlySourcePositioning && !cancelDeltaPositioningOnSync; float mslt = minShurikenLifetime<.00001f? 0.00001f : minShurikenLifetime; float initYpos = PlaygroundC.initialTargetPosition.y; for (int p = 0; p0&&particleCache[p].position.y!=initYpos)?playgroundCache.size[p]:0; #else particleCache[p].startSize = (playgroundCache.maskAlpha[p]>0&&particleCache[p].position.y!=initYpos)?playgroundCache.size[p]:0; #endif // Rotation particleCache[p].rotation = playgroundCache.rotation[p]; } } } // Assign all particles into the particle system if (!inTransition && particleCache!=null && particleCache.Length>0 && calculate && hasActiveParticles && !isPrewarming) { shurikenParticleSystem.SetParticles(particleCache, particleCache.Length); } else if (particleCache.Length==0 && hasActiveParticles) { shurikenParticleSystem.Clear(); shurikenParticleSystem.Stop(); hasActiveParticles = false; isDoneThread = true; } // Make sure this particle system is playing if (hasActiveParticles && calculate) if (shurikenParticleSystem.isPaused || shurikenParticleSystem.isStopped) shurikenParticleSystem.Play(); } // Initial target position public static void SetInitialTargetPosition (PlaygroundParticlesC playgroundParticles, Vector3 position, bool refreshParticleSystem) { for (int p = 0; p().SetParticles(playgroundParticles.particleCache, playgroundParticles.particleCache.Length); } // Set emission of PlaygroundParticlesC object public static void Emission (PlaygroundParticlesC playgroundParticles, bool emission, bool callRestEmission) { playgroundParticles.previousEmission = emission; if (emission) { PlaygroundC.RunAsync(()=>{ for (int p = 0; p /// Prepares all values for calculation which is not thread-safe. /// /// true, if threaded calculations was prepared, false otherwise. public bool PrepareThreadedCalculations () { // Component disabled? if (!enabled) return false; // In playback? if (inPlayback) 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) { loopExceeded = true; 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(); } if (sourceTransforms.Count==0) { sourceTransforms.Add(new PlaygroundTransformC()); sourceTransforms[0].transform = sourceTransform!=null?sourceTransform:particleSystemTransform; } if (isDoneThread) { for (int i = 0; i0) { for (int p = 0; p0) { for (int i = 0; i0); hasEventManipulatorLocal = false; hasEventManipulatorGlobal = false; hasGlobalAffectingManipulators = false; manipulatorEventCount = 0; thisLayer = particleSystemGameObject.layer; // Prepare Local Manipulators for (int m = 0; m1); // Prepare events hasEvent = events.Count>0; hasTimerEvent = false; if (hasEvent) { for (int i = 0; i { if (playgroundParticles.isDoneThread || !playgroundParticles.isReadyForThreadedCalculations) return; ThreadedCalculations(playgroundParticles); }); } } public static void NewCalculatedThread (PlaygroundParticlesC[] playgroundParticles) { for (int i = 0; i { for (int i = 0; i0) currentColor = lifetimeColors[playgroundCache.lifetimeColorId[p]%lifetimeColors.Count].gradient.Evaluate(colorMethod==COLORMETHOD.Lifetime?normalizedLife:normalizedP); else currentColor = lifetimeColor.Evaluate(colorMethod==COLORMETHOD.Lifetime?normalizedLife:normalizedP); break; } } else { currentColor = GetParticleColorInternal(p); if (playgroundCache.changedByPropertyColorKeepAlpha[p]) currentColor.a = (byte)(255*Mathf.Clamp(lifetimeColor.Evaluate(colorMethod==COLORMETHOD.Lifetime?normalizedLife:normalizedP).a, 0, currentColor.a)); } if (colorMethod==COLORMETHOD.ParticleArray && arrayColorUsesAlpha) { Color arrayAlphaColor; arrayAlphaColor = arrayColorAlpha.Evaluate(normalizedLife); currentColor.a = (byte)(255*arrayAlphaColor.a); } // Assign mask alpha if (applyParticleMask) { currentColor.a = (byte)Mathf.Lerp (0, currentColor.a, playgroundCache.maskAlpha[p]); } return currentColor; } /// /// Sets the source position for a particle from the selected source method in the particle system. /// /// Particle Index. void SetSourcePosition (int p) { switch (source) { case SOURCEC.State: if (!overflow) { playgroundCache.targetPosition[p] = states[activeState].GetLocalPosition(p); } else { switch (overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundCache.targetPosition[p] = states[activeState].GetLocalPosition(p)+GetOverflow2(overflowOffset, p, states[activeState].positionLength); break; case OVERFLOWMODEC.World: playgroundCache.targetPosition[p] = states[activeState].GetLocalPosition(p)+GetOverflow2(overflowOffset, p, states[activeState].positionLength); break; case OVERFLOWMODEC.SourcePoint: playgroundCache.targetPosition[p] = states[activeState].GetLocalPosition(p)+GetOverflow2(overflowOffset, states[activeState].GetLocalNormal(p), p, states[activeState].positionLength); break; } } break; case SOURCEC.Transform: if (sourceTransforms.Count>0) { if (treatAsOneTransform) transformIndex = (int)((((p*1f)/(particleCount*1f))*sourceTransforms.Count))%sourceTransforms.Count; else transformIndex = p%sourceTransforms.Count; if (transformIndex>=sourceTransforms.Count) { transformIndex=sourceTransforms.Count-1; } if (!overflow) { playgroundCache.targetPosition[p] = sourceTransforms[transformIndex].position; } else { switch (overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundCache.targetPosition[p] = sourceTransforms[transformIndex].transformMatrix.MultiplyPoint3x4(GetOverflow2(overflowOffset, treatAsOneTransform? p%(particleCount/sourceTransforms.Count) : p/sourceTransforms.Count, 1)); break; case OVERFLOWMODEC.World: playgroundCache.targetPosition[p] = sourceTransforms[transformIndex].position+GetOverflow2(overflowOffset, treatAsOneTransform? p%(particleCount/sourceTransforms.Count) : p/sourceTransforms.Count, 1); break; case OVERFLOWMODEC.SourcePoint: playgroundCache.targetPosition[p] = sourceTransforms[transformIndex].transformMatrix.MultiplyPoint3x4(GetOverflow2(overflowOffset, treatAsOneTransform? p%(particleCount/sourceTransforms.Count) : p/sourceTransforms.Count, 1)); break; } } } break; case SOURCEC.WorldObject: if (!overflow) { playgroundCache.targetPosition[p] = worldObject.transformMatrix.MultiplyPoint3x4( worldObject.vertexPositions[p%worldObject.vertexPositions.Length] ); } else { switch (overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundCache.targetPosition[p] = worldObject.transformMatrix.MultiplyPoint3x4( worldObject.vertexPositions[p%worldObject.vertexPositions.Length]+GetOverflow2(overflowOffset, p, worldObject.vertexPositions.Length) ); break; case OVERFLOWMODEC.World: playgroundCache.targetPosition[p] = worldObject.transformMatrix.MultiplyPoint3x4( worldObject.vertexPositions[p%worldObject.vertexPositions.Length] )+GetOverflow2(overflowOffset, p, worldObject.vertexPositions.Length); break; case OVERFLOWMODEC.SourcePoint: playgroundCache.targetPosition[p] = worldObject.transformMatrix.MultiplyPoint3x4( worldObject.vertexPositions[p%worldObject.vertexPositions.Length]+GetOverflow2(overflowOffset, worldObject.normals[p%worldObject.normals.Length], p, worldObject.vertexPositions.Length) ); break; } } break; case SOURCEC.SkinnedWorldObject: int downResolution = skinnedWorldObject.downResolution; int downResolutionP = Mathf.RoundToInt(p*downResolution)%skinnedWorldObject.vertexPositions.Length; if (!overflow) { playgroundCache.targetPosition[p] = skinnedWorldObject.vertexPositions[downResolutionP]; } else { switch (overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundCache.targetPosition[p] = skinnedWorldObject.vertexPositions[downResolutionP]+ GetOverflow2( stDir, p, skinnedWorldObject.vertexPositions.Length/downResolution ); break; case OVERFLOWMODEC.World: playgroundCache.targetPosition[p] = skinnedWorldObject.vertexPositions[downResolutionP]+ GetOverflow2( overflowOffset, p, skinnedWorldObject.vertexPositions.Length/downResolution ); break; case OVERFLOWMODEC.SourcePoint: playgroundCache.targetPosition[p] = skinnedWorldObject.vertexPositions[downResolutionP]+ GetOverflow2( overflowOffset, skinnedWorldObject.normals[downResolutionP], p, skinnedWorldObject.vertexPositions.Length/downResolution ); break; } } break; case SOURCEC.Projection: if (!overflow) { playgroundCache.targetPosition[p] = projection.GetPosition(p); } else { switch (overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundCache.targetPosition[p] = projection.GetPosition(p)+GetOverflow2(stDir, p, projection.positionLength); break; case OVERFLOWMODEC.World: playgroundCache.targetPosition[p] = projection.GetPosition(p)+GetOverflow2(overflowOffset, p, projection.positionLength); break; case OVERFLOWMODEC.SourcePoint: playgroundCache.targetPosition[p] = projection.GetPosition(p)+GetOverflow2(Vector3Scale(stDir, projection.GetNormal(p)), p, projection.positionLength); break; } } break; case SOURCEC.Paint: if (!overflow) { playgroundCache.targetPosition[p] = paint.GetPosition(p); } else { switch (overflowMode) { case OVERFLOWMODEC.SourceTransform: playgroundCache.targetPosition[p] = paint.GetPosition(p)+GetOverflow2(paint.GetRotation(p)*overflowOffset, p, paint.positionLength); break; case OVERFLOWMODEC.World: playgroundCache.targetPosition[p] = paint.GetPosition(p)+GetOverflow2(overflowOffset, p, paint.positionLength); break; case OVERFLOWMODEC.SourcePoint: playgroundCache.targetPosition[p] = paint.GetPosition(p)+paint.GetRotation(p)*GetOverflow2(overflowOffset, paint.GetNormal(p), p, paint.positionLength); break; } } break; case SOURCEC.Spline: int sIndex; if (treatAsOneSpline) sIndex = (int)((((p*1f)/(particleCount*1f))*splines.Count)+splineTimeOffset)%splines.Count; else sIndex = p%splines.Count; float pSplineTime; if (treatAsOneSpline) pSplineTime = ((p*splines.Count*1f) / (particleCount*1f))+splineTimeOffset; else pSplineTime = ((p*1f) / (particleCount*1f)) + splineTimeOffset; playgroundCache.targetPosition[p] = splines[sIndex].GetPoint(pSplineTime); break; } if (applySourceScatter) playgroundCache.targetPosition[p] += Vector3.Scale (playgroundCache.scatterPosition[p], scatterScale); } /// /// Asynchronous-friendly particle calculation routine. /// public static void ThreadedCalculations (PlaygroundParticlesC playgroundParticles) { if (playgroundParticles.inPlayback) { playgroundParticles.isDoneThread = true; playgroundParticles.threadHadNoActiveParticles = false; return; } // 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 0; if (hasLifetimeLoss && (playgroundParticles.playgroundCache.lifetimeLoss==null || playgroundParticles.playgroundCache.lifetimeLoss.Length!=pCount)) playgroundParticles.playgroundCache.lifetimeLoss = new float[pCount]; 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=0 && 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.SetParticleColorInternal(p, 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) { Vector3 deltaVelocity = playgroundParticles.playgroundCache.targetPosition[p]-playgroundParticles.playgroundCache.previousTargetPosition[p]; if (playgroundParticles.deltaMovementStrengthValueMethod == VALUEMETHOD.Constant) playgroundParticles.playgroundCache.velocity[p] += (deltaVelocity*playgroundParticles.deltaMovementStrength)/playgroundParticles.realSimulationTime; else playgroundParticles.playgroundCache.velocity[p] += (deltaVelocity*RandomRange(playgroundParticles.internalRandom01, playgroundParticles.deltaMovementStrength, playgroundParticles.minDeltaMovementStrength))/playgroundParticles.realSimulationTime; } playgroundParticles.playgroundCache.birthDelay[p] = 0; } if (playgroundParticles.playgroundCache.birth[p]>playgroundParticles.localTime) { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 playgroundParticles.particleCache[p].size = 0; #else playgroundParticles.particleCache[p].startSize = 0; #endif 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) { // Check if particle has sticky collision if (playgroundParticles.hasCollisionCache && playgroundParticles.stickyCollisions && playgroundParticles.collisionCache.hasCollided[p] && !playgroundParticles.playgroundCache.isNonBirthed[p]) { if (playgroundParticles.stickyCollisionsSurfaceOffset==0) playgroundParticles.playgroundCache.position[p] = playgroundParticles.collisionCache.stickyPosition[p]; else playgroundParticles.playgroundCache.position[p] = playgroundParticles.collisionCache.stickyPosition[p] + (playgroundParticles.collisionCache.collisionNormal[p]*playgroundParticles.stickyCollisionsSurfaceOffset); } else { 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.stretchLifetime.Evaluate(playgroundParticles.playgroundCache.life[p]/lifeInSeconds))/playgroundParticles.realSimulationTime, t*playgroundParticles.stretchSpeed * playgroundParticles.realSimulationTime); } 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 * playgroundParticles.realSimulationTime); 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.position[p]-playgroundParticles.playgroundCache.previousParticlePosition[p]!=Vector3.zero) { 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 { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 playgroundParticles.particleCache[p].size = 0; #else playgroundParticles.particleCache[p].startSize = 0; #endif playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; } // Calculate lifetime float evaluatedLife = (playgroundParticles.localTime-playgroundParticles.playgroundCache.birth[p])/lifeInSeconds; // Lifetime if (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); #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 playgroundParticles.particleCache[p].size = 0; #else playgroundParticles.particleCache[p].startSize = 0; #endif 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; #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 playgroundParticles.particleCache[p].size = 0; #else playgroundParticles.particleCache[p].startSize = 0; #endif playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition; } } else { playgroundParticles.InactivateParticle(p); continue; } } // Local Manipulators for (int m = 0; m0&&playgroundParticles.particleCache[p].position.y!=initYpos)?playgroundParticles.playgroundCache.size[p]:0; #else playgroundParticles.particleCache[p].startSize = (playgroundParticles.playgroundCache.maskAlpha[p]>0&&playgroundParticles.particleCache[p].position.y!=initYpos)?playgroundParticles.playgroundCache.size[p]:0; #endif } } 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 /// Sends the death events. /// /// Playground particles. /// Particle index. 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; i0) { if (playgroundParticles.turbulenceApplyLifetimeStrength) turbulenceStrengthMultiplier = playgroundParticles.turbulenceLifetimeStrength.Evaluate (Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/((playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-playgroundParticles.playgroundCache.lifetimeSubtraction[p]))); if (turbulenceStrengthMultiplier>0) { if (!playgroundParticles.axisConstraints.x) playgroundParticles.playgroundCache.velocity[p].x += (float)turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*turbulenceScale, playgroundParticles.localTime*turbulenceTimeScale )*turbulenceStrength*t*turbulenceStrengthMultiplier; if (!playgroundParticles.axisConstraints.y) playgroundParticles.playgroundCache.velocity[p].y += (float)turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*turbulenceScale, playgroundParticles.localTime*turbulenceTimeScale )*turbulenceStrength*t*turbulenceStrengthMultiplier; if (!playgroundParticles.axisConstraints.z) playgroundParticles.playgroundCache.velocity[p].z += (float)turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*turbulenceScale, playgroundParticles.localTime*turbulenceTimeScale )*turbulenceStrength*t*turbulenceStrengthMultiplier; } } else { if (turbulenceStrengthMultiplier>0) { if (!playgroundParticles.axisConstraints.x) playgroundParticles.playgroundCache.velocity[p].x += (float)turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*turbulenceScale )*turbulenceStrength*t*turbulenceStrengthMultiplier; if (!playgroundParticles.axisConstraints.y) playgroundParticles.playgroundCache.velocity[p].y += (float)turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*turbulenceScale )*turbulenceStrength*t*turbulenceStrengthMultiplier; if (!playgroundParticles.axisConstraints.z) playgroundParticles.playgroundCache.velocity[p].z += (float)turbulenceSimplex.noise( (playgroundParticles.playgroundCache.position[p].z+zeroFixZ)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].x+zeroFixX)*turbulenceScale, (playgroundParticles.playgroundCache.position[p].y+zeroFixY)*turbulenceScale )*turbulenceStrength*t*turbulenceStrengthMultiplier; } } } else { // Perlin Noise if (playgroundParticles.turbulenceStrength>0) { if (playgroundParticles.turbulenceApplyLifetimeStrength) turbulenceStrengthMultiplier = playgroundParticles.turbulenceLifetimeStrength.Evaluate (Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/((playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-playgroundParticles.playgroundCache.lifetimeSubtraction[p]))); if (turbulenceStrengthMultiplier>0) { if (!playgroundParticles.axisConstraints.x) playgroundParticles.playgroundCache.velocity[p].x += (Mathf.PerlinNoise ( playgroundParticles.localTime*turbulenceTimeScale, playgroundParticles.playgroundCache.position[p].z*turbulenceScale )-.5f)*turbulenceStrength*t*turbulenceStrengthMultiplier; if (!playgroundParticles.axisConstraints.y) playgroundParticles.playgroundCache.velocity[p].y += (Mathf.PerlinNoise ( playgroundParticles.localTime*turbulenceTimeScale, playgroundParticles.playgroundCache.position[p].x*turbulenceScale )-.5f)*turbulenceStrength*t*turbulenceStrengthMultiplier; if (!playgroundParticles.axisConstraints.z) playgroundParticles.playgroundCache.velocity[p].z += (Mathf.PerlinNoise ( playgroundParticles.localTime*turbulenceTimeScale, playgroundParticles.playgroundCache.position[p].y*turbulenceScale )-.5f)*turbulenceStrength*t*turbulenceStrengthMultiplier; } } } } } public void CalculateCollisions () { Collisions (thisInstance); } /// /// Calculates particle collisions, this runs automatically if collision is set to true. (Must currently run on main-thread due to the Physics.Raycast dependency.) /// /// Particle Playground system. public static void Collisions (PlaygroundParticlesC playgroundParticles) { if (!playgroundParticles.calculate || playgroundParticles.inPlayback) return; if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.onlyLifetimePositioning && playgroundParticles.collisionRadius>0 && playgroundParticles.hasEmitted) { Ray ray = new Ray(); float distance; bool is3d = playgroundParticles.collisionType==COLLISIONTYPEC.Physics3D; bool hasCollisionExclusion = playgroundParticles.collisionExclusion!=null && playgroundParticles.collisionExclusion.Count>0; bool hasRandomBounce = playgroundParticles.bounceRandomMin!=Vector3.zero||playgroundParticles.bounceRandomMax!=Vector3.zero; RaycastHit hitInfo; RaycastHit2D hitInfo2D; bool hasEvents = playgroundParticles.events.Count>0; // Prepare the collision cache if ((playgroundParticles.stickyCollisions || playgroundParticles.forceCollisionCaching) && playgroundParticles.collisionCache==null) playgroundParticles.collisionCache = new CollisionCache(playgroundParticles.particleCount); // Prepare the infinite collision planes if (playgroundParticles.collision && playgroundParticles.colliders.Count>0) { for (int c = 0; c=(playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-playgroundParticles.playgroundCache.lifetimeSubtraction[p] || playgroundParticles.playgroundCache.noForce[p] || playgroundParticles.maskedParticlesBypassCollision && playgroundParticles.playgroundCache.isMasked[p]) continue; // Sticky? Do a sticky update then continue if (playgroundParticles.stickyCollisions && playgroundParticles.collisionCache.hasCollided[p]) { playgroundParticles.collisionCache.UpdateStickyPosition(p); playgroundParticles.playgroundCache.position[p] = playgroundParticles.collisionCache.stickyPosition[p]; continue; } // Playground Plane colliders (never exceed these) for (int c = 0; c0) { playgroundParticles.playgroundCache.lifetimeLoss[p] = playgroundParticles.lifetime * 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]), out hitInfo, playgroundParticles.collisionPrecision? (Vector3.Distance (playgroundParticles.playgroundCache.position[p], playgroundParticles.playgroundCache.collisionParticlePosition[p])+playgroundParticles.collisionRadius)*2f : (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]; // Update collision cache if (!playgroundParticles.stickyCollisions) { if (playgroundParticles.forceCollisionCaching) playgroundParticles.collisionCache.Set (p, hitInfo.point, hitInfo.normal, hitInfo.transform); } else if ((playgroundParticles.stickyCollisionsMask.value & 1<0) { playgroundParticles.playgroundCache.lifetimeLoss[p] = playgroundParticles.lifetime * 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; i0) { playgroundParticles.playgroundCache.lifetimeLoss[p] = playgroundParticles.lifetime * 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 /// Returns the offset as a remainder from a point, constructed for multithreading. /// /// The overflow position. /// Overflow by Vector3. /// Current value. /// Max value. public static Vector3 GetOverflow2 (Vector3 overflow, int currentVal, int maxVal) { float iteration = (currentVal/maxVal); return new Vector3( overflow.x*iteration, overflow.y*iteration, overflow.z*iteration ); } /// /// Returns the offset with direction as a remainder from a point, constructed for multithreading. /// /// The overflow position in direction. /// Overflow by Vector3. /// Direction. /// Current value. /// Max value. public static Vector3 GetOverflow2 (Vector3 overflow, Vector3 direction, int currentVal, int maxVal) { float iteration = (currentVal/maxVal); return new Vector3( direction.x*overflow.x*iteration, direction.y*overflow.y*iteration, direction.z*overflow.z*iteration ); } /// /// Multiplies two Vector3. /// /// The scale of two Vector3. /// First Vector3. /// Second Vector3. public static Vector3 Vector3Scale (Vector3 v1, Vector3 v2) { return new Vector3(v1.x*v2.x,v1.y*v2.y,v1.z*v2.z); } /// /// Calculates the effect from a Manipulator. /// /// Playground particles. /// This manipulator. /// Particle index. /// Delta time. /// Lifetime. /// Particle position. /// Manipulator position. /// Manipulator distance. /// Is calculation in local space? public static void CalculateManipulator (PlaygroundParticlesC playgroundParticles, ManipulatorObjectC thisManipulator, int p, float t, float life, Vector3 particlePosition, Vector3 manipulatorPosition, bool localSpace) { if (thisManipulator.enabled && thisManipulator.transform.available && thisManipulator.strength!=0 && thisManipulator.LifetimeFilter(life, playgroundParticles.lifetime) && thisManipulator.ParticleFilter (p, playgroundParticles.particleCount)) { bool contains = thisManipulator.Contains(localSpace?(playgroundParticles.particleSystemRotation*particlePosition):particlePosition, localSpace?(playgroundParticles.particleSystemRotation*manipulatorPosition):manipulatorPosition); // Is this a particle which shouldn't be affected by this manipulator? if (contains && (playgroundParticles.playgroundCache.excludeFromManipulatorId[p]==thisManipulator.manipulatorId || thisManipulator.nonAffectedParticles.Count>0 && thisManipulator.ContainsNonAffectedParticle(playgroundParticles.particleSystemId, p))) { return; } // Manipulator events if (thisManipulator.trackParticles) { // Particle entering if (contains) { if ((thisManipulator.trackingMethod==TrackingMethod.ManipulatorId && !thisManipulator.IsSameId(playgroundParticles.playgroundCache.manipulatorId[p])) || (thisManipulator.trackingMethod==TrackingMethod.ParticleId && !thisManipulator.ContainsParticle(playgroundParticles.particleSystemId, p))) { playgroundParticles.playgroundCache.manipulatorId[p] = thisManipulator.manipulatorId; thisManipulator.AddParticle(playgroundParticles.particleSystemId, p); if (thisManipulator.sendEventEnter) { playgroundParticles.UpdateEventParticle(thisManipulator.manipulatorEventParticle, p); thisManipulator.SendParticleEventEnter(); } } } else { // Particle exiting if ((thisManipulator.trackingMethod==TrackingMethod.ManipulatorId && thisManipulator.IsSameId(playgroundParticles.playgroundCache.manipulatorId[p])) || (thisManipulator.trackingMethod==TrackingMethod.ParticleId && thisManipulator.ContainsParticle(playgroundParticles.particleSystemId, p))) { playgroundParticles.playgroundCache.manipulatorId[p] = 0; thisManipulator.RemoveParticle(playgroundParticles.particleSystemId, p); if (thisManipulator.sendEventExit) { playgroundParticles.UpdateEventParticle(thisManipulator.manipulatorEventParticle, p); thisManipulator.SendParticleEventExit(); } } } } float manipulatorDistance = 0; float manipulatorStrength = thisManipulator.applyParticleLifetimeStrength? thisManipulator.strength * thisManipulator.particleLifetimeStrength.Evaluate((life-playgroundParticles.playgroundCache.lifetimeSubtraction[p]) / playgroundParticles.lifetime) : thisManipulator.strength; bool hasConstraints = thisManipulator.axisConstraints.HasConstraints(); Vector3 constrainedVelocity = playgroundParticles.playgroundCache.velocity[p]; if (!playgroundParticles.onlySourcePositioning && !playgroundParticles.playgroundCache.noForce[p]) { // Attractors if (thisManipulator.type==MANIPULATORTYPEC.Attractor) { if (contains) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], (manipulatorPosition-particlePosition)*(manipulatorStrength/manipulatorDistance), t*(manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); } } else // Attractors Gravitational if (thisManipulator.type==MANIPULATORTYPEC.AttractorGravitational) { if (contains) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], (manipulatorPosition-particlePosition)*manipulatorStrength/manipulatorDistance, t/thisManipulator.strengthSmoothing); } } else // Repellents if (thisManipulator.type==MANIPULATORTYPEC.Repellent) { if (contains) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], (particlePosition-manipulatorPosition)*(manipulatorStrength/manipulatorDistance), t*(manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); } } else // Vortex if (thisManipulator.type==MANIPULATORTYPEC.Vortex) { if (contains) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], ((manipulatorPosition-particlePosition)*manipulatorStrength/manipulatorDistance)-Vector3.Cross(thisManipulator.transform.up, (manipulatorPosition-particlePosition))*manipulatorStrength/manipulatorDistance, (t*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); } } } // Properties if (thisManipulator.type==MANIPULATORTYPEC.Property) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; PropertyManipulator(playgroundParticles, thisManipulator, thisManipulator.property, manipulatorStrength, p, t, particlePosition, manipulatorPosition, manipulatorDistance, localSpace, contains); } else // Combined if (thisManipulator.type==MANIPULATORTYPEC.Combined) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; for (int i = 0; i /// Calculates the effect from Manipulator's properties. /// /// Playground particles. /// This manipulator. /// This manipulator property. /// Particle index. /// Delta time. /// Particle position. /// Manipulator position. /// Is calculation in local space? /// Does the Manipulator contain the particle at index? public static void PropertyManipulator (PlaygroundParticlesC playgroundParticles, ManipulatorObjectC thisManipulator, ManipulatorPropertyC thisManipulatorProperty, float manipulatorStrength, int p, float t, Vector3 particlePosition, Vector3 manipulatorPosition, float manipulatorDistance, bool localSpace, bool contains) { if (contains) { switch (thisManipulatorProperty.type) { // Math Property case MANIPULATORPROPERTYTYPEC.Math: if (thisManipulatorProperty.mathProperty.type == MATHMANIPULATORTYPE.Sin || thisManipulatorProperty.mathProperty.type == MATHMANIPULATORTYPE.Cos) t = playgroundParticles.localTime; switch (thisManipulatorProperty.mathProperty.property) { case MATHMANIPULATORPROPERTY.Position: playgroundParticles.playgroundCache.position[p] = thisManipulatorProperty.mathProperty.EvaluatePosition(playgroundParticles.playgroundCache.position[p], t); break; case MATHMANIPULATORPROPERTY.Velocity: playgroundParticles.playgroundCache.velocity[p] = thisManipulatorProperty.mathProperty.Evaluate(playgroundParticles.playgroundCache.velocity[p], t); break; case MATHMANIPULATORPROPERTY.Rotation: if (!playgroundParticles.rotateTowardsDirection) playgroundParticles.playgroundCache.rotation[p] = thisManipulatorProperty.mathProperty.Evaluate(playgroundParticles.playgroundCache.rotation[p], t)%360f; break; case MATHMANIPULATORPROPERTY.Size: playgroundParticles.playgroundCache.size[p] = thisManipulatorProperty.mathProperty.Evaluate(playgroundParticles.playgroundCache.size[p], t); playgroundParticles.playgroundCache.changedByPropertySize[p] = true; break; } break; // Velocity Property case MANIPULATORPROPERTYTYPEC.Velocity: if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) playgroundParticles.playgroundCache.velocity[p] = thisManipulatorProperty.useLocalRotation? thisManipulatorProperty.localVelocity : thisManipulatorProperty.velocity; else { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], thisManipulatorProperty.useLocalRotation? thisManipulatorProperty.localVelocity : thisManipulatorProperty.velocity, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); } break; // Additive Velocity Property case MANIPULATORPROPERTYTYPEC.AdditiveVelocity: manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.velocity[p] += thisManipulatorProperty.useLocalRotation? thisManipulatorProperty.localVelocity*((t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing) : thisManipulatorProperty.velocity*((t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); break; // Color Property case MANIPULATORPROPERTYTYPEC.Color: Color staticColor; if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) { if (thisManipulatorProperty.keepColorAlphas) { staticColor = thisManipulatorProperty.color; staticColor.a = Mathf.Clamp(playgroundParticles.lifetimeColor.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime).a, 0, staticColor.a); playgroundParticles.SetParticleColorInternal(p, staticColor); } else playgroundParticles.SetParticleColorInternal(p, thisManipulatorProperty.color); } else { if (thisManipulatorProperty.keepColorAlphas) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; staticColor = thisManipulatorProperty.color; staticColor.a = Mathf.Clamp(playgroundParticles.lifetimeColor.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime).a, 0, staticColor.a); #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 playgroundParticles.SetParticleColorInternal(p, Color.Lerp(playgroundParticles.particleCache[p].color, staticColor, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing)); #else playgroundParticles.SetParticleColorInternal(p, Color.Lerp(playgroundParticles.particleCache[p].startColor, staticColor, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing)); #endif } else { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 playgroundParticles.SetParticleColorInternal(p, Color.Lerp(playgroundParticles.particleCache[p].color, thisManipulatorProperty.color, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing)); #else playgroundParticles.SetParticleColorInternal(p, Color.Lerp(playgroundParticles.particleCache[p].startColor, thisManipulatorProperty.color, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing)); #endif } playgroundParticles.playgroundCache.changedByPropertyColorLerp[p] = true; } // Only color in range of manipulator boundaries playgroundParticles.playgroundCache.changedByPropertyColor[p] = true; // Keep alpha of original color if (thisManipulatorProperty.keepColorAlphas) playgroundParticles.playgroundCache.changedByPropertyColorKeepAlpha[p] = true; // Set color pairing key if (playgroundParticles.playgroundCache.propertyColorId[p] != thisManipulator.manipulatorId) { playgroundParticles.playgroundCache.propertyColorId[p] = thisManipulator.manipulatorId; } break; // Lifetime Color Property case MANIPULATORPROPERTYTYPEC.LifetimeColor: if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) { playgroundParticles.SetParticleColorInternal(p, thisManipulatorProperty.lifetimeColor.Evaluate(playgroundParticles.lifetime>0?playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime:0)); } else { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 playgroundParticles.SetParticleColorInternal(p, Color.Lerp(playgroundParticles.particleCache[p].color, thisManipulatorProperty.lifetimeColor.Evaluate(playgroundParticles.lifetime>0?playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime:0), (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing)); #else playgroundParticles.SetParticleColorInternal(p, Color.Lerp(playgroundParticles.particleCache[p].startColor, thisManipulatorProperty.lifetimeColor.Evaluate(playgroundParticles.lifetime>0?playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime:0), (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing)); #endif playgroundParticles.playgroundCache.changedByPropertyColorLerp[p] = true; } // Only color in range of manipulator boundaries playgroundParticles.playgroundCache.changedByPropertyColor[p] = true; // Set color pairing key if (playgroundParticles.playgroundCache.propertyColorId[p] != thisManipulator.manipulatorId) { playgroundParticles.playgroundCache.propertyColorId[p] = thisManipulator.manipulatorId; } break; // Size Property case MANIPULATORPROPERTYTYPEC.Size: if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0f?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:0; if (manipulatorDistance==0) playgroundParticles.playgroundCache.size[p] = thisManipulatorProperty.size; else playgroundParticles.playgroundCache.size[p] = thisManipulatorProperty.size+(thisManipulatorProperty.size/(1f+(manipulatorDistance/thisManipulator.strengthSmoothing))*(thisManipulator.strength*thisManipulatorProperty.strength)); } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Lerp) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.size[p] = Mathf.Lerp(playgroundParticles.playgroundCache.size[p], thisManipulatorProperty.size, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Linear) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.size[p] = Mathf.MoveTowards(playgroundParticles.playgroundCache.size[p], thisManipulatorProperty.size, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); } playgroundParticles.playgroundCache.changedByPropertySize[p] = true; break; // Target Property case MANIPULATORPROPERTYTYPEC.Target: if (thisManipulatorProperty.targets.Count>0 && thisManipulatorProperty.targets[thisManipulatorProperty.targetPointer].available) { // Set target pointer if (playgroundParticles.playgroundCache.propertyId[p] != thisManipulator.manipulatorId) { playgroundParticles.playgroundCache.propertyTarget[p] = thisManipulatorProperty.targetPointer; thisManipulatorProperty.targetPointer++; thisManipulatorProperty.targetPointer=thisManipulatorProperty.targetPointer%thisManipulatorProperty.targets.Count; playgroundParticles.playgroundCache.propertyId[p] = thisManipulator.manipulatorId; } // Teleport or lerp to position based on transition type if (playgroundParticles.playgroundCache.propertyId[p] == thisManipulator.manipulatorId && thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].available) { if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) playgroundParticles.playgroundCache.position[p] = localSpace? thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].localPosition : thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].position; else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Lerp) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = localSpace? Vector3.Lerp(particlePosition, thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].localPosition, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing) : Vector3.Lerp(particlePosition, thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].position, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Linear) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = localSpace? Vector3.MoveTowards(particlePosition, thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].localPosition, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing) : Vector3.MoveTowards(particlePosition, thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].position, (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } // This particle was changed by a target property playgroundParticles.playgroundCache.changedByPropertyTarget[p] = true; } } break; // Death Property case MANIPULATORPROPERTYTYPEC.Death: if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) playgroundParticles.playgroundCache.life[p] = playgroundParticles.lifetime; else { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.birth[p] -= (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing; } // This particle was changed by a death property playgroundParticles.playgroundCache.changedByPropertyDeath[p] = true; break; // Attractors case MANIPULATORPROPERTYTYPEC.Attractor: if (!playgroundParticles.onlySourcePositioning) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], (manipulatorPosition-particlePosition)*((thisManipulatorProperty.strength*manipulatorStrength)/manipulatorDistance), t*((thisManipulatorProperty.strength*manipulatorStrength)/manipulatorDistance)/thisManipulator.strengthSmoothing); } break; // Attractors Gravitational case MANIPULATORPROPERTYTYPEC.Gravitational: if (!playgroundParticles.onlySourcePositioning) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], (manipulatorPosition-particlePosition)*(thisManipulatorProperty.strength*manipulatorStrength)/manipulatorDistance, t/thisManipulator.strengthSmoothing); } break; // Repellents case MANIPULATORPROPERTYTYPEC.Repellent: if (!playgroundParticles.onlySourcePositioning) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], (particlePosition-manipulatorPosition)*((thisManipulatorProperty.strength*manipulatorStrength)/manipulatorDistance), t*((thisManipulatorProperty.strength*manipulatorStrength)/manipulatorDistance)/thisManipulator.strengthSmoothing); } break; // Vortex case MANIPULATORPROPERTYTYPEC.Vortex: if (!playgroundParticles.onlySourcePositioning) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, playgroundParticles.playgroundCache.position[p])/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], ((manipulatorPosition-particlePosition)*(manipulatorStrength*thisManipulatorProperty.strength)/manipulatorDistance)-Vector3.Cross(thisManipulator.transform.up, (manipulatorPosition-particlePosition))*(manipulatorStrength*thisManipulatorProperty.strength)/manipulatorDistance, (t*(manipulatorStrength*thisManipulatorProperty.strength)/manipulatorDistance)/thisManipulator.strengthSmoothing); } break; // Turbulence case MANIPULATORPROPERTYTYPEC.Turbulence: if (!playgroundParticles.onlySourcePositioning && thisManipulatorProperty.turbulenceType!=TURBULENCETYPE.None) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.Distance (manipulatorPosition, particlePosition)/thisManipulator.strengthDistanceEffect:10f; Turbulence ( playgroundParticles, thisManipulatorProperty.turbulenceSimplex, p, t/thisManipulator.strengthSmoothing, thisManipulatorProperty.turbulenceType, thisManipulatorProperty.turbulenceTimeScale, thisManipulatorProperty.turbulenceScale, ((thisManipulatorProperty.strength*manipulatorStrength)/manipulatorDistance), thisManipulatorProperty.turbulenceApplyLifetimeStrength, thisManipulatorProperty.turbulenceLifetimeStrength ); } break; // Mesh Target case MANIPULATORPROPERTYTYPEC.MeshTarget: if (!playgroundParticles.onlySourcePositioning && thisManipulatorProperty.meshTarget.initialized) { // Set target pointer if (playgroundParticles.playgroundCache.propertyId[p] != thisManipulator.manipulatorId) { playgroundParticles.playgroundCache.propertyTarget[p] = thisManipulatorProperty.targetSortingList[thisManipulatorProperty.targetPointer]; thisManipulatorProperty.targetPointer++; thisManipulatorProperty.targetPointer=thisManipulatorProperty.targetPointer%thisManipulatorProperty.meshTarget.vertexPositions.Length; playgroundParticles.playgroundCache.propertyId[p] = thisManipulator.manipulatorId; } // Teleport or lerp to position based on transition type if (playgroundParticles.playgroundCache.propertyId[p] == thisManipulator.manipulatorId) { if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) { playgroundParticles.playgroundCache.position[p] = thisManipulatorProperty.meshTargetMatrix.MultiplyPoint3x4(thisManipulatorProperty.meshTarget.vertexPositions[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.meshTarget.vertexPositions.Length]); playgroundParticles.playgroundCache.velocity[p] = Vector3.zero; } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Lerp) { playgroundParticles.playgroundCache.position[p] = Vector3.Lerp(particlePosition, thisManipulatorProperty.meshTargetMatrix.MultiplyPoint3x4(thisManipulatorProperty.meshTarget.vertexPositions[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.meshTarget.vertexPositions.Length]), (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Linear) { playgroundParticles.playgroundCache.position[p] = Vector3.MoveTowards(particlePosition, thisManipulatorProperty.meshTargetMatrix.MultiplyPoint3x4(thisManipulatorProperty.meshTarget.vertexPositions[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.meshTarget.vertexPositions.Length]), (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } // This particle was changed by a target property playgroundParticles.playgroundCache.changedByPropertyTarget[p] = true; } } break; // Skinned Mesh Target case MANIPULATORPROPERTYTYPEC.SkinnedMeshTarget: if (!playgroundParticles.onlySourcePositioning && thisManipulatorProperty.skinnedMeshTarget.initialized) { // Set target pointer if (playgroundParticles.playgroundCache.propertyId[p] != thisManipulator.manipulatorId) { playgroundParticles.playgroundCache.propertyTarget[p] = thisManipulatorProperty.targetSortingList[thisManipulatorProperty.targetPointer]; thisManipulatorProperty.targetPointer++; thisManipulatorProperty.targetPointer=thisManipulatorProperty.targetPointer%thisManipulatorProperty.skinnedMeshTarget.vertexPositions.Length; playgroundParticles.playgroundCache.propertyId[p] = thisManipulator.manipulatorId; } // Teleport or lerp to position based on transition type if (playgroundParticles.playgroundCache.propertyId[p] == thisManipulator.manipulatorId) { if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) { playgroundParticles.playgroundCache.position[p] = thisManipulatorProperty.skinnedMeshTarget.vertexPositions[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.skinnedMeshTarget.vertexPositions.Length]; playgroundParticles.playgroundCache.velocity[p] = Vector3.zero; } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Lerp) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.Lerp(particlePosition, thisManipulatorProperty.skinnedMeshTarget.vertexPositions[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.skinnedMeshTarget.vertexPositions.Length], t*(thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Linear) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.MoveTowards(particlePosition, thisManipulatorProperty.skinnedMeshTarget.vertexPositions[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.skinnedMeshTarget.vertexPositions.Length], t*(thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } // This particle was changed by a target property playgroundParticles.playgroundCache.changedByPropertyTarget[p] = true; } } break; // State Target case MANIPULATORPROPERTYTYPEC.StateTarget: if (!playgroundParticles.onlySourcePositioning && thisManipulatorProperty.stateTarget.initialized && !thisManipulatorProperty.stateTarget.IsInitializing()) { // Set target pointer if (playgroundParticles.playgroundCache.propertyId[p] != thisManipulator.manipulatorId) { playgroundParticles.playgroundCache.propertyTarget[p] = thisManipulatorProperty.targetSortingList[thisManipulatorProperty.targetPointer%thisManipulatorProperty.targetSortingList.Length]; thisManipulatorProperty.targetPointer++; thisManipulatorProperty.targetPointer=thisManipulatorProperty.targetPointer%thisManipulatorProperty.stateTarget.positionLength; playgroundParticles.playgroundCache.propertyId[p] = thisManipulator.manipulatorId; } // Teleport or lerp to position based on transition type if (playgroundParticles.playgroundCache.propertyId[p] == thisManipulator.manipulatorId) { Vector3 newPos = thisManipulatorProperty.stateTarget.stateTransformMx.MultiplyPoint3x4(thisManipulatorProperty.stateTarget.GetPosition(playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.stateTarget.positionLength)); if (!thisManipulatorProperty.onlyPositionInRange || thisManipulator.shape == MANIPULATORSHAPEC.Infinite || thisManipulatorProperty.onlyPositionInRange && thisManipulator.Contains (newPos, manipulatorPosition)) { if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) { playgroundParticles.playgroundCache.position[p] = newPos; playgroundParticles.playgroundCache.velocity[p] = Vector3.zero; } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Lerp) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.Lerp(particlePosition, newPos, t*(thisManipulatorProperty.strength*thisManipulator.strength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Linear) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.MoveTowards(particlePosition, newPos, t*(thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } // This particle was changed by a target property playgroundParticles.playgroundCache.changedByPropertyTarget[p] = true; } } } break; // Spline Target case MANIPULATORPROPERTYTYPEC.SplineTarget: if (!playgroundParticles.onlySourcePositioning && thisManipulatorProperty.SplineTargetIsReady()) { // Set target pointer if (playgroundParticles.playgroundCache.propertyId[p] != thisManipulator.manipulatorId) { playgroundParticles.playgroundCache.propertyId[p] = thisManipulator.manipulatorId; } // Teleport or lerp to position based on transition type if (playgroundParticles.playgroundCache.propertyId[p] == thisManipulator.manipulatorId) { float timePos; if (thisManipulatorProperty.splineTargetMethod==SPLINETARGETMETHOD.SplineTime) timePos = ((p*1f)/(playgroundParticles.particleCount*1f))+thisManipulatorProperty.splineTimeOffset; else timePos = Mathf.Clamp01((playgroundParticles.playgroundCache.life[p] / ((playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-playgroundParticles.playgroundCache.lifetimeSubtraction[p]))+thisManipulatorProperty.splineTimeOffset); if (Mathf.Approximately(thisManipulatorProperty.splineTimeOffset, 0) && Mathf.Approximately(timePos, 1f)) timePos = .9999f; Vector3 newPos = thisManipulatorProperty.splineTarget.GetPoint(timePos); if (!thisManipulatorProperty.onlyPositionInRange || thisManipulator.shape == MANIPULATORSHAPEC.Infinite || thisManipulatorProperty.onlyPositionInRange && thisManipulator.Contains (newPos, manipulatorPosition)) { if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) { playgroundParticles.playgroundCache.position[p] = newPos; playgroundParticles.playgroundCache.velocity[p] = Vector3.zero; } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Lerp) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.Lerp(particlePosition, newPos, t*(thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Linear) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.MoveTowards(particlePosition, newPos, t*(thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } // This particle was changed by a target property playgroundParticles.playgroundCache.changedByPropertyTarget[p] = true; } } } break; } playgroundParticles.playgroundCache.changedByProperty[p] = true; } else { // Handle size outside if (thisManipulatorProperty.onlySizeInRange) playgroundParticles.playgroundCache.changedByPropertySize[p] = false; // Handle colors outside of property manipulator range if (playgroundParticles.playgroundCache.propertyColorId[p] == thisManipulator.manipulatorId && (thisManipulatorProperty.type == MANIPULATORPROPERTYTYPEC.Color || thisManipulatorProperty.type == MANIPULATORPROPERTYTYPEC.LifetimeColor)) { // Lerp back color with previous set key if (playgroundParticles.playgroundCache.changedByPropertyColorLerp[p] && thisManipulatorProperty.transition != MANIPULATORPROPERTYTRANSITIONC.None && thisManipulatorProperty.onlyColorInRange) { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 playgroundParticles.SetParticleColorInternal(p, Color.Lerp(playgroundParticles.particleCache[p].color, playgroundParticles.lifetimeColor.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime), t*thisManipulatorProperty.strength*manipulatorStrength)); #else playgroundParticles.SetParticleColorInternal(p, Color.Lerp(playgroundParticles.particleCache[p].startColor, playgroundParticles.lifetimeColor.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime), t*thisManipulatorProperty.strength*manipulatorStrength)); #endif } if (thisManipulatorProperty.type == MANIPULATORPROPERTYTYPEC.LifetimeColor && !thisManipulatorProperty.onlyColorInRange) { if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.None) { playgroundParticles.SetParticleColorInternal(p, thisManipulatorProperty.lifetimeColor.Evaluate(playgroundParticles.lifetime>0?playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime:0)); } else { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 playgroundParticles.SetParticleColorInternal(p, Color.Lerp(playgroundParticles.particleCache[p].color, thisManipulatorProperty.lifetimeColor.Evaluate(playgroundParticles.lifetime>0?playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime:0), (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing)); #else playgroundParticles.SetParticleColorInternal(p, Color.Lerp(playgroundParticles.particleCache[p].startColor, thisManipulatorProperty.lifetimeColor.Evaluate(playgroundParticles.lifetime>0?playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime:0), (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing)); #endif } } if (thisManipulatorProperty.onlyColorInRange) playgroundParticles.playgroundCache.changedByPropertyColor[p] = false; } // Position onto targets when outside of range if (!playgroundParticles.onlySourcePositioning && !(thisManipulatorProperty.onlyPositionInRange || thisManipulator.shape == MANIPULATORSHAPEC.Infinite) && thisManipulatorProperty.transition != MANIPULATORPROPERTYTRANSITIONC.None) { // Target (Transform) positioning outside of range if (thisManipulatorProperty.type == MANIPULATORPROPERTYTYPEC.Target) { if (thisManipulatorProperty.targets.Count>0 && thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count]!=null) { if (playgroundParticles.playgroundCache.changedByPropertyTarget[p] && thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].available && playgroundParticles.playgroundCache.propertyId[p] == thisManipulator.transform.GetInstanceID()) { if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Lerp) playgroundParticles.playgroundCache.position[p] = localSpace? Vector3.Lerp(particlePosition, thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].localPosition, t*(thisManipulatorProperty.strength*manipulatorStrength)/thisManipulator.strengthSmoothing) : Vector3.Lerp(particlePosition, thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].position, t*(thisManipulatorProperty.strength*manipulatorStrength)/thisManipulator.strengthSmoothing); else playgroundParticles.playgroundCache.position[p] = localSpace? Vector3.MoveTowards(particlePosition, thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].localPosition, t*(thisManipulatorProperty.strength*manipulatorStrength)/thisManipulator.strengthSmoothing) : Vector3.MoveTowards(particlePosition, thisManipulatorProperty.targets[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.targets.Count].position, t*(thisManipulatorProperty.strength*manipulatorStrength)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } } } // Mesh Target positioning outside of range if (thisManipulatorProperty.type == MANIPULATORPROPERTYTYPEC.MeshTarget && thisManipulatorProperty.meshTarget.initialized && playgroundParticles.playgroundCache.propertyId[p] == thisManipulator.manipulatorId) { if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Lerp) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.Lerp(particlePosition, thisManipulatorProperty.meshTargetMatrix.MultiplyPoint3x4(thisManipulatorProperty.meshTarget.vertexPositions[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.meshTarget.vertexPositions.Length]), (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Linear) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.MoveTowards(particlePosition, thisManipulatorProperty.meshTargetMatrix.MultiplyPoint3x4(thisManipulatorProperty.meshTarget.vertexPositions[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.meshTarget.vertexPositions.Length]), (t*thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } } // Skinned Mesh Target positioning outside of range if (thisManipulatorProperty.type == MANIPULATORPROPERTYTYPEC.SkinnedMeshTarget && thisManipulatorProperty.skinnedMeshTarget.initialized && playgroundParticles.playgroundCache.propertyId[p] == thisManipulator.manipulatorId) { if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Lerp) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.Lerp(particlePosition, thisManipulatorProperty.skinnedMeshTarget.vertexPositions[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.skinnedMeshTarget.vertexPositions.Length], t*(thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Linear) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.MoveTowards(particlePosition, thisManipulatorProperty.skinnedMeshTarget.vertexPositions[playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.skinnedMeshTarget.vertexPositions.Length], t*(thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } } // State Target positioning outside of range if (thisManipulatorProperty.type == MANIPULATORPROPERTYTYPEC.StateTarget && thisManipulatorProperty.stateTarget.initialized && !thisManipulatorProperty.stateTarget.IsInitializing() && playgroundParticles.playgroundCache.propertyId[p] == thisManipulator.manipulatorId) { if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Lerp) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.Lerp(particlePosition, thisManipulatorProperty.stateTarget.stateTransformMx.MultiplyPoint3x4(thisManipulatorProperty.stateTarget.GetPosition(playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.stateTarget.positionLength)), t*(thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } else if (thisManipulatorProperty.transition == MANIPULATORPROPERTYTRANSITIONC.Linear) { manipulatorDistance = thisManipulator.strengthDistanceEffect>0?Vector3.SqrMagnitude (manipulatorPosition - particlePosition)/thisManipulator.strengthDistanceEffect:10f; playgroundParticles.playgroundCache.position[p] = Vector3.MoveTowards(particlePosition, thisManipulatorProperty.stateTarget.stateTransformMx.MultiplyPoint3x4(thisManipulatorProperty.stateTarget.GetPosition(playgroundParticles.playgroundCache.propertyTarget[p]%thisManipulatorProperty.stateTarget.positionLength)), t*(thisManipulatorProperty.strength*manipulatorStrength/manipulatorDistance)/thisManipulator.strengthSmoothing); if (thisManipulatorProperty.zeroVelocityStrength>0) playgroundParticles.playgroundCache.velocity[p] = Vector3.Lerp(playgroundParticles.playgroundCache.velocity[p], Vector3.zero, t*thisManipulatorProperty.zeroVelocityStrength); } } } } } // Update the source scatter public void RefreshScatter () { System.Random random = new System.Random(); for (int p = 0; p /// Returns a random range float, adapted for threading. /// /// The range. /// Random. /// Minimum. /// Max. public static float RandomRange (System.Random random, float min, float max) { return min+((float)random.NextDouble())*(max-min); } /// /// Random range Vector3 adapted for threading. This produces a rectangular random position inside min to max. Should you want spherical distribution use RandomRangeSpherical instead. /// /// The range. /// Random. /// Minimum. /// Max. public static Vector3 RandomRange (System.Random random, Vector3 min, Vector3 max) { return new Vector3 ( RandomRange(random, min.x, max.x), RandomRange(random, min.y, max.y), RandomRange(random, min.z, max.z) ); } /// /// Spherical random range Vector3, adapted for threading. /// /// The spherical random range. /// Random. /// Minimum. /// Max. public static Vector3 RandomRangeSpherical (System.Random random, float min, float max) { float a = 2f*Mathf.PI*(RandomRange(random, 0f, 1f)); float b = Mathf.Asin (2f*(RandomRange(random, 0f, 1f))-1f); float r = RandomRange(random, min, max); return new Vector3 ( Mathf.Cos(b)*Mathf.Sin(a)*r, Mathf.Cos(b)*Mathf.Cos(a)*r, Mathf.Sin(b)*r ); } // Spherical random range Vector3 adapted for threading public static Vector3 RandomRangeSpherical (System.Random random, float min, float max, float step) { float a = 2f*Mathf.PI*(RandomRange(random, 0f, 1f)); float b = Mathf.Asin (2f*(RandomRange(random, 0f, 1f))-1f); float r = Mathf.Lerp (min, max, step); return new Vector3 ( Mathf.Cos(b)*Mathf.Sin(a)*r, Mathf.Cos(b)*Mathf.Cos(a)*r, Mathf.Sin(b)*r ); } // Spherical random range Vector3 with partial sector option adapted for threading public static Vector3 RandomRangeSpherical (System.Random random, float min, float max, float sectorA, float sectorB) { if (sectorB<0) sectorB = 0; float a = (2f*sectorA)*Mathf.PI*(RandomRange(random, 0f, 1f)); float b = Mathf.Asin ((2f*sectorB)*(RandomRange(random, 0f, 1f))-1f); float r = RandomRange(random, min, max); return new Vector3 ( Mathf.Cos(b)*Mathf.Sin(a)*r, Mathf.Cos(b)*Mathf.Cos(a)*r, Mathf.Sin(b)*r ); } // Spherical random range Vector3 with partial sector option adapted for threading public static Vector3 RandomRangeSpherical (System.Random random, float min, float max, float sectorA, float sectorB, float step) { if (sectorB<0) sectorB = 0; float a = (2f*sectorA)*Mathf.PI*(RandomRange(random, 0f, 1f)); float b = Mathf.Asin ((2f*sectorB)*(RandomRange(random, 0f, 1f))-1f); float r = Mathf.Lerp (min, max, step); return new Vector3 ( Mathf.Cos(b)*Mathf.Sin(a)*r, Mathf.Cos(b)*Mathf.Cos(a)*r, Mathf.Sin(b)*r ); } // Return a random float array public static float[] RandomFloat (int length, float min, float max, System.Random random) { float[] f = new float[length]; for (int i = 0; i0) shurikenParticleSystem.SetParticles(particleCache, particleCache.Length); } // 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; if (playgroundParticles.hasCollisionCache) playgroundParticles.collisionCache.Reset(p); // 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.SetParticleColorInternal(p, 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.SetParticleColorInternal(p, 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) { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 playgroundParticles.particleCache[p].size = playgroundParticles.playgroundCache.maskAlpha[p]>0?playgroundParticles.playgroundCache.size[p]:0; #else playgroundParticles.particleCache[p].startSize = playgroundParticles.playgroundCache.maskAlpha[p]>0?playgroundParticles.playgroundCache.size[p]:0; #endif } // 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; m0 && playgroundParticles.playgroundCache.rebirth[p]) playgroundParticles.SendEvent(EVENTTYPEC.Birth, p); if (playgroundParticles.hasEventManipulatorLocal) { for (int i = 0; ipreEventVelocity.sqrMagnitude) return; // Set event position switch (events[i].eventInheritancePosition) { case EVENTINHERITANCEC.User: eventPosition = events[i].eventPosition; break; case EVENTINHERITANCEC.Particle: eventPosition = eventType==EVENTTYPEC.Death?playgroundCache.previousParticlePosition[p]:playgroundCache.position[p]; break; case EVENTINHERITANCEC.Source: eventPosition = playgroundCache.targetPosition[p]; break; } // Set event velocity switch (events[i].eventInheritanceVelocity) { case EVENTINHERITANCEC.User: eventVelocity = events[i].eventVelocity; break; case EVENTINHERITANCEC.Particle: eventVelocity = playgroundCache.velocity[p]; break; case EVENTINHERITANCEC.Source: if (applyInitialLocalVelocity) eventVelocity = playgroundCache.initialLocalVelocity[p]; if (applyInitialVelocity) eventVelocity += playgroundCache.initialVelocity[p]; if (applyLifetimeVelocity) eventVelocity += lifetimeVelocity.Evaluate(Mathf.Clamp01(playgroundCache.life[p]/lifetime), lifetimeVelocityScale); if (applyInitialVelocityShape) eventVelocity = Vector3.Scale(eventVelocity, initialVelocityShape.Evaluate((p*1f)/(particleCount*1f), initialVelocityShapeScale)); break; } // Apply multiplier eventVelocity *= events[i].velocityMultiplier; // Set event color switch (events[i].eventInheritanceColor) { case EVENTINHERITANCEC.User: eventColor = events[i].eventColor; break; case EVENTINHERITANCEC.Particle: eventColor = playgroundCache.color[p]; break; case EVENTINHERITANCEC.Source: eventColor = playgroundCache.initialColor[p]; break; } // Send the event to any Event Listeners if (events[i].broadcastType==EVENTBROADCASTC.EventListeners || events[i].broadcastType==EVENTBROADCASTC.Both) { UpdateEventParticle(eventParticle, p); eventParticle.collisionCollider = collisionCollider; eventParticle.collisionCollider2D = collisionCollider2D; eventParticle.collisionTransform = collisionTransform; events[i].SendParticleEvent(eventParticle); // Send the event to the Playground Manager if (events[i].sendToManager) { switch (events[i].eventType) { case EVENTTYPEC.Birth: PlaygroundC.SendParticleEventBirth(eventParticle); break; case EVENTTYPEC.Death: PlaygroundC.SendParticleEventDeath(eventParticle); break; case EVENTTYPEC.Collision: PlaygroundC.SendParticleEventCollision(eventParticle); break; case EVENTTYPEC.Time: PlaygroundC.SendParticleEventTime(eventParticle); break; } } } // Send the event to target if (events[i].initializedTarget && (events[i].broadcastType==EVENTBROADCASTC.Target || events[i].broadcastType==EVENTBROADCASTC.Both)) { events[i].target.ThreadSafeEmit(eventPosition, eventVelocity, eventColor); } } } } public bool UpdateEventParticle (PlaygroundEventParticle eParticle, int p) { if (p>=particleCount || p<0) return false; eParticle.particleSystemId = particleSystemId; eParticle.particleId = p; eParticle.birth = playgroundCache.birth[p]; eParticle.birthDelay = playgroundCache.birthDelay[p]; eParticle.changedByProperty = playgroundCache.changedByProperty[p]; eParticle.changedByPropertyColor = playgroundCache.changedByPropertyColor[p]; eParticle.changedByPropertyColorKeepAlpha = playgroundCache.changedByPropertyColorKeepAlpha[p]; eParticle.changedByPropertyColorLerp = playgroundCache.changedByPropertyColorLerp[p]; eParticle.changedByPropertyDeath = playgroundCache.changedByPropertyDeath[p]; eParticle.changedByPropertySize = playgroundCache.changedByPropertySize[p]; eParticle.changedByPropertyTarget = playgroundCache.changedByPropertyTarget[p]; eParticle.collisionParticlePosition = playgroundCache.collisionParticlePosition[p]; eParticle.color = playgroundCache.color[p]; eParticle.scriptedColor = playgroundCache.scriptedColor[p]; eParticle.death = playgroundCache.death[p]; eParticle.emission = playgroundCache.emission[p]; eParticle.initialColor = playgroundCache.initialColor[p]; eParticle.initialLocalVelocity = playgroundCache.initialLocalVelocity[p]; eParticle.initialRotation = playgroundCache.initialRotation[p]; eParticle.initialSize = playgroundCache.initialSize[p]; eParticle.initialVelocity = playgroundCache.initialVelocity[p]; eParticle.initialLocalVelocity = playgroundCache.initialLocalVelocity[p]; eParticle.life = playgroundCache.life[p]; eParticle.lifetimeColorId = playgroundCache.lifetimeColorId[p]; eParticle.noForce = playgroundCache.noForce[p]; eParticle.totalLifetime = (playgroundCache.death[p]-playgroundCache.birth[p])-playgroundCache.lifetimeSubtraction[p]; eParticle.lifetimeOffset = playgroundCache.lifetimeOffset[p]; eParticle.localSpaceMovementCompensation = playgroundCache.localSpaceMovementCompensation[p]; eParticle.position = playgroundCache.position[p]; eParticle.previousParticlePosition = playgroundCache.previousParticlePosition[p]; eParticle.previousTargetPosition = playgroundCache.previousTargetPosition[p]; eParticle.propertyColorId = playgroundCache.propertyColorId[p]; eParticle.propertyId = playgroundCache.propertyId[p]; eParticle.excludeFromManipulatorId = playgroundCache.excludeFromManipulatorId[p]; eParticle.propertyTarget = playgroundCache.propertyTarget[p]; eParticle.rebirth = playgroundCache.rebirth[p]; eParticle.rotation = playgroundCache.rotation[p]; eParticle.rotationSpeed = playgroundCache.rotationSpeed[p]; eParticle.scatterPosition = playgroundCache.scatterPosition[p]; eParticle.size = playgroundCache.size[p]; eParticle.targetDirection = playgroundCache.targetDirection[p]; eParticle.targetPosition = playgroundCache.targetPosition[p]; eParticle.velocity = playgroundCache.velocity[p]; eParticle.isMasked = playgroundCache.isMasked[p]; eParticle.maskAlpha = playgroundCache.maskAlpha[p]; eParticle.isFirstLoop = playgroundCache.isFirstLoop[p]; eParticle.isNonBirthed = playgroundCache.isNonBirthed[p]; return true; } // Delete a state from states list public void RemoveState (int i) { int newState = activeState; newState = (newState%states.Count)-1; if (newState<0) newState = 0; states[newState].Initialize(); activeState = newState; states.RemoveAt(i); } // Wipe out particles in current PlaygroundParticlesC object public static void Clear (PlaygroundParticlesC playgroundParticles) { playgroundParticles.inTransition = false; playgroundParticles.particleCache = new ParticleSystem.Particle[0]; playgroundParticles.playgroundCache = null; playgroundParticles.shurikenParticleSystem.SetParticles(playgroundParticles.particleCache,0); playgroundParticles.shurikenParticleSystem.Clear(); } /// /// Store the current state of a particle system as a Snapshot. /// public void Save () { if (isSnapshot) { Debug.Log("A snapshot can't store snapshot data within itself.", gameObject); return; } SaveRoutine ("New Snapshot "+(snapshots.Count+1).ToString()); } /// /// Store and name the current state of a particle system as a Snapshot. /// /// Save name. public void Save (string saveName) { if (isSnapshot) { Debug.Log("A snapshot can't store snapshot data within itself.", gameObject); return; } SaveRoutine (saveName); } /// /// Store the current state of a particle system as a Snapshot asynchronously. /// public void SaveAsynchronous () { if (isSnapshot) { Debug.Log("A snapshot can't store snapshot data within itself.", gameObject); return; } StartCoroutine (SaveAsynchronousRoutine ("New Snapshot "+(snapshots.Count+1).ToString())); } /// /// Store and name the current state of a particle system as a Snapshot asynchronously. /// public void SaveAsynchronous (string saveName) { if (isSnapshot) { Debug.Log("A snapshot can't store snapshot data within itself.", gameObject); return; } StartCoroutine (SaveAsynchronousRoutine (saveName)); } bool isSaving = false; void SaveRoutine (string saveName) { isSaving = true; PlaygroundSave data = new PlaygroundSave(); data.settings = PlaygroundC.Particle(); data.settings.isSnapshot = true; data.Save(this); data.settings.transform.parent = particleSystemTransform; data.settings.transform.name = saveName; data.settings.timeOfSnapshot = localTime; data.name = saveName; data.time = localTime; data.particleCount = particleCount; data.lifetime = lifetime; data.version = PlaygroundC.version; snapshots.Add (data); PlaygroundC.reference.particleSystems.Remove (data.settings); #if UNITY_EDITOR if (!PlaygroundC.reference.showSnapshotsInHierarchy) data.settings.gameObject.hideFlags = HideFlags.HideInHierarchy; #endif isSaving = false; } IEnumerator SaveAsynchronousRoutine (string saveName) { isSaving = true; PlaygroundSave data = new PlaygroundSave(); data.settings = PlaygroundC.Particle(); data.settings.isSnapshot = true; yield return null; data.SaveAsync(this); while (data.IsSaving()) yield return null; data.settings.transform.parent = particleSystemTransform; data.settings.transform.name = saveName; data.settings.timeOfSnapshot = localTime; data.name = saveName; data.time = localTime; data.particleCount = particleCount; data.lifetime = lifetime; data.version = PlaygroundC.version; snapshots.Add (data); PlaygroundC.reference.particleSystems.Remove (data.settings); #if UNITY_EDITOR if (!PlaygroundC.reference.showSnapshotsInHierarchy) data.settings.gameObject.hideFlags = HideFlags.HideInHierarchy; #endif isSaving = false; } /// /// Load from a saved data state (Snapshot) using an int. /// /// Load pointer. public void Load (int loadPointer) { if (snapshots.Count>0) { loadPointer = loadPointer%snapshots.Count; StartCoroutine(LoadRoutine(loadPointer, 0)); } else { Debug.Log ("No data to load from. Please use PlaygroundParticlesC.Save() to store a particle system's current state.", particleSystemGameObject); } } /// /// Load from a saved data state (Snapshot) using a string. /// /// Load name. public void Load (string loadName) { if (snapshots.Count>0) { for (int i = 0; i /// Load from a saved data state (Snapshot) and apply a mask to hide specified particles. /// The mask sorting will be determined by the loading snapshot's mask settings. /// /// The Snapshot you wish to load. /// The amount of masked particles in the particle array. public void LoadAndApplyMask (int loadPointer, int loadMask) { if (snapshots.Count>0) { loadPointer = loadPointer%snapshots.Count; StartCoroutine(LoadRoutine(loadPointer, loadMask)); } else { Debug.Log ("No data to load from. Please use PlaygroundParticlesC.Save() to store a particle system's current state.", particleSystemGameObject); } } // Snapshot loading routine bool isLoading = false; bool transitionAvailable = false; IEnumerator LoadRoutine (int loadPointer, int mask) { if (loadTransition && loadTransitionTime>0 && snapshots[loadPointer].transitionMultiplier>0 && transitionAvailable && !isYieldRefreshing) { if (snapshots[loadPointer].loadMaterial && !snapshots[loadPointer].setMaterialAfterTransition && snapshots[loadPointer].settings.particleSystemRenderer.sharedMaterial!=null) particleSystemRenderer.sharedMaterial = snapshots[loadPointer].settings.particleSystemRenderer.sharedMaterial; if (mask>0) { mask = Mathf.Clamp (mask, 0, snapshots[loadPointer].settings.particleCount); if (snapshots[loadPointer].settings.snapshotData.isMasked==null || snapshots[loadPointer].settings.snapshotData.isMasked.Length!=snapshots[loadPointer].settings.particleCount) { snapshots[loadPointer].settings.snapshotData.isMasked = new bool[snapshots[loadPointer].settings.particleCount]; snapshots[loadPointer].settings.snapshotData.maskAlpha = new float[snapshots[loadPointer].settings.particleCount]; } snapshots[loadPointer].settings.applyParticleMask = true; snapshots[loadPointer].settings.particleMask = mask; snapshots[loadPointer].settings.RefreshMaskSorting(); snapshots[loadPointer].settings.snapshotData.maskSorting = (int[])snapshots[loadPointer].settings.playgroundCache.maskSorting.Clone(); for (int i = 0; iparticleCount) { bool isResizing = true; PlaygroundC.RunAsync(()=>{ System.Array.Resize(ref transitionPosition, loadParticleCount); System.Array.Resize(ref transitionColor, loadParticleCount); System.Array.Resize(ref transitionSize, loadParticleCount); System.Array.Resize(ref transitionRotation, loadParticleCount); System.Array.Resize(ref transitionRotation, loadParticleCount); System.Array.Resize(ref playgroundCache.position, loadParticleCount); System.Array.Resize(ref playgroundCache.color, loadParticleCount); System.Array.Resize(ref playgroundCache.size, loadParticleCount); System.Array.Resize(ref playgroundCache.rotation, loadParticleCount); for (int p = particleCount; p=loadParticleLifetime-loadSnapshotData.lifetimeSubtraction[p]) { loadSnapshotData.position[p] = loadSnapshotData.targetPosition[p]; transitionColor[p].a = 0; } if (transitionPosition[p].y == initPosY || playgroundCache.life[p%particleCount]>=lifetime-playgroundCache.lifetimeSubtraction[p%particleCount]) { transitionPosition[p] = loadSnapshotData.targetPosition[p]; transitionColor[p].a = 0; } } // Transition while (PlaygroundC.globalTime0 && currentParticleCount==particleCount) { float currentTime = PlaygroundC.globalTime; PlaygroundC.RunAsync(()=>{ float t = TransitionType (thisSnapshotTransition, (currentTime-transitionStartTime)/(loadTransitionTime*snapshots[loadPointer].transitionMultiplier)); for (int p = 0; p=loadParticleCount)) playgroundCache.color[p].a = (byte)Mathf.Lerp (playgroundCache.color[p].a, 0f, t); SetParticleColorInternal(p, playgroundCache.color[p]); // Size playgroundCache.size[p] = Mathf.Lerp (transitionSize[p], loadSnapshotData.size[p%loadParticleCount], t); #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 particleCache[p].size = playgroundCache.size[p]; #else particleCache[p].startSize = playgroundCache.size[p]; #endif // Rotation playgroundCache.rotation[p] = Mathf.Lerp (transitionRotation[p], loadSnapshotData.rotation[p%loadParticleCount], t); particleCache[p].rotation = playgroundCache.rotation[p]; } else { playgroundCache.color[p].a = (byte)Mathf.Lerp (playgroundCache.color[p].a, 0, t); SetParticleColorInternal(p, playgroundCache.color[p]); } } }); yield return null; if (firstFrameDone && currentParticleCount==particleCount) { if (syncPositionsOnMainThread) for (int p = 0; p(); if (PlaygroundC.reference==null) { PlaygroundC.reference = PlaygroundC.ResourceInstantiate("Playground Manager").GetComponent(); } if (playgroundCache==null) playgroundCache = new PlaygroundCache(); if (thisInstance==null) thisInstance = this; if (particleSystemGameObject==null) { particleSystemGameObject = gameObject; particleSystemTransform = transform; particleSystemRenderer = GetComponent(); shurikenParticleSystem = particleSystemGameObject.GetComponent(); particleSystemRenderer2 = gameObject.GetComponent().GetComponent() as ParticleSystemRenderer; } } // YieldedRefresh makes sure that Playground Manager and simulation time is ready before this particle system bool isYieldRefreshing = false; bool initialized = false; public IEnumerator YieldedRefresh () { if (isSnapshot) yield break; if (isYieldRefreshing) yield break; bool okToLoadFromStart = true; if (isSettingParticleCount || isSettingLifetime) okToLoadFromStart = false; while (!PlaygroundC.IsReady()) yield return null; while (isSettingParticleCount) yield return null; while (isSettingLifetime) yield return null; if (!prewarm && (sorting==SORTINGC.NearestNeighbor || sorting==SORTINGC.NearestNeighborReversed)) { shurikenParticleSystem.Clear(); if (source==SOURCEC.SkinnedWorldObject) {yield return null;} SetLifetime(thisInstance, SORTINGC.Burst, lifetime); while (isSettingLifetime) yield return null; isYieldRefreshing = false; StartCoroutine(Prewarm(loop?prewarmTime:0f, 0)); yield return null; while (isPrewarming) yield return null; SetParticleTimeNow(thisInstance); initialized = true; yield break; } while (isSettingParticleCount || isSettingLifetime) yield return null; isYieldRefreshing = true; // Snapshot load #if UNITY_EDITOR if (!UnityEditor.EditorApplication.isPlaying) okToLoadFromStart = false; #endif if (okToLoadFromStart && loadFromStart && snapshots.Count>0) { SetLifetime(thisInstance, sorting, lifetime); Load(loadFrom); yield return null; isYieldRefreshing = false; initialized = true; yield break; } SetLifetime(thisInstance, sorting, lifetime); if (multithreadedStartup) { while (isSettingLifetime) yield return null; yield return null; } transitionAvailable = true; hasActiveParticles = true; threadHadNoActiveParticles = false; isYieldRefreshing = false; emissionStopped = 0; if (prewarm) { prewarmCycles = Mathf.Clamp (prewarmCycles, 0, 1000); StartCoroutine(Prewarm(prewarmTime, prewarmCycles)); } while (isPrewarming) yield return null; initialized = true; } /// /// Prewarms this particle system upon boot when prewarm is set to true. /// The prewarmTime determines where in the lifetime cycle the system should start after prewarming. /// The amount of prewarmCycles will determine the result resolution. /// IEnumerator Prewarm (float time, int resolution) { if (isPrewarming || isLoading) yield break; if (source==SOURCEC.SkinnedWorldObject || HasTurbulence()) yield return null; isPrewarming = true; while (isSettingParticleCount || isSettingLifetime) yield return null; resolution = Mathf.Clamp (resolution, 0, 1000); int currentCycles = 0; float storedLocalTime = 0f; bool isPrewarmingAsync = false; isReadyForThreadedCalculations = PrepareThreadedCalculations(); if (isReadyForThreadedCalculations) { localTime = PlaygroundC.globalTime; lastTimeUpdated = PlaygroundC.globalTime; if (sorting==SORTINGC.NearestNeighbor||sorting==SORTINGC.NearestNeighborReversed) { if (nearestNeighborOriginMethod==NEARESTNEIGHBORORIGINMETHOD.Transform && nearestNeighborOriginTransform!=null) { nearestNeighborOriginTransformPosition = shurikenParticleSystem.simulationSpace==ParticleSystemSimulationSpace.World? nearestNeighborOriginTransform.position: particleSystemTransform.InverseTransformPoint(nearestNeighborOriginTransform.position); } storedLocalTime = localTime; SetLifetime(thisInstance, SORTINGC.Burst, lifetime); while (isSettingLifetime) {yield return null;} localTime = storedLocalTime; for (int p=0;p0) { isPrewarmingAsync = true; PlaygroundC.RunAsync(()=>{ while (currentCycles<=resolution) { PrewarmStepper(time, resolution); currentCycles++; } if (syncPositionsOnMainThread && calculateDeltaMovement) { for (int p = 0; p0) { for (int i = 0; i0) { for (int i = 0; i(); StartCoroutine (Boot()); } 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; m0) { for (int i = 0; i(); int[] quantityList = new int[meshes.Length]; int i = 0; for (; i /// Holds information for a particle system Snapshot. /// [Serializable] public class PlaygroundSave { /// /// The name of this PlaygroundSave. /// [HideInInspector] public string name; /// /// The global time when this PlaygroundSave was created. /// [HideInInspector] public float time; /// /// The lifetime of the particle system. /// [HideInInspector] public float lifetime; /// /// The particle count of the particle system. /// [HideInInspector] public int particleCount; /// /// The version this snapshot was made with. /// [HideInInspector] public float version = 0; /// /// The cached settings of the particle system (Particles are stored in settings.snapshotData). /// [HideInInspector] public PlaygroundParticlesC settings; /// /// The stored transform information. /// [HideInInspector] public PlaygroundTransformC transform; /// /// The stored material. /// [HideInInspector] public Material material; /// /// The method to load data from this PlaygroundSave. /// 0 = Settings+Particles, 1 = Settings only, 2 = Particles only /// [HideInInspector] public int loadMode = 0; /// /// Should the stored transform information load? /// [HideInInspector] public bool loadTransform = true; /// /// Should the stored material load? /// [HideInInspector] public bool loadMaterial = true; /// /// Should the material load before or after transition? /// [HideInInspector] public bool setMaterialAfterTransition = true; /// /// The multiplier of transition time. /// [HideInInspector] public float transitionMultiplier = 1f; /// /// The transition type of this PlaygroundSave. /// [HideInInspector] public INDIVIDUALTRANSITIONTYPEC transitionType; [HideInInspector] public bool unfolded = false; bool isLoading = false; bool isSaving = false; /// /// Determines whether this PlaygroundSave is loading. /// /// true if this PlaygroundSave is loading; otherwise, false. public bool IsLoading () { return isLoading; } /// /// Determines whether this PlaygroundSave is saving. /// /// true if this PlaygroundSave is saving; otherwise, false. public bool IsSaving () { return isSaving; } /// /// Loads the data from this PlaygroundSave. /// /// Load to. public void Load (PlaygroundParticlesC loadTo) { if (loadMaterial && (setMaterialAfterTransition||!loadTo.loadTransition) && settings.particleSystemRenderer.sharedMaterial!=null) loadTo.particleSystemRenderer.sharedMaterial = settings.particleSystemRenderer.sharedMaterial; isLoading = true; PlaygroundC.RunAsync(()=>{ switch (loadMode) { case 0: settings.CopyTo(loadTo); loadTo.playgroundCache = settings.snapshotData.Clone(); break; case 1: settings.CopyTo(loadTo); break; case 2: loadTo.playgroundCache = settings.snapshotData.Clone(); break; default: settings.CopyTo(loadTo); loadTo.playgroundCache = settings.snapshotData.Clone(); break; } isLoading = false; }); if (loadTransform) transform.GetFromTransform (loadTo.particleSystemTransform); } /// /// Saves the data into this PlaygroundSave. /// /// Playground particles. public void Save (PlaygroundParticlesC playgroundParticles) { isSaving = true; transform = new PlaygroundTransformC(); transform.SetFromTransform (playgroundParticles.particleSystemTransform); settings.particleSystemRenderer.sharedMaterial = playgroundParticles.particleSystemRenderer.sharedMaterial; playgroundParticles.CopyTo(settings); settings.snapshotData = playgroundParticles.playgroundCache.Clone(); isSaving = false; } /// /// Saves the data into this PlaygroundSave asynchronously. /// /// Playground particles. public void SaveAsync (PlaygroundParticlesC playgroundParticles) { isSaving = true; transform = new PlaygroundTransformC(); transform.SetFromTransform (playgroundParticles.particleSystemTransform); settings.particleSystemRenderer.sharedMaterial = playgroundParticles.particleSystemRenderer.sharedMaterial; PlaygroundC.RunAsync(()=>{ playgroundParticles.CopyTo(settings); settings.snapshotData = playgroundParticles.playgroundCache.Clone(); isSaving = false; }); } /// /// Returns a copy of this PlaygroundSave. /// public PlaygroundSave Clone () { PlaygroundSave playgroundSave = new PlaygroundSave(); settings.CopyTo(playgroundSave.settings); playgroundSave.name = name; playgroundSave.time = time; playgroundSave.lifetime = lifetime; playgroundSave.particleCount = particleCount; playgroundSave.loadMode = loadMode; playgroundSave.loadTransform = loadTransform; playgroundSave.loadMaterial = loadMaterial; playgroundSave.setMaterialAfterTransition = setMaterialAfterTransition; playgroundSave.material = material; playgroundSave.transitionMultiplier = transitionMultiplier; playgroundSave.transitionType = transitionType; playgroundSave.unfolded = unfolded; return playgroundSave; } } /// /// Holds information for a PlaygroundEventParticle. The Playground Event Particle contains detailed data upon an event and is sent towards any event listeners. /// [Serializable] public class PlaygroundEventParticle { /// /// The initial size of this particle. /// [HideInInspector] public float initialSize; /// /// The lifetime size of this particle. /// [HideInInspector] public float size; /// /// The rotation of this particle. /// [HideInInspector] public float rotation; /// /// The lifetime of this particle. /// [HideInInspector] public float life; /// /// The total time this particle will live. This will take lifetime subtraction into account. /// [HideInInspector] public float totalLifetime; /// /// The time of birth for this particle. /// [HideInInspector] public float birth; /// /// The delayed time of birth when emission has changed. /// [HideInInspector] public float birthDelay; /// /// The time of death for this particle. /// [HideInInspector] public float death; /// /// The emission for this particle (controlled by emission rate). /// [HideInInspector] public bool emission; /// /// Determines if this particle should rebirth. /// [HideInInspector] public bool rebirth; /// /// The offset in birth-death (sorting). /// [HideInInspector] public float lifetimeOffset; /// /// The velocity of this particle. /// [HideInInspector] public Vector3 velocity; /// /// The initial velocity of this particle. /// [HideInInspector] public Vector3 initialVelocity; /// /// The initial local velocity of this particle. /// [HideInInspector] public Vector3 initialLocalVelocity; /// /// The position of this particle. /// [HideInInspector] public Vector3 position; /// /// The source position at birth for this particle. /// [HideInInspector] public Vector3 targetPosition; /// /// The source direction at birth for this particle. /// [HideInInspector] public Vector3 targetDirection; /// /// The previous source position for this particle (used to calculate delta movement). /// [HideInInspector] public Vector3 previousTargetPosition; /// /// The previous calculated frame's particle position. /// [HideInInspector] public Vector3 previousParticlePosition; /// /// The calculated particle position for collision. /// [HideInInspector] public Vector3 collisionParticlePosition; /// /// The delta to compensate for moving particles in local space. /// [HideInInspector] public Vector3 localSpaceMovementCompensation; /// /// The scattered position to apply on this particle birth. /// [HideInInspector] public Vector3 scatterPosition; /// /// The initial rotation of this particle. /// [HideInInspector] public float initialRotation; /// /// The rotation speed of this particle. /// [HideInInspector] public float rotationSpeed; /// /// The current color of this particle. /// [HideInInspector] public Color32 color; /// /// The color set from script of this particle. /// [HideInInspector] public Color32 scriptedColor; /// /// The set source color. /// [HideInInspector] public Color32 initialColor; /// /// The color gradient for this particle if Color Source is set to LifetimeColors. /// [HideInInspector] public int lifetimeColorId; /// /// The particle does not respond to forces during its lifetime. /// [HideInInspector] public bool noForce; /// /// The particle is non birthed. /// [HideInInspector] public bool isNonBirthed; /// /// The particle is in its first loop. /// [HideInInspector] public bool isFirstLoop; /// /// The id of this particle. /// [HideInInspector] public int particleId; /// /// The id of the particle system this particle belongs to (list position in Playground Manager). /// [HideInInspector] public int particleSystemId; /// /// The id of the last manipulator affecting this particle. /// [HideInInspector] public int manipulatorId; /// /// The interaction with property manipulators of this particle. /// [HideInInspector] public bool changedByProperty; /// /// The interaction with property manipulators that change color of this particle. /// [HideInInspector] public bool changedByPropertyColor; /// /// The interaction with property manipulators that change color over time of this particle. /// [HideInInspector] public bool changedByPropertyColorLerp; /// /// The interaction with property manipulators that change color and wants to keep alpha. /// [HideInInspector] public bool changedByPropertyColorKeepAlpha; /// /// The interaction with property manipulators that change size of this particle. /// [HideInInspector] public bool changedByPropertySize; /// /// The interaction with property manipulators that change target of this particle. /// [HideInInspector] public bool changedByPropertyTarget; /// /// The interaction with death manipulators that forces a particle to a sooner end. /// [HideInInspector] public bool changedByPropertyDeath; /// /// The property target pointer for this particle. /// [HideInInspector] public int propertyTarget; /// /// The property target id for this particle (pairing a particle's target to a manipulator). /// [HideInInspector] public int propertyId; /// /// The property color id for this particles (pairing a particle's color to a manipulator). /// [HideInInspector] public int propertyColorId; /// /// The manipulator id to exclude to not affect this particle. /// [HideInInspector] public int excludeFromManipulatorId; /// /// Is this particle masked? /// [HideInInspector] public bool isMasked; /// /// The alpha of this masked particle. /// [HideInInspector] public float maskAlpha; /// /// The collision transform of this collided particle. /// [HideInInspector] public Transform collisionTransform; /// /// The collision collider of this collided particle (3d). /// [HideInInspector] public Collider collisionCollider; /// /// The collision collider of this collided particle (2d). /// [HideInInspector] public Collider2D collisionCollider2D; /// /// Copies this PlaygroundEventParticle. /// public PlaygroundEventParticle Clone () { PlaygroundEventParticle playgroundEventParticle = new PlaygroundEventParticle(); playgroundEventParticle.initialSize = initialSize; playgroundEventParticle.size = size; playgroundEventParticle.life = life; playgroundEventParticle.totalLifetime = totalLifetime; playgroundEventParticle.rotation = rotation; playgroundEventParticle.birth = birth; playgroundEventParticle.birthDelay = birthDelay; playgroundEventParticle.death = death; playgroundEventParticle.emission = emission; playgroundEventParticle.rebirth = rebirth; playgroundEventParticle.lifetimeOffset = lifetimeOffset; playgroundEventParticle.velocity = velocity; playgroundEventParticle.initialVelocity = initialVelocity; playgroundEventParticle.initialLocalVelocity = initialLocalVelocity; playgroundEventParticle.position = position; playgroundEventParticle.targetPosition = targetPosition; playgroundEventParticle.targetDirection = targetDirection; playgroundEventParticle.previousTargetPosition = previousTargetPosition; playgroundEventParticle.previousParticlePosition = previousParticlePosition; playgroundEventParticle.collisionParticlePosition = collisionParticlePosition; playgroundEventParticle.localSpaceMovementCompensation = localSpaceMovementCompensation; playgroundEventParticle.scatterPosition = scatterPosition; playgroundEventParticle.initialRotation = initialRotation; playgroundEventParticle.rotationSpeed = rotationSpeed; playgroundEventParticle.color = color; playgroundEventParticle.scriptedColor = scriptedColor; playgroundEventParticle.initialColor = initialColor; playgroundEventParticle.lifetimeColorId = lifetimeColorId; playgroundEventParticle.noForce = noForce; playgroundEventParticle.changedByProperty = changedByProperty; playgroundEventParticle.changedByPropertyColor = changedByPropertyColor; playgroundEventParticle.changedByPropertyColorLerp = changedByPropertyColorLerp; playgroundEventParticle.changedByPropertyColorKeepAlpha = changedByPropertyColorKeepAlpha; playgroundEventParticle.changedByPropertySize = changedByPropertySize; playgroundEventParticle.changedByPropertyTarget = changedByPropertyTarget; playgroundEventParticle.changedByPropertyDeath = changedByPropertyDeath; playgroundEventParticle.propertyTarget = propertyTarget; playgroundEventParticle.propertyId = propertyId; playgroundEventParticle.excludeFromManipulatorId = excludeFromManipulatorId; playgroundEventParticle.propertyColorId = propertyColorId; playgroundEventParticle.particleId = particleId; playgroundEventParticle.particleSystemId = particleSystemId; playgroundEventParticle.manipulatorId = manipulatorId; playgroundEventParticle.isMasked = isMasked; playgroundEventParticle.maskAlpha = maskAlpha; playgroundEventParticle.collisionTransform = collisionTransform; playgroundEventParticle.collisionCollider = collisionCollider; playgroundEventParticle.collisionCollider2D = collisionCollider2D; return playgroundEventParticle; } } /// /// The Playground Cache contains all data for particles in built-in arrays. /// [Serializable] public class PlaygroundCache { /// /// The initial size of each particle. /// [HideInInspector] public float[] initialSize; /// /// The lifetime size of each particle. /// [HideInInspector] public float[] size; /// /// The rotation of each particle. /// [HideInInspector] public float[] rotation; /// /// The current lifetime of each particle. /// [HideInInspector] public float[] life; /// /// The lifetime subtraction of each particle (applied when using random between two values). /// [HideInInspector] public float[] lifetimeSubtraction; /// /// The time of birth for each particle. /// [HideInInspector] public float[] birth; /// /// The delayed time of birth when emission has changed. /// [HideInInspector] public float[] birthDelay; /// /// The time of death for each particle. /// [HideInInspector] public float[] death; /// /// The emission for each particle (controlled by emission rate). /// [HideInInspector] public bool[] emission; /// /// The rebirth for each particle. /// [HideInInspector] public bool[] rebirth; /// /// The offset in birth-death (sorting). /// [HideInInspector] public float[] lifetimeOffset; /// /// The velocity of each particle in. /// [HideInInspector] public Vector3[] velocity; /// /// The initial velocity of each particle. /// [HideInInspector] public Vector3[] initialVelocity; /// /// The initial local velocity of each particle. /// [HideInInspector] public Vector3[] initialLocalVelocity; /// /// The position of each particle. /// [HideInInspector] public Vector3[] position; /// /// The source position for each particle. /// [HideInInspector] public Vector3[] targetPosition; /// /// The source direction for each particle. /// [HideInInspector] public Vector3[] targetDirection; /// /// The previous source position for each particle (used to calculate delta movement). /// [HideInInspector] public Vector3[] previousTargetPosition; /// /// The previous calculated frame's particle position. /// [HideInInspector] public Vector3[] previousParticlePosition; /// /// The calculated particle position for collision (depending on collision stepper). /// [HideInInspector] public Vector3[] collisionParticlePosition; /// /// The delta to compensate for moving particles in local space. /// [HideInInspector] public Vector3[] localSpaceMovementCompensation; /// /// The scattered position to apply on each particle birth. /// [HideInInspector] public Vector3[] scatterPosition; /// /// The initial rotation of each particle. /// [HideInInspector] public float[] initialRotation; /// /// The rotation speed of each particle. /// [HideInInspector] public float[] rotationSpeed; /// /// The color of each particle. /// [HideInInspector] public Color32[] color; /// /// The color set from script of each particle. /// [HideInInspector] public Color32[] scriptedColor; /// /// The set source color. /// [HideInInspector] public Color32[] initialColor; /// /// The color gradient for each particle if Color Source is set to LifetimeColors. /// [HideInInspector] public int[] lifetimeColorId; /// /// The particle will no longer respond to any forces during its lifetime. /// [HideInInspector] public bool[] noForce; /// /// The particle is not birthed yet. /// [HideInInspector] public bool[] isNonBirthed; /// /// The particle is in its first loop. /// [HideInInspector] public bool[] isFirstLoop; /// /// The particle is set to simulate. /// [HideInInspector] public bool[] simulate; /// /// Determines if the particles are calculated during this frame. /// This helps to see if a particle has gotten its expected values during this frame or if it will be ready later. /// The method is used when syncing particles onto the Main-Thread to ensure smooth movement upon heavier calculated particle systems. /// [HideInInspector] public bool[] isCalculatedThisFrame; // Manipulator specific arrays: /// /// The interaction with property manipulators of each particle. /// [HideInInspector] public bool[] changedByProperty; /// /// The interaction with property manipulators that change color of each particle. /// [HideInInspector] public bool[] changedByPropertyColor; /// /// The interaction with property manipulators that change color over time of each particle. /// [HideInInspector] public bool[] changedByPropertyColorLerp; /// /// The interaction with property manipulators that change color and wants to keep alpha. /// [HideInInspector] public bool[] changedByPropertyColorKeepAlpha; /// /// The interaction with property manipulators that change size of each particle. /// [HideInInspector] public bool[] changedByPropertySize; /// /// The interaction with property manipulators that change target of each particle. /// [HideInInspector] public bool[] changedByPropertyTarget; /// /// The interaction with death manipulators that forces a particle to a sooner end. /// [HideInInspector] public bool[] changedByPropertyDeath; /// /// The property target pointer for each particle. /// [HideInInspector] public int[] propertyTarget; /// /// The property target id for each particle (pairing a particle's target to a manipulator). /// [HideInInspector] public int[] propertyId; /// /// The property color id for each particles (pairing a particle's color to a manipulator. /// [HideInInspector] public int[] propertyColorId; /// /// The id of the last manipulator affecting each particle. /// [HideInInspector] public int[] manipulatorId; /// /// The id of manipulator to not affect each particle. /// [HideInInspector] public int[] excludeFromManipulatorId; /// /// The lifetime loss of each particle. /// [HideInInspector] public float[] lifetimeLoss; /// /// Determines if a particle is masked. /// [HideInInspector] public bool[] isMasked; /// /// The alpha of each masked particle. /// [HideInInspector] public float[] maskAlpha; /// /// The sorting of the masked particles. /// [HideInInspector] public int[] maskSorting; /// /// Copies this PlaygroundCache. /// public PlaygroundCache Clone () { PlaygroundCache playgroundCache = new PlaygroundCache(); playgroundCache.initialSize = initialSize.Clone() as float[]; playgroundCache.size = size.Clone () as float[]; playgroundCache.life = life.Clone() as float[]; playgroundCache.lifetimeSubtraction = lifetimeSubtraction.Clone() as float[]; playgroundCache.rotation = rotation.Clone() as float[]; playgroundCache.birth = birth.Clone() as float[]; playgroundCache.birthDelay = birthDelay.Clone() as float[]; playgroundCache.death = death.Clone() as float[]; playgroundCache.emission = emission.Clone() as bool[]; playgroundCache.rebirth = rebirth.Clone() as bool[]; playgroundCache.lifetimeOffset = lifetimeOffset.Clone() as float[]; playgroundCache.velocity = velocity.Clone() as Vector3[]; playgroundCache.initialVelocity = initialVelocity.Clone() as Vector3[]; playgroundCache.initialLocalVelocity = initialLocalVelocity.Clone() as Vector3[]; playgroundCache.position = position.Clone () as Vector3[]; playgroundCache.targetPosition = targetPosition.Clone() as Vector3[]; playgroundCache.targetDirection = targetDirection.Clone() as Vector3[]; playgroundCache.previousTargetPosition = previousTargetPosition.Clone() as Vector3[]; playgroundCache.previousParticlePosition = previousParticlePosition.Clone() as Vector3[]; playgroundCache.collisionParticlePosition = collisionParticlePosition.Clone () as Vector3[]; playgroundCache.localSpaceMovementCompensation = localSpaceMovementCompensation.Clone() as Vector3[]; playgroundCache.scatterPosition = scatterPosition.Clone() as Vector3[]; playgroundCache.initialRotation = initialRotation.Clone() as float[]; playgroundCache.rotationSpeed = rotationSpeed.Clone() as float[]; playgroundCache.color = color.Clone() as Color32[]; playgroundCache.scriptedColor = scriptedColor.Clone() as Color32[]; playgroundCache.initialColor = initialColor.Clone () as Color32[]; playgroundCache.lifetimeColorId = lifetimeColorId.Clone () as int[]; playgroundCache.noForce = noForce.Clone() as bool[]; playgroundCache.changedByProperty = changedByProperty.Clone() as bool[]; playgroundCache.changedByPropertyColor = changedByPropertyColor.Clone() as bool[]; playgroundCache.changedByPropertyColorLerp = changedByPropertyColorLerp.Clone() as bool[]; playgroundCache.changedByPropertyColorKeepAlpha = changedByPropertyColorKeepAlpha.Clone () as bool[]; playgroundCache.changedByPropertySize = changedByPropertySize.Clone() as bool[]; playgroundCache.changedByPropertyTarget = changedByPropertyTarget.Clone() as bool[]; playgroundCache.changedByPropertyDeath = changedByPropertyDeath.Clone() as bool[]; playgroundCache.propertyTarget = propertyTarget.Clone() as int[]; playgroundCache.propertyId = propertyId.Clone() as int[]; playgroundCache.excludeFromManipulatorId = excludeFromManipulatorId.Clone() as int[]; playgroundCache.propertyColorId = propertyColorId.Clone() as int[]; playgroundCache.manipulatorId = manipulatorId.Clone() as int[]; playgroundCache.isMasked = isMasked.Clone() as bool[]; playgroundCache.maskAlpha = maskAlpha.Clone() as float[]; if (maskSorting!=null) playgroundCache.maskSorting = maskSorting.Clone() as int[]; playgroundCache.isNonBirthed = isNonBirthed.Clone() as bool[]; playgroundCache.isFirstLoop = isFirstLoop.Clone() as bool[]; playgroundCache.simulate = simulate.Clone() as bool[]; playgroundCache.isCalculatedThisFrame = isCalculatedThisFrame.Clone() as bool[]; if (playgroundCache.lifetimeLoss == null) playgroundCache.lifetimeLoss = new float[0]; else playgroundCache.lifetimeLoss = lifetimeLoss.Clone() as float[]; return playgroundCache; } } /// /// The collision cache contains information of all particle collisions. /// [Serializable] public class CollisionCache { /// /// Determines if a particle has collided with any colliders during its lifetime. /// [HideInInspector] public bool[] hasCollided; /// /// The position in world space a particle collided last. This will not update further during a particle's lifetime if using "sticky" particles. /// [HideInInspector] public Vector3[] collisionPosition; /// /// The collision normal of the surface the particle collided last. This will not update further during a particle's lifetime if using "sticky" particles. /// [HideInInspector] public Vector3[] collisionNormal; /// /// The transform of the object a particle collided with last. This will not update further during a particle's lifetime if using "sticky" particles. /// [HideInInspector] public Transform[] collisionTransform; [HideInInspector] public GameObject[] collisionGameObject; /// /// The position seen by the collision object's transform based on InverseTransformPoint. /// [HideInInspector] public Vector3[] collisionTransformPosition; /// /// The sticky position of a particle. This contains the position for particles that stick to their collided surface. /// [HideInInspector] public Vector3[] stickyPosition; public CollisionCache (int amount) { hasCollided = new bool[amount]; collisionPosition = new Vector3[amount]; collisionNormal = new Vector3[amount]; collisionTransform = new Transform[amount]; collisionGameObject = new GameObject[amount]; collisionTransformPosition = new Vector3[amount]; stickyPosition = new Vector3[amount]; } /// /// Sets the specified collision position, normal and transform at index. /// /// Index. /// Position. /// Normal. /// Transform. public void Set (int index, Vector3 position, Vector3 normal, Transform transform) { index = index%hasCollided.Length; hasCollided[index] = true; collisionPosition[index] = position; collisionNormal[index] = normal; collisionTransform[index] = transform; collisionGameObject[index] = transform.gameObject; } public void SetSticky (int index, Vector3 position, Vector3 normal, float offset, Transform transform) { index = index%hasCollided.Length; hasCollided[index] = true; collisionPosition[index] = position; collisionNormal[index] = normal; collisionTransform[index] = transform; collisionGameObject[index] = transform.gameObject; collisionTransformPosition[index] = transform.InverseTransformPoint(position+(collisionNormal[index]*offset)); } /// /// UpdateStickyPosition sets the sticky position based on the collision transform for a particle. The sticky position will update automatically when using "sticky" particles. /// /// Index to update. public void UpdateStickyPosition (int index) { index = index%stickyPosition.Length; if (collisionGameObject[index]!=null && collisionGameObject[index].activeSelf && collisionGameObject[index].activeInHierarchy) { stickyPosition[index] = collisionTransform[index].TransformPoint(collisionTransformPosition[index]); } else hasCollided[index] = false; } /// /// Resets the specified index. /// /// Index. public void Reset (int index) { index = index%hasCollided.Length; hasCollided[index] = false; } /// /// Clears out all the collisions. Use this if you want to toggle between having sticky and non-sticky behaviors. /// public void ClearCollisions () { for(int i = 0; i /// Copies this CollisionCache. /// public CollisionCache Clone () { CollisionCache collisionCache = new CollisionCache(collisionPosition.Length); collisionCache.hasCollided = (bool[])hasCollided.Clone(); collisionCache.collisionPosition = (Vector3[])collisionPosition.Clone(); collisionCache.collisionNormal = (Vector3[])collisionNormal.Clone(); collisionCache.collisionTransform = (Transform[])collisionTransform.Clone(); collisionCache.collisionTransformPosition = (Vector3[])collisionTransformPosition.Clone(); collisionCache.stickyPosition = (Vector3[])stickyPosition.Clone(); return collisionCache; } } /// /// The Simplex Noise algorithm applied in Turbulence. /// public class SimplexNoise { // Simplex noise based on http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf for public domain by courtesy of Stefan Gustavson. private static int[][] grad3 = new int[][] { new int[] {1,1,0}, new int[] {-1,1,0}, new int[] {1,-1,0}, new int[] {-1,-1,0}, new int[] {1,0,1}, new int[] {-1,0,1}, new int[] {1,0,-1}, new int[] {-1,0,-1}, new int[] {0,1,1}, new int[] {0,-1,1}, new int[] {0,1,-1}, new int[] {0,-1,-1}}; private static int[][] grad4 = new int[][] { new int[] {0,1,1,1}, new int[] {0,1,1,-1}, new int[] {0,1,-1,1}, new int[] {0,1,-1,-1}, new int[] {0,-1,1,1}, new int[] {0,-1,1,-1}, new int[] {0,-1,-1,1}, new int[] {0,-1,-1,-1}, new int[] {1,0,1,1}, new int[] {1,0,1,-1}, new int[] {1,0,-1,1}, new int[] {1,0,-1,-1}, new int[] {-1,0,1,1}, new int[] {-1,0,1,-1}, new int[] {-1,0,-1,1}, new int[] {-1,0,-1,-1}, new int[] {1,1,0,1}, new int[] {1,1,0,-1}, new int[] {1,-1,0,1}, new int[] {1,-1,0,-1}, new int[] {-1,1,0,1}, new int[] {-1,1,0,-1}, new int[] {-1,-1,0,1}, new int[] {-1,-1,0,-1}, new int[] {1,1,1,0}, new int[] {1,1,-1,0}, new int[] {1,-1,1,0}, new int[] {1,-1,-1,0}, new int[] {-1,1,1,0}, new int[] {-1,1,-1,0}, new int[] {-1,-1,1,0}, new int[] {-1,-1,-1,0}}; private static int[] p = {151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180}; // To remove the need for index wrapping, double the permutation table length private static int[] perm = new int[512]; static SimplexNoise() { for(int i=0; i<512; i++) perm[i]=p[i & 255]; } // moved to constructor // A lookup table to traverse the simplex around a given point in 4D. // Details can be found where this table is used, in the 4D noise method. private static int[][] simplex = new int[][] { new int[] {0,1,2,3}, new int[] {0,1,3,2}, new int[] {0,0,0,0}, new int[] {0,2,3,1}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {1,2,3,0}, new int[] {0,2,1,3}, new int[] {0,0,0,0}, new int[] {0,3,1,2}, new int[] {0,3,2,1}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {1,3,2,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {1,2,0,3}, new int[] {0,0,0,0}, new int[] {1,3,0,2}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {2,3,0,1}, new int[] {2,3,1,0}, new int[] {1,0,2,3}, new int[] {1,0,3,2}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {2,0,3,1}, new int[] {0,0,0,0}, new int[] {2,1,3,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {2,0,1,3}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {3,0,1,2}, new int[] {3,0,2,1}, new int[] {0,0,0,0}, new int[] {3,1,2,0}, new int[] {2,1,0,3}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {0,0,0,0}, new int[] {3,1,0,2}, new int[] {0,0,0,0}, new int[] {3,2,0,1}, new int[] {3,2,1,0}}; // This method is a *lot* faster than using (int)Mathf.floor(x) private static int fastfloor(double x) { return x>0 ? (int)x : (int)x-1; } private static double dot(int[] g, double x, double y) { return g[0]*x + g[1]*y; } private static double dot(int[] g, double x, double y, double z) { return g[0]*x + g[1]*y + g[2]*z; } private static double dot(int[] g, double x, double y, double z, double w) { return g[0]*x + g[1]*y + g[2]*z + g[3]*w; } // 3D simplex noise public double noise(double xin, double yin, double zin) { double n0, n1, n2, n3; // Noise contributions from the four corners // Skew the input space to determine which simplex cell we're in double F3 = 1.0/3.0; double s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D int i = fastfloor(xin+s); int j = fastfloor(yin+s); int k = fastfloor(zin+s); double G3 = 1.0/6.0; // Very nice and simple unskew factor, too double t = (i+j+k)*G3; double X0 = i-t; // Unskew the cell origin back to (x,y,z) space double Y0 = j-t; double Z0 = k-t; double x0 = xin-X0; // The x,y,z distances from the cell origin double y0 = yin-Y0; double z0 = zin-Z0; // For the 3D case, the simplex shape is a slightly irregular tetrahedron. // Determine which simplex we are in. int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords if(x0>=y0) { if(y0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order } else { // x0 y0) ? 32 : 0; int c2 = (x0 > z0) ? 16 : 0; int c3 = (y0 > z0) ? 8 : 0; int c4 = (x0 > w0) ? 4 : 0; int c5 = (y0 > w0) ? 2 : 0; int c6 = (z0 > w0) ? 1 : 0; int c = c1 + c2 + c3 + c4 + c5 + c6; int i1, j1, k1, l1; // The integer offsets for the second simplex corner int i2, j2, k2, l2; // The integer offsets for the third simplex corner int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. // Many values of c will never occur, since e.g. x>y>z>w makes x=3 ? 1 : 0; j1 = simplex[c][1]>=3 ? 1 : 0; k1 = simplex[c][2]>=3 ? 1 : 0; l1 = simplex[c][3]>=3 ? 1 : 0; // The number 2 in the "simplex" array is at the second largest coordinate. i2 = simplex[c][0]>=2 ? 1 : 0; j2 = simplex[c][1]>=2 ? 1 : 0; k2 = simplex[c][2]>=2 ? 1 : 0; l2 = simplex[c][3]>=2 ? 1 : 0; // The number 1 in the "simplex" array is at the second smallest coordinate. i3 = simplex[c][0]>=1 ? 1 : 0; j3 = simplex[c][1]>=1 ? 1 : 0; k3 = simplex[c][2]>=1 ? 1 : 0; l3 = simplex[c][3]>=1 ? 1 : 0; // The fifth corner has all coordinate offsets = 1, so no need to look that up. double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords double y1 = y0 - j1 + G4; double z1 = z0 - k1 + G4; double w1 = w0 - l1 + G4; double x2 = x0 - i2 + 2.0*G4; // Offsets for third corner in (x,y,z,w) coords double y2 = y0 - j2 + 2.0*G4; double z2 = z0 - k2 + 2.0*G4; double w2 = w0 - l2 + 2.0*G4; double x3 = x0 - i3 + 3.0*G4; // Offsets for fourth corner in (x,y,z,w) coords double y3 = y0 - j3 + 3.0*G4; double z3 = z0 - k3 + 3.0*G4; double w3 = w0 - l3 + 3.0*G4; double x4 = x0 - 1.0 + 4.0*G4; // Offsets for last corner in (x,y,z,w) coords double y4 = y0 - 1.0 + 4.0*G4; double z4 = z0 - 1.0 + 4.0*G4; double w4 = w0 - 1.0 + 4.0*G4; // Work out the hashed gradient indices of the five simplex corners int ii = i & 255; int jj = j & 255; int kk = k & 255; int ll = l & 255; int gi0 = perm[ii+perm[jj+perm[kk+perm[ll]]]] % 32; int gi1 = perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]] % 32; int gi2 = perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]] % 32; int gi3 = perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]] % 32; int gi4 = perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]] % 32; // Calculate the contribution from the five corners double t0 = 0.5 - x0*x0 - y0*y0 - z0*z0 - w0*w0; if(t0<0) n0 = 0.0; else { t0 *= t0; n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0); } double t1 = 0.6 - x1*x1 - y1*y1 - z1*z1 - w1*w1; if(t1<0) n1 = 0.0; else { t1 *= t1; n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1); } double t2 = 0.6 - x2*x2 - y2*y2 - z2*z2 - w2*w2; if(t2<0) n2 = 0.0; else { t2 *= t2; n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2); } double t3 = 0.6 - x3*x3 - y3*y3 - z3*z3 - w3*w3; if(t3<0) n3 = 0.0; else { t3 *= t3; n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3); } double t4 = 0.6 - x4*x4 - y4*y4 - z4*z4 - w4*w4; if(t4<0) n4 = 0.0; else { t4 *= t4; n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4); } // Sum up and scale the result to cover the range [-1,1] return 27.0 * (n0 + n1 + n2 + n3 + n4); } } }