FiE-Game/Assets/Particle Playground/Scripts/PlaygroundParticlesC.cs

7808 lines
391 KiB
C#
Raw Normal View History

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using PlaygroundSplines;
namespace ParticlePlayground {
[RequireComponent (typeof(ParticleSystem))]
/// <summary>
/// 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.
/// </summary>
[ExecuteInEditMode()]
public class PlaygroundParticlesC : MonoBehaviour {
/*************************************************************************************************************************************************
PlaygroundParticlesC variables
*************************************************************************************************************************************************/
// Particle Playground settings
/// <summary>
/// The particle source method for distributing particles upon birth.
/// </summary>
[HideInInspector] public SOURCEC source;
/// <summary>
/// Current active state (when using state as source).
/// </summary>
[HideInInspector] public int activeState;
/// <summary>
/// If emission of particles is active on this PlaygroundParticles.
/// </summary>
[HideInInspector] public bool emit = true;
/// <summary>
/// Should a particle re-emit when reaching the end of its lifetime?
/// </summary>
[HideInInspector] public bool loop = true;
/// <summary>
/// Should particles be removed instantly when you set emit to false?
/// </summary>
[HideInInspector] public bool clearParticlesOnEmissionStop = false;
/// <summary>
/// Should the GameObject of this PlaygroundParticlesC disable when not looping?
/// </summary>
[HideInInspector] public bool disableOnDone = false;
/// <summary>
/// The routine that should run when particle simulation has ended.
/// </summary>
[HideInInspector] public ONDONE disableOnDoneRoutine;
/// <summary>
/// The rate to update this PlaygroundParticles.
/// </summary>
[HideInInspector] public int updateRate = 1;
/// <summary>
/// Determins if this particle system should calculate (can be overrided by PlaygroundC.calculate). This will automatically enable/disable when pauseCalculationWhenInvisible is set to true.
/// </summary>
[HideInInspector] public bool calculate = true;
/// <summary>
/// Calculate the delta movement force of this particle system.
/// </summary>
[HideInInspector] public bool calculateDeltaMovement = true;
/// <summary>
/// The strength to multiply delta movement with.
/// </summary>
[HideInInspector] public float deltaMovementStrength = 10f;
/// <summary>
/// The minimum strength to multiply delta movement with. This will apply when using deltaMovementStrengthValueMethod of RandomBetweenTwoValues.
/// </summary>
[HideInInspector] public float minDeltaMovementStrength = 0;
[HideInInspector] public VALUEMETHOD deltaMovementStrengthValueMethod;
/// <summary>
/// The current world object will change its vertices over time. This produces memory garbage with quantity based upon the mesh vertices.
/// </summary>
[HideInInspector] public bool worldObjectUpdateVertices = false;
/// <summary>
/// The current world object will change its normals over time. This produces memory garbage with quantity based upon the mesh normals.
/// </summary>
[HideInInspector] public bool worldObjectUpdateNormals = false;
/// <summary>
/// The method to set the Lifetime Sorting: Nearest Neighbor/Reversed with.
/// </summary>
[HideInInspector] public NEARESTNEIGHBORORIGINMETHOD nearestNeighborOriginMethod;
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public int nearestNeighborOrigin = 0;
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public Vector3 nearestNeighborOriginVector3;
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public Transform nearestNeighborOriginTransform;
/// <summary>
/// The amount of particles within this PlaygroundParticlesC object.
/// </summary>
[HideInInspector] public int particleCount;
/// <summary>
/// The percentage to emit of particleCount in bursts from this PlaygroundParticles.
/// </summary>
[HideInInspector] public float emissionRate = 1f;
/// <summary>
/// The method to calculate Overflow Offset with.
/// </summary>
[HideInInspector] public OVERFLOWMODEC overflowMode = OVERFLOWMODEC.SourceTransform;
/// <summary>
/// The offset direction and magnitude when particle count exceeds source count.
/// </summary>
[HideInInspector] public Vector3 overflowOffset;
/// <summary>
/// Should source position scattering be applied?
/// </summary>
[HideInInspector] public bool applySourceScatter = false;
/// <summary>
/// The minimum spread of source position scattering.
/// </summary>
[HideInInspector] public Vector3 sourceScatterMin;
/// <summary>
/// The maximum spread of source position scattering.
/// </summary>
[HideInInspector] public Vector3 sourceScatterMax;
/// <summary>
/// The scale of source scatter. This can be changed over time to move the scattering seamlessly.
/// </summary>
[HideInInspector] public Vector3 scatterScale = new Vector3(1f,1f,1f);
[HideInInspector] public MINMAXVECTOR3METHOD sourceScatterMethod;
/// <summary>
/// Sort mode for particle lifetime.
/// </summary>
[HideInInspector] public SORTINGC sorting = SORTINGC.Scrambled;
/// <summary>
/// Custom sorting for particle lifetime (when sorting is set to Custom).
/// </summary>
[HideInInspector] public AnimationCurve lifetimeSorting;
/// <summary>
/// Minimum size of particles.
/// </summary>
[HideInInspector] public float sizeMin = 1f;
/// <summary>
/// Maximum size of particles.
/// </summary>
[HideInInspector] public float sizeMax = 1f;
/// <summary>
/// The scale of minimum and maximum particle size.
/// </summary>
[HideInInspector] public float scale = 1f;
/// <summary>
/// Minimum initial particle rotation.
/// </summary>
[HideInInspector] public float initialRotationMin;
/// <summary>
/// Maximum initial particle rotation.
/// </summary>
[HideInInspector] public float initialRotationMax;
/// <summary>
/// Minimum amount to rotate a particle over time.
/// </summary>
[HideInInspector] public float rotationSpeedMin;
/// <summary>
/// Maximum amount to rotate a particle over time.
/// </summary>
[HideInInspector] public float rotationSpeedMax;
/// <summary>
/// Should the particles rotate towards their movement direction. The rotationNormal will determine from which angle the rotation is based on.
/// </summary>
[HideInInspector] public bool rotateTowardsDirection = false;
/// <summary>
/// The rotation direction normal when rotating towards direction (always normalized value).
/// </summary>
[HideInInspector] public Vector3 rotationNormal = -Vector3.forward;
/// <summary>
/// The method to apply lifetime values.
/// </summary>
[HideInInspector] public VALUEMETHOD lifetimeValueMethod;
/// <summary>
/// The maximum life of a particle in seconds.
/// </summary>
[HideInInspector] public float lifetime;
/// <summary>
/// The minimum life of a particle when using lifetimeValueMethod of RandomBetweenTwoValues.
/// </summary>
[HideInInspector] public float lifetimeMin = 0;
/// <summary>
/// The offset of lifetime in this particle system.
/// </summary>
[HideInInspector] public float lifetimeOffset;
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public float lifetimeEmission = 1f;
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public float minShurikenLifetime = .08f;
/// <summary>
/// Should lifetime size affect each particle?
/// </summary>
[HideInInspector] public bool applyLifetimeSize = true;
/// <summary>
/// The size over lifetime of each particle.
/// </summary>
[HideInInspector] public AnimationCurve lifetimeSize;
/// <summary>
/// Should particle array size affect each particle? This will multiply the size of each particle depending on its position in the particle array.
/// </summary>
[HideInInspector] public bool applyParticleArraySize;
/// <summary>
/// The size over particle array of each particle. This will multiply the size of each particle depending on its position in the particle array.
/// </summary>
[HideInInspector] public AnimationCurve particleArraySize;
/// <summary>
/// Should the particles transition back to their source position during their lifetime? Use transitionBackToSourceAmount to set the strength by a normalized AnimationCurve.
/// </summary>
[HideInInspector] public bool transitionBackToSource = false;
/// <summary>
/// The amount to transition back to the source position by a normalized AnimationCurve.
/// </summary>
[HideInInspector] public AnimationCurve transitionBackToSourceAmount;
/// <summary>
/// Should the particles only position on their source (and not apply any forces)?
/// </summary>
[HideInInspector] public bool onlySourcePositioning = false;
/// <summary>
/// Should the particles only position by lifetime positioning Vector3AnimationCurves?
/// </summary>
[HideInInspector] public bool onlyLifetimePositioning = false;
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public Vector3AnimationCurveC lifetimePositioning;
/// <summary>
/// Should scale over time of lifetime positioning apply?
/// </summary>
[HideInInspector] public bool applyLifetimePositioningTimeScale = false;
/// <summary>
/// Should scale of position of lifetime positioning apply?
/// </summary>
[HideInInspector] public bool applyLifetimePositioningPositionScale = false;
/// <summary>
/// The scale of time for lifetime positioning.
/// </summary>
[HideInInspector] public AnimationCurve lifetimePositioningTimeScale;
/// <summary>
/// The scale of positioning for lifetime positioning.
/// </summary>
[HideInInspector] public AnimationCurve lifetimePositioningPositionScale;
/// <summary>
/// The overall scale of lifetime positioning.
/// </summary>
[HideInInspector] public float lifetimePositioningScale = 1f;
/// <summary>
/// Should lifetime positioning use the direction normal of the source?
/// </summary>
[HideInInspector] public bool lifetimePositioningUsesSourceDirection = false;
/// <summary>
/// Should lifetime velocity affect particles?
/// </summary>
[HideInInspector] public bool applyLifetimeVelocity = false;
/// <summary>
/// The velocity over lifetime of each particle.
/// </summary>
[HideInInspector] public Vector3AnimationCurveC lifetimeVelocity;
/// <summary>
/// The lifetime velocity scale.
/// </summary>
[HideInInspector] public float lifetimeVelocityScale = 1f;
/// <summary>
/// Should initial velocity affect particles?
/// </summary>
[HideInInspector] public bool applyInitialVelocity = false;
/// <summary>
/// The minimum starting velocity of each particle.
/// </summary>
[HideInInspector] public Vector3 initialVelocityMin;
/// <summary>
/// The maximum starting velocity of each particle.
/// </summary>
[HideInInspector] public Vector3 initialVelocityMax;
/// <summary>
/// Determines if the value of initial velocity should be rectangular or spherical.
/// </summary>
[HideInInspector] public MINMAXVECTOR3METHOD initialVelocityMethod;
/// <summary>
/// Should initial local velocity affect particles?
/// </summary>
[HideInInspector] public bool applyInitialLocalVelocity = false;
/// <summary>
/// The minimum starting velocity of each particle with normal or transform direction.
/// </summary>
[HideInInspector] public Vector3 initialLocalVelocityMin;
/// <summary>
/// The maximum starting velocity of each particle with normal or transform direction.
/// </summary>
[HideInInspector] public Vector3 initialLocalVelocityMax;
/// <summary>
/// Determines if the value of initial local velocity should be rectangular or spherical.
/// </summary>
[HideInInspector] public MINMAXVECTOR3METHOD initialLocalVelocityMethod;
/// <summary>
/// Should the initial velocity shape be applied on particle re/birth?
/// </summary>
[HideInInspector] public bool applyInitialVelocityShape = false;
/// <summary>
/// The amount of velocity to apply of the spawning particle's initial/local velocity in form of a Vector3AnimationCurve.
/// </summary>
[HideInInspector] public Vector3AnimationCurveC initialVelocityShape;
/// <summary>
/// The scale of initial velocity shape.
/// </summary>
[HideInInspector] public float initialVelocityShapeScale = 1f;
/// <summary>
/// Should bending affect particles velocity?
/// </summary>
[HideInInspector] public bool applyVelocityBending;
/// <summary>
/// The amount to bend velocity of each particle.
/// </summary>
[HideInInspector] public Vector3 velocityBending;
/// <summary>
/// The type of velocity bending.
/// </summary>
[HideInInspector] public VELOCITYBENDINGTYPEC velocityBendingType;
/// <summary>
/// The constant force towards gravitational vector.
/// </summary>
[HideInInspector] public Vector3 gravity;
/// <summary>
/// The maximum positive- and negative velocity of each particle.
/// </summary>
[HideInInspector] public float maxVelocity = 100f;
/// <summary>
/// The force axis constraints of each particle.
/// </summary>
[HideInInspector] public PlaygroundAxisConstraintsC axisConstraints = new PlaygroundAxisConstraintsC();
/// <summary>
/// Particles inertia over time.
/// </summary>
[HideInInspector] public float damping;
/// <summary>
/// The overall scale of velocity.
/// </summary>
[HideInInspector] public float velocityScale = 1f;
/// <summary>
/// The color over lifetime.
/// </summary>
[HideInInspector] public Gradient lifetimeColor;
/// <summary>
/// The colors over lifetime (if Color Source is set to LifetimeColors).
/// </summary>
[HideInInspector] public List<PlaygroundGradientC> lifetimeColors = new List<PlaygroundGradientC>();
/// <summary>
/// The source to read color from (fallback on Lifetime Color if no source color is available).
/// </summary>
[HideInInspector] public COLORSOURCEC colorSource = COLORSOURCEC.Source;
/// <summary>
/// Should the source color use alpha from Lifetime Color instead of the source's original alpha?
/// </summary>
[HideInInspector] public bool sourceUsesLifetimeAlpha;
/// <summary>
/// The method to color particles with. Either evaluated by their lifetime or by their position in the ParticleCache array.
/// </summary>
[HideInInspector] public COLORMETHOD colorMethod;
/// <summary>
/// Determines if particles should get a secondary alpha applied from the arrayColorAlpha gradient when using colorMethod of COLORMETHOD.ParticleArray.
/// </summary>
[HideInInspector] public bool arrayColorUsesAlpha;
/// <summary>
/// The array color alpha.
/// </summary>
[HideInInspector] public Gradient arrayColorAlpha;
/// <summary>
/// Should the movement of the particle system transform when in local simulation space be compensated for?
/// </summary>
[HideInInspector] public bool applyLocalSpaceMovementCompensation = true;
/// <summary>
/// Should particles get a new random size upon rebirth?
/// </summary>
[HideInInspector] public bool applyRandomSizeOnRebirth = true;
/// <summary>
/// Should particles get a new random velocity upon rebirth?
/// </summary>
[HideInInspector] public bool applyRandomInitialVelocityOnRebirth = true;
/// <summary>
/// Should particles get a new random rotation upon rebirth?
/// </summary>
[HideInInspector] public bool applyRandomRotationOnRebirth = true;
/// <summary>
/// Should particles get a new scatter position upon rebirth?
/// </summary>
[HideInInspector] public bool applyRandomScatterOnRebirth = false;
/// <summary>
/// Should particles get their initial calculated color upon rebirth? (Can resolve flickering upon rebirth.)
/// </summary>
[HideInInspector] public bool applyInitialColorOnRebirth = false;
/// <summary>
/// Should particles get a new random lifetime upon rebirth?
/// </summary>
[HideInInspector] public bool applyRandomLifetimeOnRebirth = true;
/// <summary>
/// Should particle birth position be adjusted with the delta time from birth to current? This will make the particles appear linearly.
/// </summary>
[HideInInspector] public bool applyDeltaOnRebirth = true;
/// <summary>
/// Should each birthing particle calculate the local manipulators immediately?
/// </summary>
[HideInInspector] public bool calculateManipulatorOnRebirth = false;
/// <summary>
/// Should the particle system pause calculation upon becoming invisible?
/// </summary>
[HideInInspector] public bool pauseCalculationWhenInvisible = false;
/// <summary>
/// Should the calculation trigger size gizmo be visible in Scene View?
/// </summary>
[HideInInspector] public bool calculationTriggerSizeGizmo = false;
/// <summary>
/// The calculation trigger transform determines the origin of the rendered particle area. This will affect the calculation trigger when pauseCalculationWhenInvisible is set to true.
/// </summary>
[HideInInspector] public Transform calculationTriggerTransform;
/// <summary>
/// The calculation trigger size determines the size of the rendered particle area. This will affect the calculation trigger when pauseCalculationWhenInvisible is set to true.
/// </summary>
[HideInInspector] public Vector3 calculationTriggerSize = new Vector3(1f,1f,1f);
/// <summary>
/// The calculation trigger offset determines the offset from the calculationTriggerTransform position. This will affect the calculation trigger when pauseCalculationWhenInvisible is set to true.
/// </summary>
[HideInInspector] public Vector3 calculationTriggerOffset = new Vector3();
/// <summary>
/// Should the particle system force Play() when GameObject is outside of camera view? (Fix for Shuriken stop rendering.)
/// </summary>
[HideInInspector] public bool forceVisibilityWhenOutOfFrustrum = true;
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public bool syncPositionsOnMainThread = false;
/// <summary>
/// Should the particle system force itself to remain in lockPosition?
/// </summary>
[HideInInspector] public bool applyLockPosition = false;
/// <summary>
/// Should the particle system force itself to remain in lockRotation?
/// </summary>
[HideInInspector] public bool applyLockRotation = false;
/// <summary>
/// Should the particle system force itself to remain in lockScale?
/// </summary>
[HideInInspector] public bool applyLockScale = false;
/// <summary>
/// The locked position is considered local.
/// </summary>
[HideInInspector] public bool lockPositionIsLocal = false;
/// <summary>
/// The locked rotation is considered local.
/// </summary>
[HideInInspector] public bool lockRotationIsLocal = false;
/// <summary>
/// The locked position.
/// </summary>
[HideInInspector] public Vector3 lockPosition = Vector3.zero;
/// <summary>
/// The locked rotation.
/// </summary>
[HideInInspector] public Vector3 lockRotation = Vector3.zero;
/// <summary>
/// The locked scale.
/// </summary>
[HideInInspector] public Vector3 lockScale = new Vector3(1f,1f,1f);
/// <summary>
/// Should the movementCompensationLifetimeStrength affect local space movement compensation?
/// </summary>
[HideInInspector] public bool applyMovementCompensationLifetimeStrength = false;
/// <summary>
/// The strength of movement compensation over particles lifetime
/// </summary>
[HideInInspector] public AnimationCurve movementCompensationLifetimeStrength;
/// <summary>
/// Determines if masking should be applied to the particles.
/// </summary>
[HideInInspector] public bool applyParticleMask;
/// <summary>
/// The masked amount of particles. The particleMaskTime will determine if the particles should fade in/out.
/// </summary>
[HideInInspector] public int particleMask = 0;
/// <summary>
/// The time it takes to mask in/out particles when using particleMask.
/// </summary>
[HideInInspector] public float particleMaskTime = 0f;
/// <summary>
/// The method to distribute the sorting mask when particleMask is above 0.
/// </summary>
[HideInInspector] public MASKSORTINGC particleMaskSorting;
/// <summary>
/// The speed of stretching to reach full effect.
/// </summary>
[HideInInspector] public float stretchSpeed = 1f;
/// <summary>
/// Should the start direction of particle stretching be applied?
/// </summary>
[HideInInspector] public bool applyStretchStartDirection = false;
/// <summary>
/// The starting direction of stretching if all initial velocity is zero.
/// </summary>
[HideInInspector] public Vector3 stretchStartDirection = Vector3.zero;
/// <summary>
/// Should lifetime stretching be applied?
/// </summary>
[HideInInspector] public bool applyLifetimeStretching = false;
/// <summary>
/// The lifetime stretching of stretched particles.
/// </summary>
[HideInInspector] public AnimationCurve stretchLifetime;
/// <summary>
/// The multithreading method how this particle system should calculate. Use this to bypass the Playground Manager's threadMethod.
/// </summary>
[HideInInspector] public ThreadMethodLocal threadMethod;
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public bool multithreadedStartup = true;
// Source Script variables
/// <summary>
/// When using Emit() the index will point to the next particle in pool to emit.
/// </summary>
[HideInInspector] public int scriptedEmissionIndex;
/// <summary>
/// When using Emit() the passed in position will determine the position for this particle.
/// </summary>
[HideInInspector] public Vector3 scriptedEmissionPosition;
/// <summary>
/// When using Emit() the passed in velocity will determine the speed and direction for this particle.
/// </summary>
[HideInInspector] public Vector3 scriptedEmissionVelocity;
/// <summary>
/// When using Emit() the passed in color will decide the color for this particle if colorSource is set to COLORSOURCEC.Source.
/// </summary>
[HideInInspector] public Color scriptedEmissionColor = Color.white;
/// <summary>
/// When using Emit() the passed in scriptedLifetime will determine the lifetime of the particle.
/// </summary>
[HideInInspector] float scriptedLifetime = 0;
// Collision detection
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public bool collision = false;
/// <summary>
/// Should particles affect rigidbodies? The mass determines how much they will affect the rigidobdy.
/// </summary>
[HideInInspector] public bool affectRigidbodies = true;
[HideInInspector] public bool inverseRigidbodyCollision = false;
/// <summary>
/// The mass of a particle (calculated in collision with rigidbodies).
/// </summary>
[HideInInspector] public float mass = .01f;
/// <summary>
/// The spherical radius of a particle used upon collision.
/// </summary>
[HideInInspector] public float collisionRadius = 1f;
/// <summary>
/// The layers these particles will collide with.
/// </summary>
[HideInInspector] public LayerMask collisionMask;
[HideInInspector] public List<Transform> collisionExclusion;
/// <summary>
/// The amount a particle will loose of its lifetime on collision.
/// </summary>
[HideInInspector] public float lifetimeLoss = 0f;
/// <summary>
/// The amount a particle will bounce on collision.
/// </summary>
[HideInInspector] public float bounciness = .5f;
/// <summary>
/// The minimum amount of random bounciness (seen as negative offset from the collided surface's normal direction).
/// </summary>
[HideInInspector] public Vector3 bounceRandomMin;
/// <summary>
/// The maximum amount of random bounciness (seen as positive offset from the collided surface's normal direction).
/// </summary>
[HideInInspector] public Vector3 bounceRandomMax;
/// <summary>
/// The Playground Colliders of this particle system. A Playground Collider is an infinite collision plane based on a Transform in the scene.
/// </summary>
[HideInInspector] public List<PlaygroundColliderC> colliders;
/// <summary>
/// The type of collision. This determines if 2D- or 3D raycasting should be used.
/// </summary>
[HideInInspector] public COLLISIONTYPEC collisionType;
/// <summary>
/// Minimum collision depth of Raycast2D.
/// </summary>
[HideInInspector] public float minCollisionDepth = 0f;
/// <summary>
/// Maximum collision depth of Raycast2D.
/// </summary>
[HideInInspector] public float maxCollisionDepth = 0f;
/// <summary>
/// Determines if particles should stick to their collided surface.
/// </summary>
[HideInInspector] public bool stickyCollisions = false;
/// <summary>
/// The sticky collisions offset from the collided surface normal.
/// </summary>
[HideInInspector] public float stickyCollisionsSurfaceOffset = 0;
[HideInInspector] public LayerMask stickyCollisionsMask = -1;
/// <summary>
/// Determines if collisions should be calculated using Vector3.Distance (if set to true) or Vector3.SqrMagnitude (if set to false).
/// </summary>
[HideInInspector] public bool collisionPrecision = false;
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public bool forceCollisionCaching = false;
[HideInInspector] public bool maskedParticlesBypassCollision = true;
// States (source)
/// <summary>
/// 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.
/// </summary>
public List<ParticleStateC> states = new List<ParticleStateC>();
// Splines
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public List<PlaygroundSpline> splines;
[HideInInspector] public float splineTimeOffset;
[HideInInspector] public bool treatAsOneSpline;
int splineIndex = 0;
// Scene objects (source)
/// <summary>
/// A mesh as source calculated within the scene.
/// </summary>
[HideInInspector] public WorldObject worldObject = new WorldObject();
/// <summary>
/// A skinned mesh as source calculated within the scene.
/// </summary>
[HideInInspector] public SkinnedWorldObject skinnedWorldObject = new SkinnedWorldObject();
[HideInInspector] public bool forceSkinnedMeshUpdateOnMainThread = false;
/// <summary>
/// A transform calculated within the scene.
/// </summary>
[HideInInspector] public Transform sourceTransform;
[HideInInspector] public List<PlaygroundTransformC> sourceTransforms;
[HideInInspector] public bool treatAsOneTransform;
int transformIndex = 0;
[HideInInspector] public PlaygroundParticlesC otherParticleSource;
[HideInInspector] public SOURCEBIRTHMETHOD otherParticleSourceMethod;
// Paint
/// <summary>
/// The paint source of this PlaygroundParticles.
/// </summary>
[HideInInspector] public PaintObjectC paint;
// Projection
/// <summary>
/// The projection source of this PlaygroundParticles.
/// </summary>
[HideInInspector] public ParticleProjectionC projection;
// Manipulators
/// <summary>
/// The list of Local Manipulator Objects handled by this PlaygroundParticlesC object.
/// </summary>
public List<ManipulatorObjectC> manipulators;
// Events
/// <summary>
/// List of event objects handled by this PlaygroundParticlesC object.
/// </summary>
[HideInInspector] public List<PlaygroundEventC> events;
// Cache
/// <summary>
/// Data for each particle.
/// </summary>
[NonSerialized] public PlaygroundCache playgroundCache = new PlaygroundCache();
/// <summary>
/// The particle pool.
/// </summary>
[NonSerialized] public ParticleSystem.Particle[] particleCache;
[NonSerialized] public CollisionCache collisionCache;
// Snapshots
/// <summary>
/// Saved data of properties (positions, velocities, colors etc.).
/// </summary>
[HideInInspector] public List<PlaygroundSave> snapshots = new List<PlaygroundSave>();
/// <summary>
/// Should the particle system load stored data from start?
/// </summary>
[HideInInspector] public bool loadFromStart = false;
/// <summary>
/// Which data should be loaded (if loadFromStart is true).
/// </summary>
[HideInInspector] public int loadFrom = 0;
/// <summary>
/// Should a transition occur whenever a Load is issued?
/// </summary>
[HideInInspector] public bool loadTransition = false;
/// <summary>
/// The type of transition to occur whenever a Load is issued.
/// </summary>
[HideInInspector] public TRANSITIONTYPEC loadTransitionType;
/// <summary>
/// The time for load transition in seconds.
/// </summary>
[HideInInspector] public float loadTransitionTime = 1f;
/// <summary>
/// The storage of position data if this is a snapshot.
/// </summary>
[HideInInspector] public PlaygroundCache snapshotData;
/// <summary>
/// The global time the snapshot was made.
/// </summary>
[HideInInspector] public float timeOfSnapshot = 0;
/// <summary>
/// Is this particle system a snapshot?
/// </summary>
public bool isSnapshot = false;
// Components
/// <summary>
/// This ParticleSystem (Shuriken) component.
/// </summary>
[HideInInspector] public ParticleSystem shurikenParticleSystem;
/// <summary>
/// The id of this PlaygroundParticlesC object.
/// </summary>
[HideInInspector] public int particleSystemId;
/// <summary>
/// The GameObject of a PlaygroundParticlesC object.
/// </summary>
[HideInInspector] public GameObject particleSystemGameObject;
/// <summary>
/// The Transform of a PlaygroundParticlesC object.
/// </summary>
[HideInInspector] public Transform particleSystemTransform;
/// <summary>
/// The Renderer of a PlaygroundParticlesC object.
/// </summary>
[HideInInspector] public Renderer particleSystemRenderer;
/// <summary>
/// The ParticleSystemRenderer of a PlaygroundParticlesC object.
/// </summary>
[HideInInspector] public ParticleSystemRenderer particleSystemRenderer2;
/// <summary>
/// The PlaygroundParticlesC that is controlling this particle system.
/// </summary>
[HideInInspector] public List<PlaygroundParticlesC> eventControlledBy = new List<PlaygroundParticlesC>();
// Turbulence
/// <summary>
/// The Simplex Turbulence object.
/// </summary>
SimplexNoise turbulenceSimplex;
/// <summary>
/// The type of turbulence.
/// </summary>
[HideInInspector] public TURBULENCETYPE turbulenceType = TURBULENCETYPE.None;
/// <summary>
/// The turbulence strength.
/// </summary>
[HideInInspector] public float turbulenceStrength = 10f;
/// <summary>
/// The turbulence resolution scale. A higher value will generate a more dense grid.
/// </summary>
[HideInInspector] public float turbulenceScale = 1f;
/// <summary>
/// The turbulence time scale.
/// </summary>
[HideInInspector] public float turbulenceTimeScale = 1f;
/// <summary>
/// Should Turbulence Lifetime Strength apply?
/// </summary>
[HideInInspector] public bool turbulenceApplyLifetimeStrength = false;
/// <summary>
/// The Turbulence Lifetime Strength. Use this to control how much turbulence will affect the particle over its lifetime.
/// </summary>
[HideInInspector] public AnimationCurve turbulenceLifetimeStrength;
[HideInInspector] public bool prewarm = false;
[HideInInspector] public float prewarmTime = 1f;
[HideInInspector] public int prewarmCycles = 16;
/// <summary>
/// The simulation time scale for all particles within this system. Set time scale to 0 to pause a particle system.
/// </summary>
[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
*************************************************************************************************************************************************/
/// <summary>
/// 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.
/// </summary>
/// <param name="playgroundParticles">Playground particles.</param>
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<Transform>();
for (int i = 0; i<collisionExclusion.Count; i++)
playgroundParticles.collisionExclusion.Add(collisionExclusion[i]);
playgroundParticles.colliders = new List<PlaygroundColliderC>();
for (int i = 0; i<colliders.Count; i++)
playgroundParticles.colliders.Add(colliders[i].Clone());
// States (source)
playgroundParticles.states = new List<ParticleStateC>();
for (int i = 0; i<states.Count; i++) {
playgroundParticles.states.Add(states[i].Clone());
}
// Spline
playgroundParticles.splines = new List<PlaygroundSpline>();
for (int i = 0; i<splines.Count; i++)
playgroundParticles.splines.Add(splines[i]);
playgroundParticles.splineTimeOffset = splineTimeOffset;
playgroundParticles.treatAsOneSpline = treatAsOneSpline;
// Scene objects (source)
playgroundParticles.worldObject = worldObject.Clone();
playgroundParticles.skinnedWorldObject = skinnedWorldObject.Clone();
playgroundParticles.forceSkinnedMeshUpdateOnMainThread = forceSkinnedMeshUpdateOnMainThread;
playgroundParticles.sourceTransform = sourceTransform;
playgroundParticles.sourceTransforms = new List<PlaygroundTransformC>();
for (int i = 0; i<sourceTransforms.Count; i++)
playgroundParticles.sourceTransforms.Add(sourceTransforms[i].Clone());
playgroundParticles.treatAsOneTransform = treatAsOneTransform;
// Paint
playgroundParticles.paint = paint.Clone();
// Projection
playgroundParticles.projection = projection.Clone();
// Manipulators
playgroundParticles.manipulators = new List<ManipulatorObjectC>();
for (int i = 0; i<manipulators.Count; i++)
playgroundParticles.manipulators.Add(manipulators[i].Clone());
// Events
playgroundParticles.events = new List<PlaygroundEventC>();
for (int i = 0; i<events.Count; i++)
playgroundParticles.events.Add(events[i].Clone());
// Lifetime Colors
playgroundParticles.lifetimeColors = new List<PlaygroundGradientC>();
for (int i = 0; i<lifetimeColors.Count; i++) {
playgroundParticles.lifetimeColors.Add(new PlaygroundGradientC());
lifetimeColors[i].CopyTo(playgroundParticles.lifetimeColors[i]);
}
// Turbulence
playgroundParticles.turbulenceType = turbulenceType;
playgroundParticles.turbulenceApplyLifetimeStrength = turbulenceApplyLifetimeStrength;
playgroundParticles.turbulenceLifetimeStrength = new AnimationCurve(turbulenceLifetimeStrength.keys);
playgroundParticles.turbulenceScale = turbulenceScale;
playgroundParticles.turbulenceStrength = turbulenceStrength;
playgroundParticles.turbulenceTimeScale = turbulenceTimeScale;
// Other
playgroundParticles.particleTimescale = particleTimescale;
}
/// <summary>
/// Copies stored data of Snapshots into a particle system (separated from CopyTo() to solve Save/Load overwrite paradox).
/// </summary>
/// <param name="playgroundParticles">Playground particles.</param>
public void CopySaveDataTo (PlaygroundParticlesC playgroundParticles) {
playgroundParticles.snapshots = new List<PlaygroundSave>();
for (int i = 0; i<snapshots.Count; i++)
playgroundParticles.snapshots.Add(snapshots[i].Clone());
}
/// <summary>
/// Sets emission On or Off.
/// </summary>
/// <param name="setEmission">If set to <c>true</c> then emit particles.</param>
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;
}
/// <summary>
/// Emits a single particle at previously set scripted emission position, velocity and color.
/// </summary>
public int Emit () {
source = SOURCEC.Script;
int returnIndex = scriptedEmissionIndex;
EmitProcedure(scriptedEmissionPosition, scriptedEmissionVelocity, scriptedEmissionColor);
return returnIndex;
}
/// <summary>
/// Emits a single particle at position with previously set scripted emission velocity and color.
/// </summary>
/// <param name="givePosition">Position.</param>
public int Emit (Vector3 givePosition) {
source = SOURCEC.Script;
int returnIndex = scriptedEmissionIndex;
EmitProcedure(givePosition, scriptedEmissionVelocity, scriptedEmissionColor);
return returnIndex;
}
/// <summary>
/// Emits a single particle at position with velocity, the color will be set from the previous scripted emission color.
/// </summary>
/// <param name="givePosition">Position.</param>
/// <param name="giveVelocity">Velocity.</param>
public int Emit (Vector3 givePosition, Vector3 giveVelocity) {
source = SOURCEC.Script;
int returnIndex = scriptedEmissionIndex;
EmitProcedure(givePosition, giveVelocity, scriptedEmissionColor);
return returnIndex;
}
/// <summary>
/// Emits a single particle at position with velocity and color (Source Mode SOURCEC.Script will be automatically set).
/// </summary>
/// <param name="givePosition">Position.</param>
/// <param name="giveVelocity">Velocity.</param>
/// <param name="giveColor">Color.</param>
public int Emit (Vector3 givePosition, Vector3 giveVelocity, Color32 giveColor) {
source = SOURCEC.Script;
int returnIndex = scriptedEmissionIndex;
EmitProcedure(givePosition, giveVelocity, giveColor);
return returnIndex;
}
/// <summary>
/// Emits number of particles set by quantity. All other values will be set from the previous scripted emission call (or as set in Inspector).
/// </summary>
/// <param name="quantity">Quantity.</param>
public void Emit (int quantity) {
source = SOURCEC.Script;
for (int i = 0; i<quantity; i++)
EmitProcedure(
scriptedEmissionPosition,
scriptedEmissionVelocity,
scriptedEmissionColor
);
}
/// <summary>
/// Emits number of particles set by quantity, position and minimum - maximum random velocity.
/// </summary>
/// <param name="quantity">Quantity.</param>
/// <param name="givePosition">Position.</param>
/// <param name="randomVelocityMin">Random minimum velocity.</param>
/// <param name="randomVelocityMax">Random maximum velocity.</param>
/// <param name="giveColor">Color.</param>
public void Emit (int quantity, Vector3 givePosition, Vector3 randomVelocityMin, Vector3 randomVelocityMax, Color32 giveColor) {
source = SOURCEC.Script;
for (int i = 0; i<quantity; i++)
EmitProcedure(
givePosition,
applyInitialVelocityShape?
Vector3.Scale (RandomRange(internalRandom01, randomVelocityMin, randomVelocityMax), initialVelocityShape.Evaluate((i*1f)/(quantity*1f), initialVelocityShapeScale))
:
RandomRange(internalRandom01, randomVelocityMin, randomVelocityMax),
giveColor
);
}
/// <summary>
/// Emits number of particles set by quantity, minimum - maximum random position and velocity.
/// </summary>
/// <param name="quantity">Quantity.</param>
/// <param name="randomPositionMin">Random position minimum.</param>
/// <param name="randomPositionMax">Random position max.</param>
/// <param name="randomVelocityMin">Random velocity minimum.</param>
/// <param name="randomVelocityMax">Random velocity max.</param>
/// <param name="giveColor">Color.</param>
public void Emit (int quantity, Vector3 randomPositionMin, Vector3 randomPositionMax, Vector3 randomVelocityMin, Vector3 randomVelocityMax, Color32 giveColor) {
source = SOURCEC.Script;
for (int i = 0; i<quantity; i++)
EmitProcedure(
RandomRange(internalRandom01, randomPositionMin, randomPositionMax),
applyInitialVelocityShape?
Vector3.Scale (RandomRange(internalRandom01, randomVelocityMin, randomVelocityMax), initialVelocityShape.Evaluate((i*1f)/(quantity*1f), initialVelocityShapeScale))
:
RandomRange(internalRandom01, randomVelocityMin, randomVelocityMax),
giveColor
);
}
/// <summary>
/// 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).
/// </summary>
/// <param name="giveLifetime">Lifetime.</param>
public int Emit (float giveLifetime) {
source = SOURCEC.Script;
int returnIndex = scriptedEmissionIndex;
scriptedLifetime = giveLifetime;
EmitProcedure(scriptedEmissionPosition, scriptedEmissionVelocity, scriptedEmissionColor);
scriptedLifetime = 0;
return returnIndex;
}
/// <summary>
/// Emits a single particle with specified lifetime. This will set particle velocity and color from previously called emission (as set in the Source tab).
/// </summary>
/// <param name="givePosition">Position.</param>
/// <param name="giveLifetime">Lifetime.</param>
public int Emit (Vector3 givePosition, float giveLifetime) {
source = SOURCEC.Script;
int returnIndex = scriptedEmissionIndex;
scriptedLifetime = giveLifetime;
EmitProcedure(givePosition, scriptedEmissionVelocity, scriptedEmissionColor);
scriptedLifetime = 0;
return returnIndex;
}
/// <summary>
/// Emits a single particle with specified lifetime. This will set particle color from previously called emission (as set in the Source tab).
/// </summary>
/// <param name="givePosition">Position.</param>
/// <param name="giveVelocity">Velocity.</param>
/// <param name="giveLifetime">Lifetime.</param>
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;
}
/// <summary>
/// Emits a single particle with specified position, velocity, lifetime and color.
/// </summary>
/// <param name="givePosition">Position.</param>
/// <param name="giveVelocity">Velocity.</param>
/// <param name="giveLifetime">Lifetime.</param>
/// <param name="giveColor">Color.</param>
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;
}
/// <summary>
/// Thread-safe version of Emit().
/// </summary>
/// <param name="givePosition">Position.</param>
/// <param name="giveVelocity">Velocity.</param>
/// <param name="giveColor">Color.</param>
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<quantity; i++)
EmitProcedure(
givePosition,
applyInitialVelocityShape?
Vector3.Scale (RandomRange(internalRandom01, randomVelocityMin, randomVelocityMax), initialVelocityShape.Evaluate((i*1f)/(quantity*1f), initialVelocityShapeScale))
:
RandomRange(internalRandom01, randomVelocityMin, randomVelocityMax),
giveColor
);
}
/// <summary>
/// Internal emission procedure called upon Emit().
/// </summary>
/// <param name="givePosition">Give position.</param>
/// <param name="giveVelocity">Give velocity.</param>
/// <param name="giveColor">Give color.</param>
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;
}
/// <summary>
/// Checks if particles are still in simulation.
/// </summary>
/// <returns><c>true</c> if this particle system is alive; otherwise, <c>false</c>.</returns>
public bool IsAlive () {
return calculate && hasActiveParticles;
}
/// <summary>
/// Determines whether this particle system is simulated in local space.
/// </summary>
/// <returns><c>true</c> if this particle system is simulated in local space; otherwise, <c>false</c>.</returns>
public bool IsLocalSpace () {
return localSpace;
}
/// <summary>
/// Determines if this particle system is in a transition.
/// </summary>
/// <returns><c>true</c>, if transition is active, <c>false</c> otherwise.</returns>
public bool InTransition () {
return inTransition;
}
/// <summary>
/// Determines whether this particle system is loading a snapshot.
/// </summary>
/// <returns><c>true</c> if this particle system is loading a snapshot; otherwise, <c>false</c>.</returns>
public bool IsLoading () {
return isLoading;
}
/// <summary>
/// Determines whether this particle system is saving a snapshot.
/// </summary>
/// <returns><c>true</c> if this particle system is saving a snapshot; otherwise, <c>false</c>.</returns>
public bool IsSaving () {
return isSaving;
}
/// <summary>
/// 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().
/// </summary>
/// <returns><c>true</c> if the particle system is ready; otherwise, <c>false</c>.</returns>
public bool IsReady () {
return initialized&&!isPrewarming;
}
/// <summary>
/// Check if the particle system is ready. You should wait for Initialized() to become true before calling any scripted emission.
/// </summary>
/// <returns><c>true</c> if the particle system is ready; otherwise, <c>false</c>.</returns>
public bool Initialized () {
return initialized&&!isPrewarming;
}
/// <summary>
/// Determines whether this particle system is yield refreshing.
/// </summary>
/// <returns><c>true</c> if this particle system is yield refreshing; otherwise, <c>false</c>.</returns>
public bool IsYieldRefreshing () {
return isYieldRefreshing;
}
public bool IsSettingParticleTime () {
return isSettingParticleTime;
}
public bool IsDoneThread {
get {return isDoneThread;}
set {isDoneThread = value;}
}
/// <summary>
/// Determines whether the skinned world object is ready.
/// </summary>
/// <returns><c>true</c> if this skinned world object is ready; otherwise, <c>false</c>.</returns>
public bool IsSkinnedWorldObjectReady () {
return source==SOURCEC.SkinnedWorldObject&&skinnedWorldObjectReady;
}
/// <summary>
/// Determines whether this particle system has turbulence active.
/// </summary>
/// <returns><c>true</c> if this particle system has turbulence; otherwise, <c>false</c>.</returns>
public bool HasTurbulence () {
return calculate && !onlySourcePositioning && !onlyLifetimePositioning && turbulenceStrength>0 && turbulenceType!=TURBULENCETYPE.None;
}
/// <summary>
/// Determines whether this particle system has overflow set by Overflow Offset.
/// </summary>
/// <returns><c>true</c> if this particle system has overflow; otherwise, <c>false</c>.</returns>
public bool HasOverflow () {
return overflow;
}
/// <summary>
/// Determines whether this particle system may be affected by one or more available Global Manipulators.
/// </summary>
/// <returns><c>true</c> if this particle system will be affected by one or more Global Manipulators; otherwise, <c>false</c>.</returns>
public bool HasGlobalManipulator () {
return hasGlobalAffectingManipulators;
}
/// <summary>
/// Determines whether this particle system has an initialized collision cache.
/// </summary>
/// <returns><c>true</c> if this particle system has a collision cache; otherwise, <c>false</c>.</returns>
public bool HasCollisionCache () {
return hasCollisionCache;
}
/// <summary>
/// Determines if a particle at specified index has collided during its lifetime.
/// </summary>
/// <returns><c>true</c> if the particle at index has collided during its lifetime; otherwise, <c>false</c>.</returns>
/// <param name="index">Index.</param>
public bool HasCollided (int index) {
if (index<0||index>particleCount-1)
return false;
if (hasCollisionCache)
return collisionCache.hasCollided[index];
return false;
}
/// <summary>
/// Gets the particle collision position at index.
/// </summary>
/// <returns>The particle collision position.</returns>
/// <param name="index">Index.</param>
public Vector3 GetCollisionPosition (int index) {
if (!hasCollisionCache || index<0||index>particleCount-1)
return Vector3.zero;
return collisionCache.collisionPosition[index];
}
/// <summary>
/// Gets the particle collision normal at index.
/// </summary>
/// <returns>The particle collision normal.</returns>
/// <param name="index">Index.</param>
public Vector3 GetCollisionNormal (int index) {
if (!hasCollisionCache || index<0||index>particleCount-1)
return Vector3.zero;
return collisionCache.collisionNormal[index];
}
/// <summary>
/// Gets the particle sticky position at index. Note that stickyCollisions must be enabled to return expected values.
/// </summary>
/// <returns>The particle sticky position.</returns>
/// <param name="index">Index.</param>
public Vector3 GetStickyPosition (int index) {
if (!hasCollisionCache || index<0||index>particleCount-1)
return Vector3.zero;
return collisionCache.stickyPosition[index];
}
/// <summary>
/// Sets the particle at index to stick onto a parent transform.
/// </summary>
/// <param name="index">Index.</param>
/// <param name="position">Position.</param>
/// <param name="normal">Normal.</param>
/// <param name="parent">Parent.</param>
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);
}
/// <summary>
/// Updates the sticky particle position.
/// </summary>
/// <param name="index">Index.</param>
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];
}
/// <summary>
/// Clears the collisions. Use this if you for example want to toggle between sticky and non-sticky particle behaviors.
/// </summary>
public void ClearCollisions () {
if (!hasCollisionCache)
return;
collisionCache.ClearCollisions();
}
/// <summary>
/// Clears the collisions at index. Use this if you for example want to toggle between sticky and non-sticky particle behaviors.
/// </summary>
public void ClearCollisions (int index) {
if (!hasCollisionCache || index<0||index>particleCount-1)
return;
collisionCache.Reset(index);
}
/// <summary>
/// Gets the particle collision transform at index.
/// </summary>
/// <returns>The particle collision transform.</returns>
/// <param name="index">Index.</param>
public Transform GetCollisionTransform (int index) {
if (!hasCollisionCache || index<0||index>particleCount-1)
return null;
return collisionCache.collisionTransform[index];
}
/// <summary>
/// Gets the layer this particle system is within. This is safe to call from another thread.
/// </summary>
/// <returns>The layer.</returns>
public int GetLayer () {
return thisLayer;
}
/// <summary>
/// Sets the random seed for the internal System.Random.
/// </summary>
/// <param name="seed">Seed.</param>
public void SetRandomSeed (int seed) {
internalRandom01 = new System.Random(seed);
}
int reportUpdateStepper = 100;
bool isReportingBadUpdateRate = false;
/// <summary>
/// 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.
/// </summary>
/// <returns><c>true</c> if this particle system is reporting bad update rate; otherwise, <c>false</c>.</returns>
public bool IsReportingBadUpdateRate () {
if (isPrewarming||inTransition) return false;
reportUpdateStepper--;
if (reportUpdateStepper==0) {
reportUpdateStepper = 100;
isReportingBadUpdateRate = localTime>1f && localDeltaTime>.04f;
}
return isReportingBadUpdateRate;
}
/// <summary>
/// Gets the current delta time of a particle system's update loop.
/// </summary>
/// <returns>The delta time.</returns>
public float GetDeltaTime () {
return t;
}
/// <summary>
/// Gets or sets when this particle system was last time updated. This is done automatically while the particle system is simulated.
/// </summary>
/// <value>The last time updated.</value>
public float LastTimeUpdated {
get {return lastTimeUpdated;}
set {lastTimeUpdated = value;}
}
/// <summary>
/// Gets or sets the local delta time of this particle system. This is done automatically while the particle system is simulated.
/// </summary>
/// <value>The local delta time.</value>
public float LocalDeltaTime {
get {return localDeltaTime;}
set {
localDeltaTime = value;
t = value;
}
}
/// <summary>
/// Gets the running simplex algorithm.
/// </summary>
/// <returns>The running simplex algorithm.</returns>
public SimplexNoise GetSimplex () {
if (turbulenceSimplex==null)
turbulenceSimplex = new SimplexNoise();
return turbulenceSimplex;
}
/// <summary>
/// Kill the specified particle.
/// </summary>
/// <param name="p">Index of particle.</param>
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;
}
/// <summary>
/// Kill the specified particle and send death events to all Manipulators tracking particles.
/// </summary>
/// <param name="p">Index of particle.</param>
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<manipulators.Count; m++) {
if (manipulators[m].trackParticles &&
manipulators[m].ContainsParticle(particleSystemId, p)) {
playgroundCache.manipulatorId[p] = 0;
manipulators[m].RemoveParticle(particleSystemId, p);
if (manipulators[m].sendEventExit) {
UpdateEventParticle(manipulators[m].manipulatorEventParticle, p);
manipulators[m].SendParticleEventDeath();
}
}
}
// Check if particle has a connection to a global manipulator to send death event
if (playgroundCache.manipulatorId[p]!=0) {
for (int m = 0; m<PlaygroundC.reference.manipulators.Count; m++) {
if (PlaygroundC.reference.manipulators[m].trackParticles &&
PlaygroundC.reference.manipulators[m].ContainsParticle(particleSystemId, p)) {
PlaygroundC.reference.manipulators[m].RemoveParticle(particleSystemId, p);
if (PlaygroundC.reference.manipulators[m].sendEventExit) {
UpdateEventParticle(PlaygroundC.reference.manipulators[m].manipulatorEventParticle, p);
PlaygroundC.reference.manipulators[m].SendParticleEventDeath();
}
}
}
}
playgroundCache.manipulatorId[p] = 0;
}
}
/// <summary>
/// Sets a particle to no longer respond to forces.
/// </summary>
/// <param name="p">Index of particle.</param>
/// <param name="noForce">If set to <c>true</c> then disable force for the particle at index.</param>
public void SetNoForce (int p, bool noForce) {
playgroundCache.noForce[p] = noForce;
}
/// <summary>
/// Determines if the particle have noForce set to true
/// </summary>
/// <returns><c>true</c>, if force is disabled, <c>false</c> otherwise.</returns>
/// <param name="p">Index of particle.</param>
public bool NoForce (int p) {
return playgroundCache.noForce[p];
}
/// <summary>
/// Translate the specified particle
/// </summary>
/// <param name="p">Particle index.</param>
/// <param name="translation">Translation.</param>
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;
}
/// <summary>
/// Positions the specified particle.
/// </summary>
/// <param name="p">Particle index.</param>
/// <param name="position">Position.</param>
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;
}
/// <summary>
/// Sets the color of the specified particle.
/// </summary>
/// <param name="p">Particle index.</param>
/// <param name="color">Color.</param>
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
}
/// <summary>
/// Sets the size of the specified particle.
/// </summary>
/// <param name="p">Particle index.</param>
/// <param name="size">Size.</param>
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
}
/// <summary>
/// Sets the velocity of the specified particle.
/// </summary>
/// <param name="p">Particle index.</param>
/// <param name="size">Velocity.</param>
public void ParticleVelocity (int p, Vector3 velocity) {
p = Mathf.Clamp (p, 0, playgroundCache.velocity.Length);
playgroundCache.velocity[p] = velocity;
}
/// <summary>
/// Positions to transform point.
/// </summary>
/// <param name="p">Particle index.</param>
/// <param name="position">Position.</param>
/// <param name="targetTransform">Target transform.</param>
public void PositionToTransformPoint (int p, Vector3 position, Transform targetTransform) {
playgroundCache.position[p] = targetTransform.TransformPoint (position);
}
/// <summary>
/// Positions to inverse transform point.
/// </summary>
/// <param name="p">Particle index.</param>
/// <param name="position">Position.</param>
/// <param name="targetTransform">Target transform.</param>
public void PositionToInverseTransformPoint (int p, Vector3 position, Transform targetTransform) {
playgroundCache.position[p] = targetTransform.InverseTransformPoint (position);
}
/// <summary>
/// Gets the particle position.
/// </summary>
/// <returns>The particle position.</returns>
/// <param name="p">Particle index.</param>
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;
}
/// <summary>
/// Determines whether this particle system has several manipulator events.
/// </summary>
/// <returns><c>true</c> if this particle system has several manipulator events; otherwise, <c>false</c>.</returns>
public bool HasSeveralManipulatorEvents () {
return hasSeveralManipulatorEvents;
}
/// <summary>
/// Protects the particle from specified manipulator.
/// </summary>
/// <param name="particle">Particle index.</param>
/// <param name="manipulator">Manipulator.</param>
public void ProtectParticleFromManipulator (int particle, ManipulatorObjectC manipulator) {
if (manipulator.transform.Update())
playgroundCache.excludeFromManipulatorId[particle] = manipulator.transform.instanceID;
}
/// <summary>
/// Removes the particle protection.
/// </summary>
/// <param name="particle">Particle index.</param>
public void RemoveParticleProtection (int particle) {
playgroundCache.excludeFromManipulatorId[particle] = 0;
}
/// <summary>
/// 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.
/// </summary>
/// <returns><c>true</c> if this particle system is setting the lifetime; otherwise, <c>false</c>.</returns>
public bool IsSettingLifetime () {
return isSettingLifetime;
}
/// <summary>
/// Determines whether this particle system is currently prewarming.
/// </summary>
/// <returns><c>true</c> if this particle system is prewarming; otherwise, <c>false</c>.</returns>
public bool IsPrewarming () {
return isPrewarming;
}
/// <summary>
/// Determines whether a particle is inside the area of a Manipulator.
/// </summary>
/// <returns><c>true</c> if this particle is inside manipulator; otherwise, <c>false</c>.</returns>
public bool IsParticleInsideManipulator (int particleId, ManipulatorObjectC manipulator) {
return (manipulator.Contains (playgroundCache.position[particleId], (localSpace?manipulator.transform.localPosition:manipulator.transform.position)));
}
/// <summary>
/// Gets the index of the source spline which currently is read in simulation.
/// </summary>
/// <returns>The spline index.</returns>
public int GetSplineIndex () {
return splineIndex;
}
/// <summary>
/// Gets the index of the source transform which currently is read in simulation.
/// </summary>
/// <returns>The transform index.</returns>
public int GetTransformIndex () {
return transformIndex;
}
/// <summary>
/// Refreshes the mask sorting. If you're using a particleMaskSorting of MASKSORTINGC.Scrambled you can use this to randomize new mask positions.
/// </summary>
public void RefreshMaskSorting () {
playgroundCache.maskSorting = new int[particleCount];
switch (particleMaskSorting) {
case MASKSORTINGC.Linear:
for (int i = 0; i<playgroundCache.maskSorting.Length; i++)
playgroundCache.maskSorting[i] = i;
break;
case MASKSORTINGC.Reversed:
for (int i = 0; i<playgroundCache.maskSorting.Length; i++)
playgroundCache.maskSorting[i] = (playgroundCache.maskSorting.Length-1)-i;
break;
case MASKSORTINGC.Scrambled:
for (int i = 0; i<playgroundCache.maskSorting.Length; i++)
playgroundCache.maskSorting[i] = playgroundCache.maskSorting[i] = i;
PlaygroundC.ShuffleArray(playgroundCache.maskSorting);
break;
}
previousMaskSorting = particleMaskSorting;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="maskSortingArray">Mask sorting array.</param>
public void RefreshMaskSorting (int[] maskSortingArray) {
playgroundCache.maskSorting = new int[particleCount];
for (int i = 0; i<particleCount; i++) {
if (i<maskSortingArray.Length)
playgroundCache.maskSorting[i] = maskSortingArray[i];
else
playgroundCache.maskSorting[i] = i;
}
previousMaskSorting = particleMaskSorting;
}
/// <summary>
/// Creates a new PlaygroundParticlesC object.
/// </summary>
/// <returns>The playground particles.</returns>
/// <param name="images">Images.</param>
/// <param name="name">Name.</param>
/// <param name="position">Position.</param>
/// <param name="rotation">Rotation.</param>
/// <param name="offset">Offset.</param>
/// <param name="particleSize">Particle size.</param>
/// <param name="scale">Scale.</param>
/// <param name="material">Material.</param>
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<images.Length; i++)
quantityList[i] = images[i].width*images[i].height;
playgroundParticles.particleCache = new ParticleSystem.Particle[quantityList[PlaygroundC.Largest(quantityList)]];
OnCreatePlaygroundParticles(playgroundParticles);
for (i = 0; i<images.Length; i++) {
playgroundParticles.states.Add(new ParticleStateC());
playgroundParticles.states[playgroundParticles.states.Count-1].ConstructParticles(images[i],scale,offset,"State 0",null);
}
return playgroundParticles;
}
/// <summary>
/// Sets default settings for a PlaygroundParticlesC object.
/// </summary>
/// <param name="playgroundParticles">Playground particles.</param>
public static void OnCreatePlaygroundParticles (PlaygroundParticlesC playgroundParticles) {
playgroundParticles.playgroundCache = new PlaygroundCache();
playgroundParticles.paint = new PaintObjectC();
playgroundParticles.states = new List<ParticleStateC>();
playgroundParticles.projection = new ParticleProjectionC();
playgroundParticles.colliders = new List<PlaygroundColliderC>();
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<playgroundParticles.particleCache.Length; p++) {
playgroundParticles.playgroundCache.size[p] = playgroundParticles.playgroundCache.initialSize[p];
}
PlaygroundParticlesC.SetParticleCount(playgroundParticles, playgroundParticles.particleCount);
if (PlaygroundC.reference!=null) {
PlaygroundC.particlesQuantity++;
PlaygroundC.reference.particleSystems.Add(playgroundParticles);
playgroundParticles.particleSystemId = PlaygroundC.particlesQuantity-1;
}
}
/// <summary>
/// Creates a Shuriken Particle System.
/// </summary>
/// <returns>The particle object.</returns>
/// <param name="name">Name.</param>
/// <param name="position">Position.</param>
/// <param name="rotation">Rotation.</param>
/// <param name="particleSize">Particle size.</param>
/// <param name="material">Material.</param>
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<PlaygroundParticlesC>();
playgroundParticles.particleSystemGameObject = go;
playgroundParticles.particleSystemGameObject.name = name;
playgroundParticles.shurikenParticleSystem = playgroundParticles.particleSystemGameObject.GetComponent<ParticleSystem>();
playgroundParticles.particleSystemRenderer = playgroundParticles.shurikenParticleSystem.GetComponent<Renderer>();
playgroundParticles.particleSystemRenderer2 = playgroundParticles.shurikenParticleSystem.GetComponent<Renderer>() 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;
}
/// <summary>
/// Creates a new WorldObject.
/// </summary>
/// <returns>The world object.</returns>
/// <param name="meshTransform">Mesh transform.</param>
public static WorldObject NewWorldObject (Transform meshTransform) {
WorldObject worldObject = new WorldObject();
if (meshTransform.GetComponentInChildren<MeshFilter>()) {
worldObject.transform = meshTransform;
worldObject.Initialize ();
} else Debug.Log("Could not find a mesh in "+meshTransform.name+".");
return worldObject;
}
/// <summary>
/// Creates a new SkinnedWorldObject.
/// </summary>
/// <returns>The skinned world object.</returns>
/// <param name="meshTransform">Mesh transform.</param>
public static SkinnedWorldObject NewSkinnedWorldObject (Transform meshTransform) {
SkinnedWorldObject skinnedWorldObject = new SkinnedWorldObject();
if (meshTransform.GetComponentInChildren<SkinnedMeshRenderer>()) {
skinnedWorldObject.transform = meshTransform;
skinnedWorldObject.Initialize ();
} else Debug.Log("Could not find a skinned mesh in "+meshTransform.name+".");
return skinnedWorldObject;
}
/// <summary>
/// Creates a new SkinnedWorldObject with pre-set down resolution.
/// </summary>
/// <returns>The skinned world object.</returns>
/// <param name="meshTransform">Mesh transform.</param>
/// <param name="downResolution">Down resolution.</param>
public static SkinnedWorldObject NewSkinnedWorldObject (Transform meshTransform, int downResolution) {
SkinnedWorldObject skinnedWorldObject = NewSkinnedWorldObject(meshTransform);
skinnedWorldObject.downResolution = downResolution;
return skinnedWorldObject;
}
/// <summary>
/// Creates a new PaintObject.
/// </summary>
/// <returns>The paint object.</returns>
/// <param name="playgroundParticles">Playground particles.</param>
public static PaintObjectC NewPaintObject (PlaygroundParticlesC playgroundParticles) {
PaintObjectC paintObject = new PaintObjectC();
playgroundParticles.paint = paintObject;
playgroundParticles.paint.Initialize();
return paintObject;
}
/// <summary>
/// Creates a new ParticleProjection object.
/// </summary>
/// <returns>The projection object.</returns>
/// <param name="playgroundParticles">Playground particles.</param>
public static ParticleProjectionC NewProjectionObject (PlaygroundParticlesC playgroundParticles) {
ParticleProjectionC projectionObject = new ParticleProjectionC();
playgroundParticles.projection = projectionObject;
playgroundParticles.projection.Initialize();
return projectionObject;
}
/// <summary>
/// Creates a new ManipulatorObject and attach to the Playground Manager.
/// </summary>
/// <returns>The manipulator object.</returns>
/// <param name="type">Type.</param>
/// <param name="affects">Affects.</param>
/// <param name="manipulatorTransform">Manipulator transform.</param>
/// <param name="size">Size.</param>
/// <param name="strength">Strength.</param>
/// <param name="playgroundParticles">Playground particles.</param>
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<boneMatrices.Length; i++) {
boneMatrices[i] = particleStateWorldObject.renderer.bones[i].localToWorldMatrix * bindPoses[i];
}
PlaygroundC.RunAsync(()=>{
Matrix4x4 vertexMatrix = new Matrix4x4();
for (i = 0; i<vertices.Length; i++) {
BoneWeight weight = weights[i];
Matrix4x4 m0 = boneMatrices[weight.boneIndex0];
Matrix4x4 m1 = boneMatrices[weight.boneIndex1];
Matrix4x4 m2 = boneMatrices[weight.boneIndex2];
Matrix4x4 m3 = boneMatrices[weight.boneIndex3];
for (int n=0;n<16;n++) {
vertexMatrix[n] =
m0[n] * weight.weight0 +
m1[n] * weight.weight1 +
m2[n] * weight.weight2 +
m3[n] * weight.weight3;
}
vertices[i] = vertexMatrix.MultiplyPoint3x4(vertices[i]);
}
particleStateWorldObject.vertexPositions = vertices;
});
}
// Get position from Mesh World Object
public static void GetPosition (Vector3[] v3, WorldObject particleStateWorldObject) {
if (particleStateWorldObject.meshFilter.sharedMesh!=particleStateWorldObject.mesh)
particleStateWorldObject.mesh = particleStateWorldObject.meshFilter.sharedMesh;
v3 = particleStateWorldObject.mesh.vertices;
}
// Get procedural position from Mesh World Object
public static void GetProceduralPosition (Vector3[] v3, WorldObject particleStateWorldObject) {
if (particleStateWorldObject.meshFilter.sharedMesh!=particleStateWorldObject.mesh)
particleStateWorldObject.mesh = particleStateWorldObject.meshFilter.sharedMesh;
Vector3[] vertices = particleStateWorldObject.mesh.vertices;
if (v3.Length!=vertices.Length) v3 = new Vector3[vertices.Length];
for (int i = 0; i<v3.Length; i++) {
v3[i] = particleStateWorldObject.transform.TransformPoint(vertices[i%vertices.Length]);
}
}
// Get normals from Mesh World Object
public static void GetNormals (Vector3[] v3, WorldObject particleStateWorldObject) {
v3 = particleStateWorldObject.mesh.normals;
}
// Set size for particles
public static void SetSize (PlaygroundParticlesC playgroundParticles, float size) {
for (int i = 0; i<playgroundParticles.particleCache.Length; i++) {
playgroundParticles.playgroundCache.initialSize[i] = size;
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2
playgroundParticles.particleCache[i].size = size;
#else
playgroundParticles.particleCache[i].startSize = size;
#endif
}
}
public void RefreshSystemRandom () {
internalRandom01 = new System.Random();
}
// Set random size for particles within sizeMinimum- and sizeMaximum range
public static void SetSizeRandom (PlaygroundParticlesC playgroundParticles, float sizeMinimum, float sizeMaximum) {
playgroundParticles.playgroundCache.initialSize = RandomFloat(playgroundParticles.particleCache.Length, sizeMinimum, sizeMaximum, playgroundParticles.internalRandom01);
playgroundParticles.sizeMin = sizeMinimum;
playgroundParticles.sizeMax = sizeMaximum;
playgroundParticles.previousSizeMin = playgroundParticles.sizeMin;
playgroundParticles.previousSizeMax = playgroundParticles.sizeMax;
}
// Set random rotation for particles within rotationMinimum- and rotationMaximum range
public static void SetRotationRandom (PlaygroundParticlesC playgroundParticles, float rotationMinimum, float rotationMaximum) {
playgroundParticles.playgroundCache.rotationSpeed = RandomFloat(playgroundParticles.particleCache.Length, rotationMinimum, rotationMaximum, playgroundParticles.internalRandom01);
for (int i = 0; i<playgroundParticles.particleCache.Length; i++) {
playgroundParticles.playgroundCache.rotation[i] = playgroundParticles.playgroundCache.initialRotation[i];
}
playgroundParticles.rotationSpeedMin = rotationMinimum;
playgroundParticles.rotationSpeedMax = rotationMaximum;
playgroundParticles.previousRotationSpeedMin = playgroundParticles.rotationSpeedMin;
playgroundParticles.previousRotationSpeedMax = playgroundParticles.rotationSpeedMax;
}
// Set random initial rotation for particles within rotationMinimum- and rotationMaximum range
public static void SetInitialRotationRandom (PlaygroundParticlesC playgroundParticles, float rotationMinimum, float rotationMaximum) {
playgroundParticles.playgroundCache.initialRotation = RandomFloat(playgroundParticles.particleCache.Length, rotationMinimum, rotationMaximum, playgroundParticles.internalRandom01);
for (int i = 0; i<playgroundParticles.particleCache.Length; i++) {
playgroundParticles.playgroundCache.rotation[i] = playgroundParticles.playgroundCache.initialRotation[i];
}
playgroundParticles.initialRotationMin = rotationMinimum;
playgroundParticles.initialRotationMax = rotationMaximum;
playgroundParticles.previousInitialRotationMin = playgroundParticles.initialRotationMin;
playgroundParticles.previousInitialRotationMax = playgroundParticles.initialRotationMax;
}
// Set initial random velocity for particles within velocityMinimum- and velocityMaximum range
public static void SetVelocityRandom (PlaygroundParticlesC playgroundParticles, Vector3 velocityMinimum, Vector3 velocityMaximum) {
playgroundParticles.playgroundCache.initialVelocity = new Vector3[playgroundParticles.particleCount];
for (int i = 0; i<playgroundParticles.particleCount; i++) {
if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.Spherical)
playgroundParticles.playgroundCache.initialVelocity[i] = RandomRangeSpherical(playgroundParticles.internalRandom01, velocityMinimum.x, velocityMaximum.x);
else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.SphericalLinear)
playgroundParticles.playgroundCache.initialVelocity[i] = RandomRangeSpherical(playgroundParticles.internalRandom01, velocityMinimum.x, velocityMaximum.x, (i*1f)/(playgroundParticles.particleCount*1f));
else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.RectangularLinear)
playgroundParticles.playgroundCache.initialVelocity[i] = Vector3.Lerp (velocityMinimum, velocityMaximum, (i*1f)/(playgroundParticles.particleCount*1f));
//else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.SphericalSector)
// playgroundParticles.playgroundCache.initialVelocity[i] = RandomRangeSpherical(playgroundParticles.internalRandom01, playgroundParticles.initialVelocityMin.x, playgroundParticles.initialVelocityMax.x, playgroundParticles.initialVelocityMin.y, playgroundParticles.initialVelocityMax.y);
//else if (playgroundParticles.initialVelocityMethod==MINMAXVECTOR3METHOD.SphericalSectorLinear)
// playgroundParticles.playgroundCache.initialVelocity[i] = RandomRangeSpherical(playgroundParticles.internalRandom01, playgroundParticles.initialVelocityMin.x, playgroundParticles.initialVelocityMax.x, playgroundParticles.initialVelocityMin.y, playgroundParticles.initialVelocityMax.y, (i*1f)/(playgroundParticles.particleCount*1f));
else playgroundParticles.playgroundCache.initialVelocity[i] = RandomRange(playgroundParticles.internalRandom01, velocityMinimum, velocityMaximum);
}
playgroundParticles.initialVelocityMin = velocityMinimum;
playgroundParticles.initialVelocityMax = velocityMaximum;
playgroundParticles.previousVelocityMin = playgroundParticles.initialVelocityMin;
playgroundParticles.previousVelocityMax = playgroundParticles.initialVelocityMax;
playgroundParticles.previousVelocityMethod = playgroundParticles.initialVelocityMethod;
}
// Set initial random local velocity for particles within velocityMinimum- and velocityMaximum range
public static void SetLocalVelocityRandom (PlaygroundParticlesC playgroundParticles, Vector3 velocityMinimum, Vector3 velocityMaximum) {
playgroundParticles.playgroundCache.initialLocalVelocity = new Vector3[playgroundParticles.particleCount];
for (int i = 0; i<playgroundParticles.particleCount; i++) {
if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.Spherical)
playgroundParticles.playgroundCache.initialLocalVelocity[i] = RandomRangeSpherical(playgroundParticles.internalRandom01, velocityMinimum.x, velocityMaximum.x);
else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.SphericalLinear)
playgroundParticles.playgroundCache.initialLocalVelocity[i] = RandomRangeSpherical(playgroundParticles.internalRandom01, velocityMinimum.x, velocityMaximum.x, (i*1f)/(playgroundParticles.particleCount*1f));
else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.RectangularLinear)
playgroundParticles.playgroundCache.initialLocalVelocity[i] = Vector3.Lerp (velocityMinimum, velocityMaximum, (i*1f)/(playgroundParticles.particleCount*1f));
//else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.SphericalSector)
// playgroundParticles.playgroundCache.initialLocalVelocity[i] = RandomRangeSpherical(playgroundParticles.internalRandom01, playgroundParticles.initialLocalVelocityMin.x, playgroundParticles.initialLocalVelocityMax.x, playgroundParticles.initialLocalVelocityMin.y, playgroundParticles.initialLocalVelocityMax.y);
//else if (playgroundParticles.initialLocalVelocityMethod==MINMAXVECTOR3METHOD.SphericalSectorLinear)
// playgroundParticles.playgroundCache.initialLocalVelocity[i] = RandomRangeSpherical(playgroundParticles.internalRandom01, playgroundParticles.initialLocalVelocityMin.x, playgroundParticles.initialLocalVelocityMax.x, playgroundParticles.initialLocalVelocityMin.y, playgroundParticles.initialLocalVelocityMax.y, (i*1f)/(playgroundParticles.particleCount*1f));
else playgroundParticles.playgroundCache.initialLocalVelocity[i] = RandomRange(playgroundParticles.internalRandom01, velocityMinimum, velocityMaximum);
}
playgroundParticles.initialLocalVelocityMin = velocityMinimum;
playgroundParticles.initialLocalVelocityMax = velocityMaximum;
playgroundParticles.previousLocalVelocityMin = playgroundParticles.initialLocalVelocityMin;
playgroundParticles.previousLocalVelocityMax = playgroundParticles.initialLocalVelocityMax;
playgroundParticles.previousLocalVelocityMethod = playgroundParticles.initialLocalVelocityMethod;
}
// Set material for particle system
public static void SetMaterial (PlaygroundParticlesC playgroundParticles, Material particleMaterial) {
playgroundParticles.particleSystemRenderer.sharedMaterial = particleMaterial;
}
// Set alphas for particles
public static void SetAlpha (PlaygroundParticlesC playgroundParticles, float alpha) {
Color pColor;
for (int i = 0; i<playgroundParticles.particleCache.Length; i++) {
pColor = playgroundParticles.GetParticleColorInternal(i);
pColor.a = alpha;
playgroundParticles.SetParticleColorInternal(i, pColor);
}
}
// Move all particles in direction
public static void Translate (PlaygroundParticlesC playgroundParticles, Vector3 direction) {
for (int i = 0; i<playgroundParticles.particleCache.Length; i++)
playgroundParticles.particleCache[i].position += direction;
}
// Add new state from state
public static void Add (PlaygroundParticlesC playgroundParticles, ParticleStateC state) {
playgroundParticles.states.Add(state);
state.Initialize();
}
// Add new state from image
public static void Add (PlaygroundParticlesC playgroundParticles, Texture2D image, float scale, Vector3 offset, string stateName, Transform stateTransform) {
playgroundParticles.states.Add(new ParticleStateC());
playgroundParticles.states[playgroundParticles.states.Count-1].ConstructParticles(image,scale,offset,stateName,stateTransform);
}
// Add new state from image with depthmap
public static void Add (PlaygroundParticlesC playgroundParticles, Texture2D image, Texture2D depthmap, float depthmapStrength, float scale, Vector3 offset, string stateName, Transform stateTransform) {
playgroundParticles.states.Add(new ParticleStateC());
playgroundParticles.states[playgroundParticles.states.Count-1].ConstructParticles(image,scale,offset,stateName,stateTransform);
playgroundParticles.states[playgroundParticles.states.Count-1].stateDepthmap = depthmap;
playgroundParticles.states[playgroundParticles.states.Count-1].stateDepthmapStrength = depthmapStrength;
}
// Destroy a PlaygroundParticlesC object
public static void Destroy (PlaygroundParticlesC playgroundParticles) {
Clear(playgroundParticles);
MonoBehaviour.DestroyImmediate(playgroundParticles.particleSystemGameObject);
playgroundParticles = null;
}
// Sorts the particles in lifetime
bool isSettingLifetime = false;
public static void SetLifetime (PlaygroundParticlesC playgroundParticles, SORTINGC sorting, float time) {
if (!playgroundParticles.enabled) return;
if (playgroundParticles.isSettingLifetime || playgroundParticles.isSettingParticleCount) return;
if (playgroundParticles.internalRandom01==null)
playgroundParticles.RefreshSystemRandom();
playgroundParticles.isSettingLifetime = true;
if (playgroundParticles.multithreadedStartup)
{
PlaygroundC.RunAsync(()=>{
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; r<playgroundParticles.particleCount; r++) {
if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;}
playgroundParticles.playgroundCache.lifetimeOffset[r] = RandomRange(playgroundParticles.internalRandom01, 0f, playgroundParticles.lifetime*lifetimeEmission);
}
break;
case SORTINGC.ScrambledLinear:
float slPerc;
for (int sl = 0; sl<playgroundParticles.particleCount; sl++) {
if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;}
slPerc = (sl*1f)/(playgroundParticles.particleCount*1f);
playgroundParticles.playgroundCache.lifetimeOffset[sl] = playgroundParticles.lifetime*lifetimeEmission*slPerc;
}
for (int i = playgroundParticles.playgroundCache.lifetimeOffset.Length-1; i>0; 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<playgroundParticles.particleCount; l++) {
if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;}
lPerc = (l*1f)/(playgroundParticles.particleCount*1f);
playgroundParticles.playgroundCache.lifetimeOffset[l] = playgroundParticles.lifetime*lifetimeEmission*lPerc;
}
break;
case SORTINGC.Reversed:
float rPerc;
int rInc = 0;
for (int r = playgroundParticles.particleCount-1; r>=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; nn<playgroundParticles.particleCount; nn++) {
if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;}
nnDist[nn%playgroundParticles.particleCount] = Vector3.SqrMagnitude(nnrOrigin - playgroundParticles.playgroundCache.targetPosition[nn%playgroundParticles.particleCount]);
if (nnDist[nn%playgroundParticles.particleCount]>nnHighest)
nnHighest = nnDist[nn%playgroundParticles.particleCount];
}
if (nnHighest>0) {
for (int nn = 0; nn<playgroundParticles.particleCount; nn++) {
if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;}
playgroundParticles.playgroundCache.lifetimeOffset[nn%playgroundParticles.particleCount] = Mathf.Lerp(playgroundParticles.lifetime*(lifetimeEmission-.001f), 0, (nnDist[nn%playgroundParticles.particleCount]/nnHighest));
}
} else {
for (int nn = 0; nn<playgroundParticles.particleCount; nn++) {
if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;}
playgroundParticles.playgroundCache.lifetimeOffset[nn%playgroundParticles.particleCount] = 0;
}
}
break;
case SORTINGC.NearestNeighbor:
playgroundParticles.nearestNeighborOrigin = Mathf.Clamp(playgroundParticles.nearestNeighborOrigin, 0, playgroundParticles.particleCount-1);
float[] nnrDist = new float[playgroundParticles.particleCount];
float nnrHighest = 0;
Vector3 nnOrigin = Vector3.zero;
switch (playgroundParticles.nearestNeighborOriginMethod) {
case NEARESTNEIGHBORORIGINMETHOD.SourcePoint: nnOrigin = playgroundParticles.playgroundCache.targetPosition[playgroundParticles.nearestNeighborOrigin%playgroundParticles.particleCount]; break;
case NEARESTNEIGHBORORIGINMETHOD.Vector3: nnOrigin = playgroundParticles.nearestNeighborOriginVector3; break;
case NEARESTNEIGHBORORIGINMETHOD.Transform: nnOrigin = playgroundParticles.nearestNeighborOriginTransformPosition; break;
}
for (int nnr = 0; nnr<playgroundParticles.particleCount; nnr++) {
if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;}
nnrDist[nnr%playgroundParticles.particleCount] = Vector3.SqrMagnitude(nnOrigin-playgroundParticles.playgroundCache.targetPosition[nnr%playgroundParticles.particleCount]);
if (nnrDist[nnr%playgroundParticles.particleCount]>nnrHighest)
nnrHighest = nnrDist[nnr%playgroundParticles.particleCount];
}
if (nnrHighest>0) {
for (int nnr = 0; nnr<playgroundParticles.particleCount; nnr++) {
if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;}
playgroundParticles.playgroundCache.lifetimeOffset[nnr%playgroundParticles.particleCount] = Mathf.Lerp(0, playgroundParticles.lifetime*(lifetimeEmission-.001f), (nnrDist[nnr%playgroundParticles.particleCount]/nnrHighest));
}
} else {
for (int nnr = 0; nnr<playgroundParticles.particleCount; nnr++) {
if (pCount!=playgroundParticles.playgroundCache.lifetimeOffset.Length) {playgroundParticles.isSettingLifetime = false; return;}
playgroundParticles.playgroundCache.lifetimeOffset[nnr%playgroundParticles.particleCount] = 0;
}
}
break;
case SORTINGC.Custom:
for (int cs = playgroundParticles.particleCount-1; cs>=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.playgroundCache.lifetimeSubtraction.Length; p++) {
if (playgroundParticles.lifetimeValueMethod==VALUEMETHOD.Constant) {
playgroundParticles.playgroundCache.lifetimeSubtraction[p] = 0;
} else {
playgroundParticles.playgroundCache.lifetimeSubtraction[p] = RandomRange(random, 0, playgroundParticles.lifetime-playgroundParticles.lifetimeMin);
}
}
playgroundParticles.previousLifetimeMin = playgroundParticles.lifetimeMin;
playgroundParticles.previousLifetimeValueMethod = playgroundParticles.lifetimeValueMethod;
}
// Set emission rate percentage of particle count
public static void SetEmissionRate (PlaygroundParticlesC playgroundParticles) {
if (playgroundParticles.playgroundCache.emission==null) return;
float rateCount = playgroundParticles.lifetime*playgroundParticles.emissionRate;
int currentCount = playgroundParticles.playgroundCache.emission.Length;
bool hasOverflow = playgroundParticles.source!=SOURCEC.Transform&&(playgroundParticles.overflowOffset!=Vector3.zero||playgroundParticles.applySourceScatter&&(playgroundParticles.sourceScatterMin!=Vector3.zero||playgroundParticles.sourceScatterMax!=Vector3.zero));
for (int p = 0; p<playgroundParticles.playgroundCache.emission.Length; p++) {
if (currentCount!=playgroundParticles.playgroundCache.emission.Length || playgroundParticles.playgroundCache.lifetimeOffset.Length!=currentCount) return;
if (playgroundParticles.emissionRate!=0 && playgroundParticles.source!=SOURCEC.Script) {
if (playgroundParticles.sorting!=SORTINGC.Burst || playgroundParticles.sorting==SORTINGC.NearestNeighbor && hasOverflow || playgroundParticles.sorting==SORTINGC.NearestNeighborReversed && hasOverflow) {
playgroundParticles.playgroundCache.emission[p] = (playgroundParticles.playgroundCache.lifetimeOffset[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; p<playgroundParticles.particleCount; p++) {
playgroundParticles.playgroundCache.birth[p] = (currentTime+playgroundParticles.playgroundCache.lifetimeOffset[p])-playgroundParticles.lifetime;
playgroundParticles.playgroundCache.death[p] = playgroundParticles.playgroundCache.birth[p]+playgroundParticles.lifetime;
playgroundParticles.playgroundCache.life[p] = (playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.lifetimeOffset[p])-playgroundParticles.playgroundCache.birth[p];
playgroundParticles.particleCache[p].startLifetime = playgroundParticles.lifetime;
#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.targetPosition[p] = PlaygroundC.initialTargetPosition;
playgroundParticles.playgroundCache.previousTargetPosition[p] = PlaygroundC.initialTargetPosition;
playgroundParticles.playgroundCache.position[p] = PlaygroundC.initialTargetPosition;
playgroundParticles.playgroundCache.simulate[p] = true;
playgroundParticles.playgroundCache.emission[p] = true;
playgroundParticles.playgroundCache.isFirstLoop[p] = true;
playgroundParticles.playgroundCache.isNonBirthed[p] = true;
playgroundParticles.playgroundCache.isCalculatedThisFrame[p] = false;
if (!playgroundParticles.isPrewarming)
playgroundParticles.playgroundCache.rebirth[p] = true;
}
playgroundParticles.localTime = currentTime;
playgroundParticles.lastTimeUpdated = playgroundParticles.localTime;
playgroundParticles.localDeltaTime = 0;
playgroundParticles.t = 0;
playgroundParticles.simulationStarted = playgroundParticles.localTime;
playgroundParticles.cameFromNonEmissionFrame = true;
playgroundParticles.hasActiveParticles = true;
playgroundParticles.threadHadNoActiveParticles = false;
}
playgroundParticles.isSettingParticleTime = false;
}
bool isSettingParticleTime = false;
// Set time of particles with current time and take emission delta into consideration (time when emit went false to current)
public static void SetParticleTimeNowWithRestEmission (PlaygroundParticlesC playgroundParticles) {
playgroundParticles.isSettingParticleTime = true;
float currentTime = playgroundParticles.localTime;
float emissionDelta = currentTime-playgroundParticles.emissionStopped;
if (playgroundParticles.loop && emissionDelta<playgroundParticles.lifetime && emissionDelta>0) {
playgroundParticles.cameFromNonEmissionFrame = true;
} else {
SetParticleTimeNow(playgroundParticles);
return;
}
for (int p = 0; p<playgroundParticles.particleCount; p++) {
playgroundParticles.playgroundCache.birthDelay[p] = emissionDelta;
playgroundParticles.playgroundCache.rebirth[p] = true;
playgroundParticles.playgroundCache.simulate[p] = true;
playgroundParticles.playgroundCache.emission[p] = true;
}
playgroundParticles.isSettingParticleTime = false;
}
// Get color from evaluated lifetime color value where time is normalized
public static Color32 GetColorAtLifetime (PlaygroundParticlesC playgroundParticles, float time) {
return playgroundParticles.lifetimeColor.Evaluate(time/playgroundParticles.lifetime);
}
// Color all particles from evaluated lifetime color value where time is normalized
public static void SetColorAtLifetime (PlaygroundParticlesC playgroundParticles, float time) {
Color32 c = playgroundParticles.lifetimeColor.Evaluate(time/playgroundParticles.lifetime);
for (int p = 0; p<playgroundParticles.particleCount; p++)
playgroundParticles.SetParticleColorInternal(p, c);
playgroundParticles.shurikenParticleSystem.SetParticles(playgroundParticles.particleCache, playgroundParticles.particleCache.Length);
}
// Color all particles from lifetime span with sorting
public static void SetColorWithLifetimeSorting (PlaygroundParticlesC playgroundParticles) {
SetLifetime(playgroundParticles, playgroundParticles.sorting, playgroundParticles.lifetime);
Color32 c;
for (int p = 0; p<playgroundParticles.particleCount; p++) {
c = playgroundParticles.lifetimeColor.Evaluate(playgroundParticles.playgroundCache.life[p]/playgroundParticles.lifetime);
playgroundParticles.SetParticleColorInternal(p, c);
}
playgroundParticles.shurikenParticleSystem.SetParticles(playgroundParticles.particleCache, playgroundParticles.particleCache.Length);
}
public bool IsSettingParticleCount () {
return isSettingParticleCount;
}
// Sets the amount of particles and initiates the necessary arrays
bool isSettingParticleCount = false;
public static void SetParticleCount (PlaygroundParticlesC playgroundParticles, int amount) {
if (playgroundParticles.isSettingParticleCount) return;
if (playgroundParticles.isPrewarming) return;
if (amount<0) amount = 0;
if (playgroundParticles.internalRandom01==null)
playgroundParticles.RefreshSystemRandom();
playgroundParticles.particleCount = amount;
playgroundParticles.previousParticleCount = amount;
// Create Particles
playgroundParticles.particleCache = new ParticleSystem.Particle[amount];
playgroundParticles.shurikenParticleSystem.Emit(amount);
playgroundParticles.shurikenParticleSystem.GetParticles(playgroundParticles.particleCache);
// Clear collision cache
playgroundParticles.collisionCache = null;
playgroundParticles.inTransition = false;
playgroundParticles.hasActiveParticles = true;
playgroundParticles.threadHadNoActiveParticles = false;
playgroundParticles.isSettingParticleCount = true;
if (playgroundParticles.multithreadedStartup)
{
PlaygroundC.RunAsync(()=>{
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; i<amount; i++) {
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2
playgroundParticles.particleCache[i].size = 0f;
#else
playgroundParticles.particleCache[i].startSize = 0f;
#endif
playgroundParticles.playgroundCache.rebirth[i] = true;
playgroundParticles.playgroundCache.simulate[i] = true;
playgroundParticles.playgroundCache.isNonBirthed[i] = true;
playgroundParticles.playgroundCache.isFirstLoop[i] = true;
// 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[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;
/// <summary>
/// Updates a PlaygroundParticlesC object (called each calculation step from PlaygroundC).
/// </summary>
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;
}
/// <summary>
/// Updates the Shuriken component. This sets all particles previously calculated and syncs particle positioning if syncPositionsOnMainThread is true.
/// </summary>
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; p<particleCount; p++) {
// This particle is still calculating (happens on heavier particle systems), move to next!
if (!playgroundCache.isCalculatedThisFrame[p])
continue;
// Source positions and 0 life reset
if (setSource || playgroundCache.life[p]==0f && playgroundCache.simulate[p] && !onlyLifetimePositioning) {
particleCache[p].lifetime = lifetime;
playgroundCache.previousTargetPosition[p] = playgroundCache.targetPosition[p];
SetSourcePosition(p);
playgroundCache.position[p] = playgroundCache.targetPosition[p];
}
// Reposition particle
particleCache[p].position = playgroundCache.position[p];
// Shuriken lifetime (for Texture Atlas Animation)
if (playgroundCache.life[p]!=0f) {
float evalLife = playgroundCache.death[p]-localTime;
if (evalLife<mslt) evalLife = mslt;
particleCache[p].lifetime = evalLife;
}
// Size
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2
particleCache[p].size = (playgroundCache.maskAlpha[p]>0&&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<playgroundParticles.particleCache.Length; p++) {
playgroundParticles.playgroundCache.previousTargetPosition[p] = position;
playgroundParticles.playgroundCache.targetPosition[p] = position;
#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].position = position;
playgroundParticles.playgroundCache.position[p] = position;
playgroundParticles.playgroundCache.collisionParticlePosition[p] = position;
}
if (refreshParticleSystem)
playgroundParticles.GetComponent<ParticleSystem>().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<playgroundParticles.playgroundCache.rebirth.Length; p++) {
playgroundParticles.playgroundCache.rebirth[p] = true;
if (!callRestEmission)
playgroundParticles.playgroundCache.simulate[p] = true;
}
if (callRestEmission)
SetParticleTimeNowWithRestEmission(playgroundParticles);
playgroundParticles.emissionStopped = 0;
playgroundParticles.hasActiveParticles = true;
playgroundParticles.threadHadNoActiveParticles = false;
});
}
}
// Returns the angle between a and b with normal direction
public static float SignedAngle (Vector3 a, Vector3 b, Vector3 n) {
return (Vector3.Angle(a, b)*Mathf.Sign(Vector3.Dot(n.normalized, Vector3.Cross(a, b))));
}
// Returns a random value between negative- and positive Vector3
public static Vector3 RandomVector3 (System.Random random, Vector3 v1, Vector3 v2) {
return RandomRange(random, v1, v2);
}
float realSimulationTime;
#if UNITY_EDITOR
public static bool didHierarchyRepaint = false;
#endif
/// <summary>
/// Prepares all values for calculation which is not thread-safe.
/// </summary>
/// <returns><c>true</c>, if threaded calculations was prepared, <c>false</c> otherwise.</returns>
public bool PrepareThreadedCalculations () {
// Component disabled?
if (!enabled) return false;
// 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<PlaygroundTransformC>();
}
if (sourceTransforms.Count==0) {
sourceTransforms.Add(new PlaygroundTransformC());
sourceTransforms[0].transform = sourceTransform!=null?sourceTransform:particleSystemTransform;
}
if (isDoneThread) {
for (int i = 0; i<sourceTransforms.Count; i++) {
if (!sourceTransforms[i].Update()) return false;
if (IsLocalSpace()) {
psTransformNum = -1;
sourceTransforms[i].SetLocalPosition(particleSystemTransform);
sourceTransforms[i].SetPostitionAsLocal();
if (sourceTransforms[i].IsSameAs (particleSystemTransform)) {
sourceTransforms[i].SetZeroRotation();
psTransformNum = i;
}
}
}
}
if (isDoneThread) {
stRot = sourceTransform.rotation;
}
if (localSpace && sourceTransform==particleSystemTransform && isDoneThread) {
cancelDeltaPositioningOnSync = true;
stRot = Quaternion.Euler (Vector3.zero);
}
break;
case SOURCEC.WorldObject:
// Handle vertex data in active World Object
if (worldObject.gameObject!=null) {
if (worldObject.gameObject.GetInstanceID()!=worldObject.cachedId || !worldObject.initialized)
worldObject = NewWorldObject(worldObject.gameObject.transform);
if (worldObject.mesh!=null) {
if (isDoneThread) {
stRot = worldObject.transform.transform.rotation;
}
if (localSpace && isDoneThread) {
cancelDeltaPositioningOnSync = true;
stRot = Quaternion.Euler (Vector3.zero);
}
worldObject.UpdateMatrix(localSpace);
worldObject.updateNormals = worldObjectUpdateNormals;
if (worldObjectUpdateVertices)
worldObject.Update();
} else return false;
} else return false;
break;
case SOURCEC.SkinnedWorldObject:
// Handle vertex data in active Skinned World Object
if (skinnedWorldObject.gameObject!=null) {
if (skinnedWorldObject.gameObject.GetInstanceID()!=skinnedWorldObject.cachedId) {
skinnedWorldObject = NewSkinnedWorldObject(skinnedWorldObject.gameObject.transform, skinnedWorldObject.downResolution);
}
}
skinnedWorldObjectReady = skinnedWorldObject.gameObject!=null && skinnedWorldObject.mesh!=null;
if (PlaygroundC.reference.skinnedMeshThreadMethod==ThreadMethodComponent.OneForAll && PlaygroundC.reference.IsFirstUnsafeAutomaticFrames() && !PlaygroundC.reference.IsDoneThreadSkinnedMeshes())
return false;
if (skinnedWorldObjectReady) {
if (isDoneThread) {
stRot = skinnedWorldObject.transform.rotation;
stDir = skinnedWorldObject.transform.TransformDirection (overflowOffset);
}
skinnedWorldObject.updateNormals = worldObjectUpdateNormals;
if (Time.frameCount%PlaygroundC.skinnedUpdateRate==0) {
if (worldObjectUpdateVertices)
skinnedWorldObject.MeshUpdate();
skinnedWorldObject.BoneUpdate();
// Forced update for skinned mesh vertices on main-thread
if (forceSkinnedMeshUpdateOnMainThread) {
skinnedWorldObject.Update();
}
}
} else return false;
break;
case SOURCEC.Paint:
if (paint.initialized) {
if (isDoneThread) {
stRot = particleSystemTransform.rotation;
}
if (paint.positionLength>0) {
for (int p = 0; p<particleCache.Length; p++) {
paint.Update(p);
}
} else return false;
} else {
paint.Initialize ();
return false;
}
break;
case SOURCEC.Projection:
if (projection.projectionTexture!=null && projection.projectionTransform!=null) {
if (!projection.initialized)
projection.Initialize();
stRot = projection.projectionTransform.rotation;
if (localSpace)
shurikenParticleSystem.simulationSpace = ParticleSystemSimulationSpace.World;
if (projection.liveUpdate || !projection.hasRefreshed) {
projection.UpdateSource();
projection.Update();
stDir = projection.projectionTransform.TransformDirection (overflowOffset);
projection.hasRefreshed = true;
}
} else return false;
break;
case SOURCEC.Spline:
if (splines.Count>0) {
for (int i = 0; i<splines.Count; i++)
if (splines[i]==null || !splines[i].enabled || !splines[i].IsReady()) return false;
if (isDoneThread) {
stRot = particleSystemTransform.rotation;
if (localSpace) {
cancelDeltaPositioningOnSync = true;
stRot = Quaternion.Euler (Vector3.zero);
}
}
} else return false;
break;
}
}
// Prepare Particle colors
stateReadyForTextureColor = (source==SOURCEC.State && states[activeState].stateTexture!=null && states[activeState].colorLength>0);
hasEventManipulatorLocal = false;
hasEventManipulatorGlobal = false;
hasGlobalAffectingManipulators = false;
manipulatorEventCount = 0;
thisLayer = particleSystemGameObject.layer;
// Prepare Local Manipulators
for (int m = 0; m<manipulators.Count; m++) {
manipulators[m].Update();
manipulators[m].transform.SetLocalPosition(particleSystemTransform);
manipulators[m].SetLocalTargetsPosition(particleSystemTransform);
if (manipulators[m].trackParticles) {
hasEventManipulatorLocal = true;
manipulatorEventCount++;
}
}
// Prepare Global Manipulators from this local space, bundle the thread call in case any Global Manipulator is tracking particles
if (PlaygroundC.reference.HasEnabledGlobalManipulators()) {
for (int m = 0; m<PlaygroundC.reference.manipulators.Count; m++) {
if ((PlaygroundC.reference.manipulators[m].affects.value & 1<<GetLayer())!=0) {
hasGlobalAffectingManipulators = true;
PlaygroundC.reference.manipulators[m].transform.SetLocalPosition(particleSystemTransform);
if (PlaygroundC.reference.manipulators[m].trackParticles && PlaygroundC.reference.threadMethod!=ThreadMethod.NoThreads) {
threadMethod = ThreadMethodLocal.OneForAll;
hasEventManipulatorGlobal = true;
manipulatorEventCount++;
}
}
}
}
hasSeveralManipulatorEvents = (manipulatorEventCount>1);
// Prepare events
hasEvent = events.Count>0;
hasTimerEvent = false;
if (hasEvent) {
for (int i = 0; i<events.Count; i++) {
events[i].Initialize();
if (events[i].initializedTarget && events[i].broadcastType!=EVENTBROADCASTC.EventListeners)
if (!events[i].target.eventControlledBy.Contains (thisInstance))
events[i].target.eventControlledBy.Add (thisInstance);
if (events[i].eventType==EVENTTYPEC.Time && ((events[i].broadcastType!=EVENTBROADCASTC.EventListeners && events[i].initializedTarget) || events[i].broadcastType==EVENTBROADCASTC.EventListeners && events[i].initializedEvent)) {
hasTimerEvent = events[i].UpdateTime();
}
}
}
// Prepare collision cache checking
hasCollisionCache = collisionCache!=null;
// All good! Next step is to send all particles to calculation.
return true;
}
public static void NewCalculatedThread (PlaygroundParticlesC playgroundParticles) {
if (playgroundParticles.isDoneThread && playgroundParticles.isReadyForThreadedCalculations && !playgroundParticles.IsYieldRefreshing()) {
playgroundParticles.isDoneThread = false;
PlaygroundC.RunAsync(()=> {
if (playgroundParticles.isDoneThread || !playgroundParticles.isReadyForThreadedCalculations) return;
ThreadedCalculations(playgroundParticles);
});
}
}
public static void NewCalculatedThread (PlaygroundParticlesC[] playgroundParticles) {
for (int i = 0; i<playgroundParticles.Length; i++) {
if (playgroundParticles[i].isDoneThread && !playgroundParticles[i].IsYieldRefreshing() && playgroundParticles[i].threadMethod==ThreadMethodLocal.Inherit)
playgroundParticles[i].isDoneThread = false;
}
PlaygroundC.RunAsync(()=> {
for (int i = 0; i<playgroundParticles.Length; i++) {
if (!playgroundParticles[i].isDoneThread && PlaygroundC.reference.IsDoneThread() && playgroundParticles[i].isReadyForThreadedCalculations && playgroundParticles[i].threadMethod==ThreadMethodLocal.Inherit) {
ThreadedCalculations(playgroundParticles[i]);
}
}
});
}
Color32 GetParticleColor (int p, float normalizedLife, float normalizedP) {
Color32 currentColor = new Color32();
// Get this particle's color
if (!playgroundCache.changedByPropertyColor[p] && !playgroundCache.changedByPropertyColorLerp[p]) {
switch (colorSource) {
case COLORSOURCEC.Source:
switch (source) {
case SOURCEC.Script:
currentColor = playgroundCache.scriptedColor[p];
break;
case SOURCEC.State:
if (stateReadyForTextureColor)
currentColor = states[activeState].GetColor(p%states[activeState].colorLength);
else
currentColor = lifetimeColor.Evaluate(colorMethod==COLORMETHOD.Lifetime?normalizedLife:normalizedP);
break;
case SOURCEC.Projection:
currentColor = projection.GetColor(p);
break;
case SOURCEC.Paint:
currentColor = paint.GetColor(p);
break;
default:
currentColor = lifetimeColor.Evaluate(colorMethod==COLORMETHOD.Lifetime?normalizedLife:normalizedP);
break;
}
if (sourceUsesLifetimeAlpha)
currentColor.a = (byte)(255*Mathf.Clamp(lifetimeColor.Evaluate(colorMethod==COLORMETHOD.Lifetime?normalizedLife:normalizedP).a, 0, currentColor.a));
break;
case COLORSOURCEC.LifetimeColor:
currentColor = lifetimeColor.Evaluate(colorMethod==COLORMETHOD.Lifetime?normalizedLife:normalizedP);
break;
case COLORSOURCEC.LifetimeColors:
if (lifetimeColors.Count>0)
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;
}
/// <summary>
/// Sets the source position for a particle from the selected source method in the particle system.
/// </summary>
/// <param name="p">Particle Index.</param>
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);
}
/// <summary>
/// Asynchronous-friendly particle calculation routine.
/// </summary>
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<playgroundParticles.sourceTransforms.Count; i++)
playgroundParticles.sourceTransforms[i].UpdateMatrix();
// Update skinned mesh vertices
if (playgroundParticles.source==SOURCEC.SkinnedWorldObject && playgroundParticles.skinnedWorldObjectReady && !playgroundParticles.forceSkinnedMeshUpdateOnMainThread && PlaygroundC.reference.skinnedMeshThreadMethod==ThreadMethodComponent.InsideParticleCalculation) {
playgroundParticles.skinnedWorldObject.Update();
}
// Misc
int pCount = playgroundParticles.particleCache.Length;
// Check that cache is correct
if (playgroundParticles.playgroundCache.lifetimeSubtraction.Length!=pCount)
SetLifetimeSubtraction(playgroundParticles);
if (playgroundParticles.playgroundCache.maskAlpha.Length!=pCount) {
playgroundParticles.playgroundCache.maskAlpha = new float[pCount];
playgroundParticles.playgroundCache.isMasked = new bool[pCount];
}
if (playgroundParticles.playgroundCache.manipulatorId.Length!=pCount)
playgroundParticles.playgroundCache.manipulatorId = new int[pCount];
if (playgroundParticles.playgroundCache.excludeFromManipulatorId.Length!=pCount)
playgroundParticles.playgroundCache.excludeFromManipulatorId = new int[pCount];
if (playgroundParticles.playgroundCache.noForce.Length!=pCount)
playgroundParticles.playgroundCache.noForce = new bool[pCount];
if (playgroundParticles.playgroundCache.isNonBirthed.Length!=pCount)
playgroundParticles.playgroundCache.isNonBirthed = new bool[pCount];
if (playgroundParticles.playgroundCache.isFirstLoop.Length!=pCount)
playgroundParticles.playgroundCache.isFirstLoop = new bool[pCount];
if (playgroundParticles.playgroundCache.isCalculatedThisFrame.Length!=pCount)
playgroundParticles.playgroundCache.isCalculatedThisFrame = new bool[pCount];
if (playgroundParticles.playgroundCache.simulate.Length!=pCount) {
playgroundParticles.playgroundCache.simulate = new bool[pCount];
for (int p = 0; p<pCount; p++)
playgroundParticles.playgroundCache.simulate[p] = true;
}
bool hasLifetimeLoss = playgroundParticles.collision && playgroundParticles.lifetimeLoss > 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<playgroundParticles.particleCount; p++) {
// Check that particle count is correct
if (pCount != playgroundParticles.particleCache.Length || playgroundParticles.isYieldRefreshing || playgroundParticles.isSettingParticleTime || playgroundParticles.isSettingParticleCount) {
playgroundParticles.cameFromNonEmissionFrame = false;
playgroundParticles.isDoneThread = true;
playgroundParticles.threadHadNoActiveParticles = false;
return;
}
// Check simulation
if (!playgroundParticles.playgroundCache.simulate[p]) {
if (playgroundParticles.playgroundCache.rebirth[p] && playgroundParticles.loop) {
playgroundParticles.InactivateParticle(p);
}
continue;
} else {
noActiveParticles = false;
}
// This particle is about to be calculated
playgroundParticles.playgroundCache.isCalculatedThisFrame[p] = false;
// Prepare variables inside scope
float lifeInSeconds = (playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-playgroundParticles.playgroundCache.lifetimeSubtraction[p];
float normalizedLife = Mathf.Clamp01(playgroundParticles.playgroundCache.life[p]/lifeInSeconds);
float normalizedP = (p*1f)/(playgroundParticles.particleCount*1f);
float lifetimePositioningTimeScale = 1f;
if (playgroundParticles.applyLifetimePositioningTimeScale)
lifetimePositioningTimeScale = playgroundParticles.lifetimePositioningTimeScale.Evaluate(normalizedLife);
// Apply particle mask
if (playgroundParticles.applyParticleMask) {
int maskedP = playgroundParticles.playgroundCache.maskSorting[p];
if (maskedP>=0 && maskedP<playgroundParticles.particleCount) {
if (p<playgroundParticles.particleMask) {
if (playgroundParticles.playgroundCache.maskAlpha[maskedP]<=0 || playgroundParticles.particleMaskTime<=0) {
playgroundParticles.playgroundCache.isMasked[maskedP] = true;
playgroundParticles.playgroundCache.maskAlpha[maskedP] = 0;
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2
playgroundParticles.particleCache[maskedP].size = 0;
#else
playgroundParticles.particleCache[maskedP].startSize = 0;
#endif
} else {
playgroundParticles.playgroundCache.maskAlpha[maskedP] -= (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime;
}
} else {
if (playgroundParticles.playgroundCache.maskAlpha[maskedP]>=1f || playgroundParticles.particleMaskTime<=0) {
playgroundParticles.playgroundCache.isMasked[maskedP] = false;
playgroundParticles.playgroundCache.maskAlpha[maskedP] = 1f;
} else {
playgroundParticles.playgroundCache.maskAlpha[maskedP] += (1f/playgroundParticles.particleMaskTime)*playgroundParticles.localDeltaTime;
}
}
}
} else {
playgroundParticles.playgroundCache.isMasked[p] = false;
playgroundParticles.playgroundCache.maskAlpha[p] = 1f;
}
Color32 lifetimeColor = playgroundParticles.GetParticleColor(p, normalizedLife, normalizedP);
// Assign color to particle
playgroundParticles.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]<playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p]) {
playgroundParticles.playgroundCache.life[p] = lifeInSeconds*evaluatedLife;
if (hasLifetimeLoss)
{
playgroundParticles.playgroundCache.life[p] += playgroundParticles.playgroundCache.lifetimeLoss[p];
playgroundParticles.playgroundCache.lifetimeLoss[p] = 0;
}
if (!playgroundParticles.syncPositionsOnMainThread)
playgroundParticles.particleCache[p].lifetime = Mathf.Clamp (lifeInSeconds*(1f-evaluatedLife), playgroundParticles.minShurikenLifetime, playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p]);
if (playgroundParticles.lifetimeValueMethod==VALUEMETHOD.RandomBetweenTwoValues && playgroundParticles.playgroundCache.life[p]>lifeInSeconds) {
// Send death event for particles with lifetime subtraction
if ((playgroundParticles.hasEvent||playgroundParticles.hasEventManipulatorGlobal||playgroundParticles.hasEventManipulatorLocal) && !playgroundParticles.playgroundCache.isNonBirthed[p])
SendDeathEvents(playgroundParticles, p);
#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; m<playgroundParticles.manipulators.Count; m++) {
CalculateManipulator(playgroundParticles, playgroundParticles.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], (playgroundParticles.localSpace?playgroundParticles.manipulators[m].transform.localPosition:playgroundParticles.manipulators[m].transform.position)+playgroundParticles.manipulatorFix, playgroundParticles.localSpace);
}
// Global Manipulators
if (playgroundParticles.hasGlobalAffectingManipulators) {
for (int m = 0; m<PlaygroundC.reference.manipulators.Count; m++) {
if ((PlaygroundC.reference.manipulators[m].affects.value & 1<<playgroundParticles.GetLayer())!=0)
CalculateManipulator(playgroundParticles, PlaygroundC.reference.manipulators[m], p, t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], (playgroundParticles.localSpace?PlaygroundC.reference.manipulators[m].transform.localPosition:PlaygroundC.reference.manipulators[m].transform.position)+playgroundParticles.manipulatorFix, playgroundParticles.localSpace);
}
}
// Set particle size
if (!playgroundParticles.syncPositionsOnMainThread)
{
#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.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<playgroundParticles.particleCount; p++) {
if (!playgroundParticles.playgroundCache.noForce[p])
Turbulence(playgroundParticles, playgroundParticles.turbulenceSimplex, p, playgroundParticles.t, playgroundParticles.turbulenceType, playgroundParticles.turbulenceTimeScale, playgroundParticles.turbulenceScale/playgroundParticles.velocityScale, playgroundParticles.turbulenceStrength, playgroundParticles.turbulenceApplyLifetimeStrength, playgroundParticles.turbulenceLifetimeStrength);
}
});
}
}
/// <summary>
/// Sends the death events.
/// </summary>
/// <param name="playgroundParticles">Playground particles.</param>
/// <param name="p">Particle index.</param>
public static void SendDeathEvents (PlaygroundParticlesC playgroundParticles, int p) {
if ((playgroundParticles.playgroundCache.life[p]>0||playgroundParticles.playgroundCache.changedByPropertyDeath[p]) && !playgroundParticles.playgroundCache.isNonBirthed[p]) {
if (playgroundParticles.loop || (!playgroundParticles.loop && playgroundParticles.playgroundCache.isFirstLoop[p])) {
playgroundParticles.SendEvent(EVENTTYPEC.Death, p);
}
}
if (playgroundParticles.hasEventManipulatorLocal) {
for (int i = 0; i<playgroundParticles.manipulators.Count; i++) {
if (playgroundParticles.manipulators[i].trackParticles &&
playgroundParticles.manipulators[i].RemoveParticle (playgroundParticles.particleSystemId, p)) {
if (playgroundParticles.manipulators[i].sendEventDeath) {
playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p);
playgroundParticles.manipulators[i].SendParticleEventDeath();
}
}
}
}
if (playgroundParticles.hasEventManipulatorGlobal) {
for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) {
if (PlaygroundC.reference.manipulators[i].trackParticles &&
PlaygroundC.reference.manipulators[i].RemoveParticle (playgroundParticles.particleSystemId, p)) {
if (PlaygroundC.reference.manipulators[i].sendEventDeath) {
playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p);
PlaygroundC.reference.manipulators[i].SendParticleEventDeath();
}
}
}
}
playgroundParticles.playgroundCache.isNonBirthed[p] = true;
}
public static void Turbulence (PlaygroundParticlesC playgroundParticles, SimplexNoise turbulenceSimplex, int p, float t, TURBULENCETYPE turbulenceType, float turbulenceTimeScale, float turbulenceScale, float turbulenceStrength, bool turbulenceApplyLifetimeStrength, AnimationCurve turbulenceLifetimeStrength) {
if (!playgroundParticles.calculate || playgroundParticles.inPlayback)
return;
float turbulenceStrengthMultiplier = 1f;
float zeroFixX = 1f;
float zeroFixY = 2f;
float zeroFixZ = 3f;
if (playgroundParticles.playgroundCache.simulate[p] && playgroundParticles.playgroundCache.position[p].y != PlaygroundC.initialTargetPosition.y) {
if (turbulenceType==TURBULENCETYPE.Simplex) {
// Simplex Noise
if (turbulenceTimeScale>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 += (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);
}
/// <summary>
/// Calculates particle collisions, this runs automatically if collision is set to true. (Must currently run on main-thread due to the Physics.Raycast dependency.)
/// </summary>
/// <param name="playgroundParticles">Particle Playground system.</param>
public static void Collisions (PlaygroundParticlesC playgroundParticles) {
if (!playgroundParticles.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.colliders.Count; c++) {
playgroundParticles.colliders[c].UpdatePlane();
}
}
// Check cache length
if (playgroundParticles.playgroundCache.noForce.Length!=playgroundParticles.particleCount)
playgroundParticles.playgroundCache.noForce = new bool[playgroundParticles.particleCount];
if (playgroundParticles.playgroundCache.lifetimeSubtraction.Length!=playgroundParticles.particleCount)
playgroundParticles.playgroundCache.lifetimeSubtraction = new float[playgroundParticles.particleCount];
if (playgroundParticles.playgroundCache.lifetimeLoss == null || playgroundParticles.playgroundCache.lifetimeLoss.Length!=playgroundParticles.particleCount)
playgroundParticles.playgroundCache.lifetimeLoss = new float[playgroundParticles.particleCount];
for (int p = 0; p<playgroundParticles.particleCount; p++) {
// Early out if there's no need to continue with this particle
if (playgroundParticles.playgroundCache.life[p]==0 ||
playgroundParticles.playgroundCache.life[p]>=(playgroundParticles.playgroundCache.death[p]-playgroundParticles.playgroundCache.birth[p])-playgroundParticles.playgroundCache.lifetimeSubtraction[p] ||
playgroundParticles.playgroundCache.noForce[p] ||
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; c<playgroundParticles.colliders.Count; c++) {
if (playgroundParticles.colliders[c].enabled && playgroundParticles.colliders[c].transform && !playgroundParticles.colliders[c].plane.GetSide(playgroundParticles.playgroundCache.position[p])) {
// Set particle to location
ray.origin = playgroundParticles.playgroundCache.position[p];
ray.direction = playgroundParticles.colliders[c].plane.normal;
if (playgroundParticles.colliders[c].plane.Raycast(ray, out distance))
playgroundParticles.playgroundCache.position[p] = ray.GetPoint(distance);
// Store velocity before collision
Vector3 preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p];
// Update collision cache
if (!playgroundParticles.stickyCollisions) {
if (playgroundParticles.forceCollisionCaching)
playgroundParticles.collisionCache.Set (p, playgroundParticles.playgroundCache.position[p], playgroundParticles.colliders[c].plane.normal, playgroundParticles.colliders[c].transform);
} else
playgroundParticles.collisionCache.SetSticky (p, playgroundParticles.playgroundCache.position[p], playgroundParticles.colliders[c].plane.normal, playgroundParticles.stickyCollisionsSurfaceOffset, playgroundParticles.colliders[c].transform);
// Reflect particle
playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], hasRandomBounce?playgroundParticles.colliders[c].plane.normal+RandomVector3(playgroundParticles.internalRandom01, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax):playgroundParticles.colliders[c].plane.normal)*playgroundParticles.bounciness;
// Apply lifetime loss
if (playgroundParticles.lifetimeLoss>0) {
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<<hitInfo.transform.gameObject.layer)!=0)
playgroundParticles.collisionCache.SetSticky (p, hitInfo.point, hitInfo.normal, playgroundParticles.stickyCollisionsSurfaceOffset, hitInfo.transform);
// Store velocity before collision
Vector3 preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p];
// Reflect particle
playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], hasRandomBounce?hitInfo.normal+RandomVector3(playgroundParticles.internalRandom01, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax) : hitInfo.normal)*playgroundParticles.bounciness;
// Apply lifetime loss
if (playgroundParticles.lifetimeLoss>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; i<playgroundParticles.manipulators.Count; i++) {
if (playgroundParticles.manipulators[i].trackParticles &&
playgroundParticles.manipulators[i].sendEventCollision &&
playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], playgroundParticles.manipulators[i].transform.position)) {
playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p);
playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider = hitInfo.collider;
playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo.point;
playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo.transform;
playgroundParticles.manipulators[i].SendParticleEventCollision();
}
}
}
if (playgroundParticles.hasEventManipulatorGlobal) {
for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) {
if (PlaygroundC.reference.manipulators[i].trackParticles &&
PlaygroundC.reference.manipulators[i].sendEventCollision &&
PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], PlaygroundC.reference.manipulators[i].transform.position)) {
playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p);
PlaygroundC.reference.manipulators[i].manipulatorEventParticle.collisionCollider = hitInfo.collider;
PlaygroundC.reference.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo.point;
PlaygroundC.reference.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo.transform;
PlaygroundC.reference.manipulators[i].SendParticleEventCollision();
}
}
}
}
} else {
hitInfo2D = Physics2D.Raycast(
playgroundParticles.playgroundCache.collisionParticlePosition[p],
(playgroundParticles.playgroundCache.position[p]-playgroundParticles.playgroundCache.collisionParticlePosition[p]),
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,
playgroundParticles.minCollisionDepth,
playgroundParticles.maxCollisionDepth
);
if (hitInfo2D.collider!=null) {
// Check that this object isn't excluded
if (hasCollisionExclusion) {
if (playgroundParticles.collisionExclusion.Contains(hitInfo2D.transform))
continue;
}
// Set particle to location
playgroundParticles.playgroundCache.position[p] = playgroundParticles.playgroundCache.collisionParticlePosition[p];
// Update collision cache
if (!playgroundParticles.stickyCollisions) {
if (playgroundParticles.forceCollisionCaching)
playgroundParticles.collisionCache.Set (p, hitInfo2D.point, hitInfo2D.normal, hitInfo2D.transform);
} else if ((playgroundParticles.stickyCollisionsMask.value & 1<<hitInfo2D.transform.gameObject.layer)!=0)
playgroundParticles.collisionCache.SetSticky (p, hitInfo2D.point, hitInfo2D.normal, playgroundParticles.stickyCollisionsSurfaceOffset, hitInfo2D.transform);
// Store velocity before collision
Vector3 preCollisionVelocity = playgroundParticles.playgroundCache.velocity[p];
// Reflect particle
playgroundParticles.playgroundCache.velocity[p] = Vector3.Reflect(playgroundParticles.playgroundCache.velocity[p], hasRandomBounce?(Vector3)hitInfo2D.normal+RandomVector3(playgroundParticles.internalRandom01, playgroundParticles.bounceRandomMin, playgroundParticles.bounceRandomMax) : (Vector3)hitInfo2D.normal)*playgroundParticles.bounciness;
// Apply lifetime loss
if (playgroundParticles.lifetimeLoss>0) {
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<playgroundParticles.manipulators.Count; i++) {
if (playgroundParticles.manipulators[i].trackParticles &&
playgroundParticles.manipulators[i].sendEventCollision &&
playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], playgroundParticles.manipulators[i].transform.position)) {
playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p);
playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider2D = hitInfo2D.collider;
playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo2D.point;
playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo2D.transform;
playgroundParticles.manipulators[i].SendParticleEventCollision();
}
}
}
if (playgroundParticles.hasEventManipulatorGlobal) {
for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) {
if (PlaygroundC.reference.manipulators[i].trackParticles &&
PlaygroundC.reference.manipulators[i].sendEventCollision &&
PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.position[p], PlaygroundC.reference.manipulators[i].transform.position)) {
playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p);
playgroundParticles.manipulators[i].manipulatorEventParticle.collisionCollider2D = hitInfo2D.collider;
playgroundParticles.manipulators[i].manipulatorEventParticle.collisionParticlePosition = hitInfo2D.point;
playgroundParticles.manipulators[i].manipulatorEventParticle.collisionTransform = hitInfo2D.transform;
PlaygroundC.reference.manipulators[i].SendParticleEventCollision();
}
}
}
}
}
} else {
playgroundParticles.playgroundCache.velocity[p] = Vector3.zero;
}
}
}
}
/// <summary>
/// Returns the offset as a remainder from a point, constructed for multithreading.
/// </summary>
/// <returns>The overflow position.</returns>
/// <param name="overflow">Overflow by Vector3.</param>
/// <param name="currentVal">Current value.</param>
/// <param name="maxVal">Max value.</param>
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
);
}
/// <summary>
/// Returns the offset with direction as a remainder from a point, constructed for multithreading.
/// </summary>
/// <returns>The overflow position in direction.</returns>
/// <param name="overflow">Overflow by Vector3.</param>
/// <param name="direction">Direction.</param>
/// <param name="currentVal">Current value.</param>
/// <param name="maxVal">Max value.</param>
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
);
}
/// <summary>
/// Multiplies two Vector3.
/// </summary>
/// <returns>The scale of two Vector3.</returns>
/// <param name="v1">First Vector3.</param>
/// <param name="v2">Second Vector3.</param>
public static Vector3 Vector3Scale (Vector3 v1, Vector3 v2) {
return new Vector3(v1.x*v2.x,v1.y*v2.y,v1.z*v2.z);
}
/// <summary>
/// Calculates the effect from a Manipulator.
/// </summary>
/// <param name="playgroundParticles">Playground particles.</param>
/// <param name="thisManipulator">This manipulator.</param>
/// <param name="p">Particle index.</param>
/// <param name="t">Delta time.</param>
/// <param name="life">Lifetime.</param>
/// <param name="particlePosition">Particle position.</param>
/// <param name="manipulatorPosition">Manipulator position.</param>
/// <param name="manipulatorDistance">Manipulator distance.</param>
/// <param name="localSpace">Is calculation in local space?</param>
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<thisManipulator.properties.Count; i++)
PropertyManipulator(playgroundParticles, thisManipulator, thisManipulator.properties[i], manipulatorStrength, p, t, particlePosition, manipulatorPosition, manipulatorDistance, localSpace, contains);
}
// Apply constraints
if (hasConstraints) {
playgroundParticles.playgroundCache.velocity[p] = new Vector3(
thisManipulator.axisConstraints.x? constrainedVelocity.x : playgroundParticles.playgroundCache.velocity[p].x,
thisManipulator.axisConstraints.y? constrainedVelocity.y : playgroundParticles.playgroundCache.velocity[p].y,
thisManipulator.axisConstraints.z? constrainedVelocity.z : playgroundParticles.playgroundCache.velocity[p].z
);
playgroundParticles.playgroundCache.position[p] = new Vector3(
thisManipulator.axisConstraints.x? particlePosition.x : playgroundParticles.playgroundCache.position[p].x,
thisManipulator.axisConstraints.y? particlePosition.y : playgroundParticles.playgroundCache.position[p].y,
thisManipulator.axisConstraints.z? particlePosition.z : playgroundParticles.playgroundCache.position[p].z
);
}
}
}
/// <summary>
/// Calculates the effect from Manipulator's properties.
/// </summary>
/// <param name="playgroundParticles">Playground particles.</param>
/// <param name="thisManipulator">This manipulator.</param>
/// <param name="thisManipulatorProperty">This manipulator property.</param>
/// <param name="p">Particle index.</param>
/// <param name="t">Delta time.</param>
/// <param name="particlePosition">Particle position.</param>
/// <param name="manipulatorPosition">Manipulator position.</param>
/// <param name="localSpace">Is calculation in local space?</param>
/// <param name="contains">Does the Manipulator contain the particle at index?</param>
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<particleCount; p++) {
if (applySourceScatter) {
if (sourceScatterMethod==MINMAXVECTOR3METHOD.Rectangular)
playgroundCache.scatterPosition[p] = RandomRange(random, sourceScatterMin, sourceScatterMax);
else if (sourceScatterMethod==MINMAXVECTOR3METHOD.RectangularLinear)
playgroundCache.scatterPosition[p] = Vector3.Lerp (sourceScatterMin, sourceScatterMax, (p*1f)/(particleCount*1f));
else if (sourceScatterMethod==MINMAXVECTOR3METHOD.Spherical)
playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, sourceScatterMin.x, sourceScatterMax.x);
else if (sourceScatterMethod==MINMAXVECTOR3METHOD.SphericalLinear)
playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, sourceScatterMin.x, sourceScatterMax.x, (p*1f)/(particleCount*1f));
//else if (sourceScatterMethod==MINMAXVECTOR3METHOD.SphericalSector)
// playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, sourceScatterMin.x, sourceScatterMax.x, sourceScatterMin.y, sourceScatterMax.y);
//else if (sourceScatterMethod==MINMAXVECTOR3METHOD.SphericalSectorLinear)
// playgroundCache.scatterPosition[p] = RandomRangeSpherical(random, sourceScatterMin.x, sourceScatterMax.x, sourceScatterMin.y, sourceScatterMax.y, (p*1f)/(particleCount*1f));
} else playgroundCache.scatterPosition[p] = Vector3.zero;
}
}
/// <summary>
/// Returns a random range float, adapted for threading.
/// </summary>
/// <returns>The range.</returns>
/// <param name="random">Random.</param>
/// <param name="min">Minimum.</param>
/// <param name="max">Max.</param>
public static float RandomRange (System.Random random, float min, float max) {
return min+((float)random.NextDouble())*(max-min);
}
/// <summary>
/// Random range Vector3 adapted for threading. This produces a rectangular random position inside min to max. Should you want spherical distribution use RandomRangeSpherical instead.
/// </summary>
/// <returns>The range.</returns>
/// <param name="random">Random.</param>
/// <param name="min">Minimum.</param>
/// <param name="max">Max.</param>
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)
);
}
/// <summary>
/// Spherical random range Vector3, adapted for threading.
/// </summary>
/// <returns>The spherical random range.</returns>
/// <param name="random">Random.</param>
/// <param name="min">Minimum.</param>
/// <param name="max">Max.</param>
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; i<length; i++) {
f[i] = RandomRange (random, min, max);
}
return f;
}
public void InactivateParticle (int particleId) {
playgroundCache.simulate[particleId] = false;
playgroundCache.rebirth[particleId] = false;
playgroundCache.isNonBirthed[particleId] = true;
playgroundCache.isFirstLoop[particleId] = true;
playgroundCache.isCalculatedThisFrame[particleId] = false;
playgroundCache.position[particleId] = PlaygroundC.initialTargetPosition;
playgroundCache.targetPosition[particleId] = playgroundCache.position[particleId];
playgroundCache.previousTargetPosition[particleId] = playgroundCache.targetPosition[particleId];
playgroundCache.velocity[particleId] = Vector3.zero;
particleCache[particleId].position = playgroundCache.position[particleId];
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2
particleCache[particleId].size = 0;
#else
particleCache[particleId].startSize = 0;
#endif
// Reset manipulators influence
playgroundCache.changedByProperty[particleId] = false;
playgroundCache.changedByPropertyColor[particleId] = false;
playgroundCache.changedByPropertyColorLerp[particleId] = false;
playgroundCache.changedByPropertyColorKeepAlpha[particleId] = false;
playgroundCache.changedByPropertySize[particleId] = false;
playgroundCache.changedByPropertyTarget[particleId] = false;
playgroundCache.changedByPropertyDeath[particleId] = false;
playgroundCache.propertyTarget[particleId] = 0;
playgroundCache.propertyId[particleId] = 0;
playgroundCache.propertyColorId[particleId] = 0;
playgroundCache.manipulatorId[particleId] = 0;
}
public void InactivateParticles () {
for (int p = 0; p<particleCount; p++)
InactivateParticle(p);
hasActiveParticles = false;
threadHadNoActiveParticles = true;
isDoneThread = true;
if (particleCache!=null && particleCache.Length>0)
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; m<playgroundParticles.manipulators.Count; m++) {
if (playgroundParticles.manipulators[m].transform!=null) {
CalculateManipulator(playgroundParticles, playgroundParticles.manipulators[m], p, playgroundParticles.t, playgroundParticles.playgroundCache.life[p], playgroundParticles.playgroundCache.position[p], (playgroundParticles.localSpace?playgroundParticles.manipulators[m].transform.localPosition:playgroundParticles.manipulators[m].transform.position)+playgroundParticles.manipulatorFix, playgroundParticles.localSpace);
}
}
}
// Send birth event
if (playgroundParticles.events.Count>0 && playgroundParticles.playgroundCache.rebirth[p])
playgroundParticles.SendEvent(EVENTTYPEC.Birth, p);
if (playgroundParticles.hasEventManipulatorLocal) {
for (int i = 0; i<playgroundParticles.manipulators.Count; i++) {
if (playgroundParticles.manipulators[i].trackParticles &&
playgroundParticles.manipulators[i].sendEventBirth &&
playgroundParticles.manipulators[i].Contains (playgroundParticles.playgroundCache.targetPosition[p], playgroundParticles.manipulators[i].transform.position)) {
playgroundParticles.UpdateEventParticle(playgroundParticles.manipulators[i].manipulatorEventParticle, p);
playgroundParticles.manipulators[i].SendParticleEventBirth();
}
}
}
if (playgroundParticles.hasEventManipulatorGlobal) {
for (int i = 0; i<PlaygroundC.reference.manipulators.Count; i++) {
if (PlaygroundC.reference.manipulators[i].trackParticles &&
PlaygroundC.reference.manipulators[i].sendEventBirth &&
PlaygroundC.reference.manipulators[i].Contains (playgroundParticles.playgroundCache.targetPosition[p], PlaygroundC.reference.manipulators[i].transform.position)) {
playgroundParticles.UpdateEventParticle(PlaygroundC.reference.manipulators[i].manipulatorEventParticle, p);
PlaygroundC.reference.manipulators[i].SendParticleEventBirth();
}
}
}
}
// Sends an event to Event- Targets and Listeners
void SendEvent (EVENTTYPEC eventType, int p) {
SendEvent(eventType, p, playgroundCache.velocity[p], null, null, null);
}
void SendEvent (EVENTTYPEC eventType, int p, Vector3 preEventVelocity) {
SendEvent(eventType, p, preEventVelocity, null, null, null);
}
void SendEvent (EVENTTYPEC eventType, int p, Vector3 preEventVelocity, Transform collisionTransform) {
SendEvent(eventType, p, preEventVelocity, collisionTransform, null, null);
}
void SendEvent (EVENTTYPEC eventType, int p, Vector3 preEventVelocity, Transform collisionTransform, Collider collisionCollider) {
SendEvent(eventType, p, preEventVelocity, collisionTransform, collisionCollider, null);
}
void SendEvent (EVENTTYPEC eventType, int p, Vector3 preEventVelocity, Transform collisionTransform, Collider2D collisionCollider2D) {
SendEvent(eventType, p, preEventVelocity, collisionTransform, null, collisionCollider2D);
}
void SendEvent (EVENTTYPEC eventType, int p, Vector3 preEventVelocity, Transform collisionTransform, Collider collisionCollider, Collider2D collisionCollider2D) {
Vector3 eventPosition = Vector3.zero;
Vector3 eventVelocity = Vector3.zero;
Color32 eventColor = Color.white;
// Loop through available events
for (int i = 0; i<events.Count; i++) {
if (events[i].enabled && events[i].eventType==eventType) {
// Check thresholds
if (events[i].eventType==EVENTTYPEC.Collision)
if (events[i].collisionThreshold>preEventVelocity.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();
}
/// <summary>
/// Store the current state of a particle system as a Snapshot.
/// </summary>
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());
}
/// <summary>
/// Store and name the current state of a particle system as a Snapshot.
/// </summary>
/// <param name="saveName">Save name.</param>
public void Save (string saveName) {
if (isSnapshot) {
Debug.Log("A snapshot can't store snapshot data within itself.", gameObject);
return;
}
SaveRoutine (saveName);
}
/// <summary>
/// Store the current state of a particle system as a Snapshot asynchronously.
/// </summary>
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()));
}
/// <summary>
/// Store and name the current state of a particle system as a Snapshot asynchronously.
/// </summary>
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;
}
/// <summary>
/// Load from a saved data state (Snapshot) using an int.
/// </summary>
/// <param name="loadPointer">Load pointer.</param>
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);
}
}
/// <summary>
/// Load from a saved data state (Snapshot) using a string.
/// </summary>
/// <param name="loadName">Load name.</param>
public void Load (string loadName) {
if (snapshots.Count>0) {
for (int i = 0; i<snapshots.Count; i++) {
if (snapshots[i].name == loadName.Trim()) {
StartCoroutine(LoadRoutine(i, 0));
return;
}
}
} else {
Debug.Log ("No data found with the name "+loadName+".", particleSystemGameObject);
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="loadPointer">The Snapshot you wish to load.</param>
/// <param name="loadMask">The amount of masked particles in the particle array.</param>
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; i<snapshots[loadPointer].settings.particleCount; i++) {
int maskedP = snapshots[loadPointer].settings.snapshotData.maskSorting[i];
snapshots[loadPointer].settings.snapshotData.isMasked[maskedP] = i<mask;
snapshots[loadPointer].settings.snapshotData.maskAlpha[maskedP] = i<mask?0:1f;
}
}
StartCoroutine(LoadTransition(loadPointer));
while (inTransition) yield return null;
}
if (isLoading || inTransition || abortTransition)
yield break;
isLoading = true;
int prevParticleCount = particleCount;
if (prevParticleCount!=snapshots[loadPointer].settings.particleCount) {
SetParticleCount(thisInstance, snapshots[loadPointer].settings.particleCount);
while (isSettingParticleCount)
yield return null;
}
snapshots[loadPointer].Load(thisInstance);
while (snapshots[loadPointer].IsLoading())
yield return null;
lastTimeUpdated = PlaygroundC.globalTime;
localTime = PlaygroundC.globalTime;
cameFromNonCalculatedFrame = false;
cameFromNonEmissionFrame = false;
if (snapshots[loadPointer].loadMode!=1) {
if (loadFromStart && isYieldRefreshing)
yield return null;
float tos = snapshots[loadPointer].settings.timeOfSnapshot<=0?snapshots[loadPointer].time:snapshots[loadPointer].settings.timeOfSnapshot;
localTime = tos;
for (int p = 0; p<particleCount; p++) {
playgroundCache.birth[p] = tos+(playgroundCache.birth[p]-tos);
playgroundCache.death[p] = tos+(playgroundCache.death[p]-tos);
playgroundCache.size[p] = snapshots[loadPointer].settings.snapshotData.size[p];
#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
}
lifetime = snapshots[loadPointer].settings.lifetime;
previousLifetimeValueMethod = lifetimeValueMethod;
previousLifetime = lifetime;
previousLifetimeMin = lifetimeMin;
previousLifetimeEmission = lifetimeEmission;
previousNearestNeighborOrigin = nearestNeighborOrigin;
previousNearestNeighborOriginMethod = nearestNeighborOriginMethod;
previousNearestNeighborOriginVector3 = nearestNeighborOriginVector3;
previousNearestNeighborOriginTransform = nearestNeighborOriginTransform;
previousSorting = sorting;
localDeltaTime = .001f;
t = localDeltaTime;
} else if (prevParticleCount!=particleCount) {
SetLifetime(thisInstance, sorting, lifetime);
yield return null;
while (isSettingLifetime) {
yield return null;
}
}
hasActiveParticles = true;
threadHadNoActiveParticles = false;
lastTimeUpdated = PlaygroundC.globalTime;
cameFromNonCalculatedFrame = false;
cameFromNonEmissionFrame = false;
loopExceeded = false;
loopExceededOnParticle = -1;
isLoading = false;
isDoneThread = true;
transitionAvailable = true;
}
// Apply transition between snapshot load
bool abortTransition = false;
IEnumerator LoadTransition (int loadPointer) {
if (inTransition) {
abortTransition = true;
yield return null;
}
abortTransition = false;
inTransition = true;
float transitionStartTime = PlaygroundC.globalTime;
float loadParticleLifetime = snapshots[loadPointer].settings.lifetime;
int loadParticleCount = snapshots[loadPointer].settings.particleCount;
bool liveParticles = snapshots[loadPointer].loadMode!=1;
bool firstFrameDone = false;
int currentParticleCount = particleCount;
int transitionParticleCount = currentParticleCount;
float initPosY = PlaygroundC.initialTargetPosition.y;
PlaygroundCache loadSnapshotData = snapshots[loadPointer].settings.snapshotData;
Color transparent = new Color(1f,1f,1f,0);
INDIVIDUALTRANSITIONTYPEC thisSnapshotTransition = snapshots[loadPointer].transitionType;
if (thisSnapshotTransition==INDIVIDUALTRANSITIONTYPEC.Inherit) {
thisSnapshotTransition = (INDIVIDUALTRANSITIONTYPEC)((int)loadTransitionType+1);
}
// Prepare arrays
Vector3[] transitionPosition = (Vector3[])playgroundCache.position.Clone();
Color32[] transitionColor = (Color32[])playgroundCache.color.Clone();
float[] transitionSize = (float[])playgroundCache.size.Clone();
float[] transitionRotation = (float[])playgroundCache.rotation.Clone();
// Resize if more particles are needed
if (loadParticleCount>particleCount) {
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<loadParticleCount; p++) {
transitionPosition[p] = transitionPosition[p%particleCount];
transitionColor[p].a = 0;
}
isResizing = false;
});
while (isResizing)
yield return null;
particleCache = new ParticleSystem.Particle[loadParticleCount];
shurikenParticleSystem.Emit(loadParticleCount);
shurikenParticleSystem.GetParticles(particleCache);
transitionParticleCount = loadParticleCount;
}
// Check array sizes (additions to new versions of Playground Cache after snapshot was created)
if (loadSnapshotData.lifetimeSubtraction==null || loadSnapshotData.lifetimeSubtraction.Length!=loadParticleCount)
loadSnapshotData.lifetimeSubtraction = new float[loadParticleCount];
if (loadSnapshotData.isNonBirthed==null || loadSnapshotData.isNonBirthed.Length!=loadParticleCount)
loadSnapshotData.isNonBirthed = new bool[loadParticleCount];
if (loadSnapshotData.isMasked==null || loadSnapshotData.isMasked.Length!=loadParticleCount)
loadSnapshotData.isMasked = new bool[loadParticleCount];
// Fix positioning for misplaced particles
for (int p = 0; p<loadParticleCount; p++) {
if (loadSnapshotData.position[p].y == initPosY || loadSnapshotData.life[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.globalTime<transitionStartTime+((loadTransitionTime*snapshots[loadPointer].transitionMultiplier)-.001f) && inTransition && !abortTransition && loadTransition && (loadTransitionTime*snapshots[loadPointer].transitionMultiplier)>0 && currentParticleCount==particleCount) {
float currentTime = PlaygroundC.globalTime;
PlaygroundC.RunAsync(()=>{
float t = TransitionType (thisSnapshotTransition, (currentTime-transitionStartTime)/(loadTransitionTime*snapshots[loadPointer].transitionMultiplier));
for (int p = 0; p<transitionParticleCount; p++) {
if (!loadSnapshotData.isNonBirthed[p%loadParticleCount]) {
// Position
playgroundCache.position[p] = Vector3.Lerp (transitionPosition[p], liveParticles?loadSnapshotData.position[p%loadParticleCount]:loadSnapshotData.targetPosition[p%loadParticleCount], t);
if (!syncPositionsOnMainThread)
particleCache[p].position = playgroundCache.position[p];
// Color
if (!loadSnapshotData.isMasked[p%loadParticleCount]) {
playgroundCache.color[p] = Color32.Lerp (transitionColor[p], loadSnapshotData.color[p%loadParticleCount], t);
} else {
if (playgroundCache.isMasked[p])
playgroundCache.color[p] = transparent;
else playgroundCache.color[p] = Color32.Lerp (transitionColor[p], transparent, t);
}
if ((loadParticleCount<particleCount && 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<transitionParticleCount; p++)
particleCache[p].position = playgroundCache.position[p];
shurikenParticleSystem.SetParticles(particleCache, particleCache.Length);
}
firstFrameDone = true;
}
if (loadParticleCount!=particleCount && abortTransition) {
particleCache = new ParticleSystem.Particle[particleCount];
shurikenParticleSystem.Emit(particleCount);
shurikenParticleSystem.GetParticles(particleCache);
shurikenParticleSystem.SetParticles(particleCache, particleCache.Length);
System.Array.Resize(ref playgroundCache.position, particleCount);
System.Array.Resize(ref playgroundCache.color, particleCount);
System.Array.Resize(ref playgroundCache.size, particleCount);
System.Array.Resize(ref playgroundCache.rotation, particleCount);
}
lastTimeUpdated = PlaygroundC.globalTime;
cameFromNonCalculatedFrame = false;
cameFromNonEmissionFrame = false;
if (!abortTransition)
inTransition = false;
}
float TransitionType (INDIVIDUALTRANSITIONTYPEC thisTransitionType, float t) {
if (thisTransitionType==INDIVIDUALTRANSITIONTYPEC.Linear)
return t;
else if (thisTransitionType==INDIVIDUALTRANSITIONTYPEC.EaseIn)
return Mathf.Lerp (0f, 1f, 1f-Mathf.Cos(t*Mathf.PI*.5f));
else if (thisTransitionType==INDIVIDUALTRANSITIONTYPEC.EaseOut)
return Mathf.Lerp (0f, 1f, Mathf.Sin(t*Mathf.PI*.5f));
return t;
}
// Check all needed references
void CheckReferences () {
if (PlaygroundC.reference==null)
PlaygroundC.reference = FindObjectOfType<PlaygroundC>();
if (PlaygroundC.reference==null) {
PlaygroundC.reference = PlaygroundC.ResourceInstantiate("Playground Manager").GetComponent<PlaygroundC>();
}
if (playgroundCache==null)
playgroundCache = new PlaygroundCache();
if (thisInstance==null)
thisInstance = this;
if (particleSystemGameObject==null) {
particleSystemGameObject = gameObject;
particleSystemTransform = transform;
particleSystemRenderer = GetComponent<Renderer>();
shurikenParticleSystem = particleSystemGameObject.GetComponent<ParticleSystem>();
particleSystemRenderer2 = gameObject.GetComponent<ParticleSystem>().GetComponent<Renderer>() 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;
}
/// <summary>
/// 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.
/// </summary>
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;p<particleCount;p++) SetSourcePosition(p);
storedLocalTime = localTime;
SetLifetime(thisInstance, sorting, lifetime);
while (isSettingLifetime) {yield return null;}
localTime = storedLocalTime;
for (int p=0;p<particleCount;p++) SetSourcePosition(p);
}
while (isSettingLifetime) yield return null;
if (resolution>0) {
isPrewarmingAsync = true;
PlaygroundC.RunAsync(()=>{
while (currentCycles<=resolution) {
PrewarmStepper(time, resolution);
currentCycles++;
}
if (syncPositionsOnMainThread && calculateDeltaMovement) {
for (int p = 0; p<particleCount; p++)
playgroundCache.previousTargetPosition[p] = playgroundCache.targetPosition[p];
}
isPrewarmingAsync = false;
});
}
}
while (isPrewarmingAsync)
yield return null;
isPrewarming = false;
}
void PrewarmStepper (float time, int resolution) {
localDeltaTime = ((lifetime*time)/(resolution*1f));
t = localDeltaTime;
localTime += t;
ThreadedCalculations(thisInstance);
if (HasTurbulence() && PlaygroundC.reference.turbulenceThreadMethod==ThreadMethodComponent.OneForAll)
for (int p = 0; p<particleCount; p++)
Turbulence(thisInstance, turbulenceSimplex, p, t, turbulenceType, turbulenceTimeScale, turbulenceScale, turbulenceStrength, turbulenceApplyLifetimeStrength, turbulenceLifetimeStrength);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MonoBehaviours
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void OnEnable () {
if (isSnapshot || isLoading) return;
thisInstance = this;
thisLayer = particleSystemGameObject.layer;
initialized = false;
isYieldRefreshing = false;
isSettingParticleCount = false;
isSettingLifetime = false;
hasActiveParticles = true;
threadHadNoActiveParticles = false;
isDoneThread = true;
calculate = true;
lastTimeUpdated = PlaygroundC.globalTime;
localTime = lastTimeUpdated;
emissionStopped = 0;
queueEmissionHalt = false;
hasEmitted = false;
// Set new randoms
RefreshSystemRandom();
// Set 0 size to avoid one-frame flash
shurikenParticleSystem.startSize = 0f;
if (shurikenParticleSystem.isPaused || shurikenParticleSystem.isStopped)
shurikenParticleSystem.Play();
// Set initial values
previousLifetimeValueMethod = lifetimeValueMethod;
previousLifetime = lifetime;
previousLifetimeMin = lifetimeMin;
previousEmission = emit;
previousLoop = loop;
previousNearestNeighborOrigin = nearestNeighborOrigin;
previousNearestNeighborOriginMethod = nearestNeighborOriginMethod;
previousNearestNeighborOriginVector3 = nearestNeighborOriginVector3;
previousNearestNeighborOriginTransform = nearestNeighborOriginTransform;
previousSorting = sorting;
previousParticleCount = particleCount;
previousLifetimeEmission = lifetimeEmission;
prevSource = source;
loopExceeded = false;
loopExceededOnParticle = -1;
stRot = Quaternion.identity;
stDir = new Vector3();
// Initiate all arrays by setting particle count
if (particleCache==null) {
SetParticleCount(thisInstance, particleCount);
} else {
// Clean up particle positions
SetInitialTargetPosition(thisInstance, PlaygroundC.initialTargetPosition, true);
// Clean up collision cache
hasCollisionCache = false;
collisionCache = null;
// Refresh
StartCoroutine(YieldedRefresh());
}
}
public void Start () {
if (isSnapshot || !gameObject.activeInHierarchy || !gameObject.activeSelf) return;
// Make sure all references exists
CheckReferences();
if (thisInstance.enabled) {
// Make sure that state data is initialized
if (source==SOURCEC.State)
for (int x = 0; x<states.Count; x++)
states[x].Initialize();
// Initialize
if (worldObject != null && worldObject.transform != null && worldObject.gameObject != null && worldObject.gameObject.activeInHierarchy)
worldObject.Initialize();
if (skinnedWorldObject != null && skinnedWorldObject.transform != null && skinnedWorldObject.gameObject != null && skinnedWorldObject.gameObject.activeInHierarchy)
skinnedWorldObject.Initialize();
if (projection != null && projection.projectionTexture != null)
projection.Initialize();
if (manipulators.Count>0) {
for (int i = 0; i<manipulators.Count; i++)
manipulators[i].Update();
}
}
if (PlaygroundC.reference!=null) {
#if UNITY_EDITOR
if (isSnapshot && !PlaygroundC.reference.showSnapshotsInHierarchy) {
gameObject.hideFlags = HideFlags.HideInHierarchy;
return;
}
#endif
if (isSnapshot) return;
if (!PlaygroundC.reference.particleSystems.Contains(thisInstance))
PlaygroundC.reference.particleSystems.Add(thisInstance);
if (particleSystemTransform.parent==null && PlaygroundC.reference.autoGroup)
particleSystemTransform.parent = PlaygroundC.referenceTransform;
}
// Add this particle system as user of its splines
if (splines!=null && splines.Count>0) {
for (int i = 0; i<splines.Count; i++)
if (splines[i]!=null)
splines[i].AddUser(particleSystemTransform);
}
// Reset event controlled by-list, this will be refreshed first calculation
eventControlledBy = new List<PlaygroundParticlesC>();
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; m<PlaygroundC.reference.manipulators.Count; m++) {
if (PlaygroundC.reference.manipulators[m].trackParticles && (PlaygroundC.reference.manipulators[m].affects.value & 1<<GetLayer())!=0 && PlaygroundC.reference.threadMethod!=ThreadMethod.NoThreads)
threadMethod = ThreadMethodLocal.OneForAll;
}
}
// Update time
localTime = PlaygroundC.globalTime;
lastTimeUpdated = localTime;
// Update id if needed
for (int i = 0; i<PlaygroundC.reference.particleSystems.Count; i++) {
if (PlaygroundC.reference.particleSystems[i].particleSystemId==particleSystemId && PlaygroundC.reference.particleSystems[i]!=this) {
particleSystemId = PlaygroundC.reference.particleSystems.Count-1;
break;
}
}
// Refresh
if (!isYieldRefreshing)
StartCoroutine(YieldedRefresh());
}
}
#if UNITY_EDITOR
void OnDrawGizmos () {
if (pauseCalculationWhenInvisible && calculationTriggerSizeGizmo)
Gizmos.DrawWireCube(calculationTriggerTransform==null?particleSystemTransform.position+calculationTriggerOffset:calculationTriggerTransform.position+calculationTriggerOffset, calculationTriggerSize);
}
#endif
void OnDestroy () {
// Remove any events listed by other particle systems
for (int i = 0; i<events.Count; i++) {
if (events[i].target!=null)
events[i].target.eventControlledBy.Remove (thisInstance);
}
// Remove this particle system as user of its splines
if (splines.Count>0) {
for (int i = 0; i<splines.Count; i++)
if (splines[i]!=null)
splines[i].RemoveUser(particleSystemTransform);
}
// Remove this PlaygroundParticlesC object from Particle Systems list
if (PlaygroundC.reference)
PlaygroundC.reference.particleSystems.Remove(thisInstance);
}
void OnDisable () {
initialized = false;
}
// Select the particle system in Editor from MonoBehavior as we need one frame to initialize
public void EditorYieldSelect () {StartCoroutine (YieldSelect ());}
public IEnumerator YieldSelect () {
if (isSnapshot || Application.isPlaying) yield break;
yield return null;
if (!UnityEditor.EditorApplication.isPlaying)
UnityEditor.Selection.activeGameObject = particleSystemGameObject;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MeshParticles - Extension class for PlaygroundParticlesC which creates mesh states.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class MeshParticles : PlaygroundParticlesC {
public static PlaygroundParticlesC CreateMeshParticles (Mesh[] meshes, Texture2D[] textures, Texture2D[] heightMap, string name, Vector3 position, Quaternion rotation, float particleScale, Vector3[] offsets, Material material) {
PlaygroundParticlesC meshParticles = PlaygroundParticlesC.CreateParticleObject(name,position,rotation,particleScale,material);
meshParticles.states = new List<ParticleStateC>();
int[] quantityList = new int[meshes.Length];
int i = 0;
for (; i<textures.Length; i++)
quantityList[i] = meshes[i].vertexCount;
meshParticles.particleCache = new ParticleSystem.Particle[quantityList[PlaygroundC.Largest(quantityList)]];
meshParticles.shurikenParticleSystem.Emit(meshParticles.particleCache.Length);
meshParticles.shurikenParticleSystem.GetParticles(meshParticles.particleCache);
for (i = 0; i<meshes.Length; i++) {
meshParticles.states.Add(new ParticleStateC());
meshParticles.states[0].ConstructParticles(meshes[i],textures[i],particleScale,offsets[i], "State "+i,null);
}
meshParticles.UpdateSystem();
PlaygroundC.particlesQuantity++;
return meshParticles;
}
public static void Add (PlaygroundParticlesC meshParticles, Mesh mesh, float scale, Vector3 offset, string stateName, Transform stateTransform) {
meshParticles.states.Add(new ParticleStateC());
meshParticles.states[meshParticles.states.Count-1].ConstructParticles(mesh,scale,offset,stateName,stateTransform);
}
public static void Add (PlaygroundParticlesC meshParticles, Mesh mesh, Texture2D texture, float scale, Vector3 offset, string stateName, Transform stateTransform) {
meshParticles.states.Add(new ParticleStateC());
meshParticles.states[meshParticles.states.Count-1].ConstructParticles(mesh,texture,scale,offset,stateName,stateTransform);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Cache
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// Holds information for a particle system Snapshot.
/// </summary>
[Serializable]
public class PlaygroundSave {
/// <summary>
/// The name of this PlaygroundSave.
/// </summary>
[HideInInspector] public string name;
/// <summary>
/// The global time when this PlaygroundSave was created.
/// </summary>
[HideInInspector] public float time;
/// <summary>
/// The lifetime of the particle system.
/// </summary>
[HideInInspector] public float lifetime;
/// <summary>
/// The particle count of the particle system.
/// </summary>
[HideInInspector] public int particleCount;
/// <summary>
/// The version this snapshot was made with.
/// </summary>
[HideInInspector] public float version = 0;
/// <summary>
/// The cached settings of the particle system (Particles are stored in settings.snapshotData).
/// </summary>
[HideInInspector] public PlaygroundParticlesC settings;
/// <summary>
/// The stored transform information.
/// </summary>
[HideInInspector] public PlaygroundTransformC transform;
/// <summary>
/// The stored material.
/// </summary>
[HideInInspector] public Material material;
/// <summary>
/// The method to load data from this PlaygroundSave.
/// 0 = Settings+Particles, 1 = Settings only, 2 = Particles only
/// </summary>
[HideInInspector] public int loadMode = 0;
/// <summary>
/// Should the stored transform information load?
/// </summary>
[HideInInspector] public bool loadTransform = true;
/// <summary>
/// Should the stored material load?
/// </summary>
[HideInInspector] public bool loadMaterial = true;
/// <summary>
/// Should the material load before or after transition?
/// </summary>
[HideInInspector] public bool setMaterialAfterTransition = true;
/// <summary>
/// The multiplier of transition time.
/// </summary>
[HideInInspector] public float transitionMultiplier = 1f;
/// <summary>
/// The transition type of this PlaygroundSave.
/// </summary>
[HideInInspector] public INDIVIDUALTRANSITIONTYPEC transitionType;
[HideInInspector] public bool unfolded = false;
bool isLoading = false;
bool isSaving = false;
/// <summary>
/// Determines whether this PlaygroundSave is loading.
/// </summary>
/// <returns><c>true</c> if this PlaygroundSave is loading; otherwise, <c>false</c>.</returns>
public bool IsLoading () {
return isLoading;
}
/// <summary>
/// Determines whether this PlaygroundSave is saving.
/// </summary>
/// <returns><c>true</c> if this PlaygroundSave is saving; otherwise, <c>false</c>.</returns>
public bool IsSaving () {
return isSaving;
}
/// <summary>
/// Loads the data from this PlaygroundSave.
/// </summary>
/// <param name="loadTo">Load to.</param>
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);
}
/// <summary>
/// Saves the data into this PlaygroundSave.
/// </summary>
/// <param name="playgroundParticles">Playground particles.</param>
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;
}
/// <summary>
/// Saves the data into this PlaygroundSave asynchronously.
/// </summary>
/// <param name="playgroundParticles">Playground particles.</param>
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;
});
}
/// <summary>
/// Returns a copy of this PlaygroundSave.
/// </summary>
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;
}
}
/// <summary>
/// Holds information for a PlaygroundEventParticle. The Playground Event Particle contains detailed data upon an event and is sent towards any event listeners.
/// </summary>
[Serializable]
public class PlaygroundEventParticle {
/// <summary>
/// The initial size of this particle.
/// </summary>
[HideInInspector] public float initialSize;
/// <summary>
/// The lifetime size of this particle.
/// </summary>
[HideInInspector] public float size;
/// <summary>
/// The rotation of this particle.
/// </summary>
[HideInInspector] public float rotation;
/// <summary>
/// The lifetime of this particle.
/// </summary>
[HideInInspector] public float life;
/// <summary>
/// The total time this particle will live. This will take lifetime subtraction into account.
/// </summary>
[HideInInspector] public float totalLifetime;
/// <summary>
/// The time of birth for this particle.
/// </summary>
[HideInInspector] public float birth;
/// <summary>
/// The delayed time of birth when emission has changed.
/// </summary>
[HideInInspector] public float birthDelay;
/// <summary>
/// The time of death for this particle.
/// </summary>
[HideInInspector] public float death;
/// <summary>
/// The emission for this particle (controlled by emission rate).
/// </summary>
[HideInInspector] public bool emission;
/// <summary>
/// Determines if this particle should rebirth.
/// </summary>
[HideInInspector] public bool rebirth;
/// <summary>
/// The offset in birth-death (sorting).
/// </summary>
[HideInInspector] public float lifetimeOffset;
/// <summary>
/// The velocity of this particle.
/// </summary>
[HideInInspector] public Vector3 velocity;
/// <summary>
/// The initial velocity of this particle.
/// </summary>
[HideInInspector] public Vector3 initialVelocity;
/// <summary>
/// The initial local velocity of this particle.
/// </summary>
[HideInInspector] public Vector3 initialLocalVelocity;
/// <summary>
/// The position of this particle.
/// </summary>
[HideInInspector] public Vector3 position;
/// <summary>
/// The source position at birth for this particle.
/// </summary>
[HideInInspector] public Vector3 targetPosition;
/// <summary>
/// The source direction at birth for this particle.
/// </summary>
[HideInInspector] public Vector3 targetDirection;
/// <summary>
/// The previous source position for this particle (used to calculate delta movement).
/// </summary>
[HideInInspector] public Vector3 previousTargetPosition;
/// <summary>
/// The previous calculated frame's particle position.
/// </summary>
[HideInInspector] public Vector3 previousParticlePosition;
/// <summary>
/// The calculated particle position for collision.
/// </summary>
[HideInInspector] public Vector3 collisionParticlePosition;
/// <summary>
/// The delta to compensate for moving particles in local space.
/// </summary>
[HideInInspector] public Vector3 localSpaceMovementCompensation;
/// <summary>
/// The scattered position to apply on this particle birth.
/// </summary>
[HideInInspector] public Vector3 scatterPosition;
/// <summary>
/// The initial rotation of this particle.
/// </summary>
[HideInInspector] public float initialRotation;
/// <summary>
/// The rotation speed of this particle.
/// </summary>
[HideInInspector] public float rotationSpeed;
/// <summary>
/// The current color of this particle.
/// </summary>
[HideInInspector] public Color32 color;
/// <summary>
/// The color set from script of this particle.
/// </summary>
[HideInInspector] public Color32 scriptedColor;
/// <summary>
/// The set source color.
/// </summary>
[HideInInspector] public Color32 initialColor;
/// <summary>
/// The color gradient for this particle if Color Source is set to LifetimeColors.
/// </summary>
[HideInInspector] public int lifetimeColorId;
/// <summary>
/// The particle does not respond to forces during its lifetime.
/// </summary>
[HideInInspector] public bool noForce;
/// <summary>
/// The particle is non birthed.
/// </summary>
[HideInInspector] public bool isNonBirthed;
/// <summary>
/// The particle is in its first loop.
/// </summary>
[HideInInspector] public bool isFirstLoop;
/// <summary>
/// The id of this particle.
/// </summary>
[HideInInspector] public int particleId;
/// <summary>
/// The id of the particle system this particle belongs to (list position in Playground Manager).
/// </summary>
[HideInInspector] public int particleSystemId;
/// <summary>
/// The id of the last manipulator affecting this particle.
/// </summary>
[HideInInspector] public int manipulatorId;
/// <summary>
/// The interaction with property manipulators of this particle.
/// </summary>
[HideInInspector] public bool changedByProperty;
/// <summary>
/// The interaction with property manipulators that change color of this particle.
/// </summary>
[HideInInspector] public bool changedByPropertyColor;
/// <summary>
/// The interaction with property manipulators that change color over time of this particle.
/// </summary>
[HideInInspector] public bool changedByPropertyColorLerp;
/// <summary>
/// The interaction with property manipulators that change color and wants to keep alpha.
/// </summary>
[HideInInspector] public bool changedByPropertyColorKeepAlpha;
/// <summary>
/// The interaction with property manipulators that change size of this particle.
/// </summary>
[HideInInspector] public bool changedByPropertySize;
/// <summary>
/// The interaction with property manipulators that change target of this particle.
/// </summary>
[HideInInspector] public bool changedByPropertyTarget;
/// <summary>
/// The interaction with death manipulators that forces a particle to a sooner end.
/// </summary>
[HideInInspector] public bool changedByPropertyDeath;
/// <summary>
/// The property target pointer for this particle.
/// </summary>
[HideInInspector] public int propertyTarget;
/// <summary>
/// The property target id for this particle (pairing a particle's target to a manipulator).
/// </summary>
[HideInInspector] public int propertyId;
/// <summary>
/// The property color id for this particles (pairing a particle's color to a manipulator).
/// </summary>
[HideInInspector] public int propertyColorId;
/// <summary>
/// The manipulator id to exclude to not affect this particle.
/// </summary>
[HideInInspector] public int excludeFromManipulatorId;
/// <summary>
/// Is this particle masked?
/// </summary>
[HideInInspector] public bool isMasked;
/// <summary>
/// The alpha of this masked particle.
/// </summary>
[HideInInspector] public float maskAlpha;
/// <summary>
/// The collision transform of this collided particle.
/// </summary>
[HideInInspector] public Transform collisionTransform;
/// <summary>
/// The collision collider of this collided particle (3d).
/// </summary>
[HideInInspector] public Collider collisionCollider;
/// <summary>
/// The collision collider of this collided particle (2d).
/// </summary>
[HideInInspector] public Collider2D collisionCollider2D;
/// <summary>
/// Copies this PlaygroundEventParticle.
/// </summary>
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;
}
}
/// <summary>
/// The Playground Cache contains all data for particles in built-in arrays.
/// </summary>
[Serializable]
public class PlaygroundCache {
/// <summary>
/// The initial size of each particle.
/// </summary>
[HideInInspector] public float[] initialSize;
/// <summary>
/// The lifetime size of each particle.
/// </summary>
[HideInInspector] public float[] size;
/// <summary>
/// The rotation of each particle.
/// </summary>
[HideInInspector] public float[] rotation;
/// <summary>
/// The current lifetime of each particle.
/// </summary>
[HideInInspector] public float[] life;
/// <summary>
/// The lifetime subtraction of each particle (applied when using random between two values).
/// </summary>
[HideInInspector] public float[] lifetimeSubtraction;
/// <summary>
/// The time of birth for each particle.
/// </summary>
[HideInInspector] public float[] birth;
/// <summary>
/// The delayed time of birth when emission has changed.
/// </summary>
[HideInInspector] public float[] birthDelay;
/// <summary>
/// The time of death for each particle.
/// </summary>
[HideInInspector] public float[] death;
/// <summary>
/// The emission for each particle (controlled by emission rate).
/// </summary>
[HideInInspector] public bool[] emission;
/// <summary>
/// The rebirth for each particle.
/// </summary>
[HideInInspector] public bool[] rebirth;
/// <summary>
/// The offset in birth-death (sorting).
/// </summary>
[HideInInspector] public float[] lifetimeOffset;
/// <summary>
/// The velocity of each particle in.
/// </summary>
[HideInInspector] public Vector3[] velocity;
/// <summary>
/// The initial velocity of each particle.
/// </summary>
[HideInInspector] public Vector3[] initialVelocity;
/// <summary>
/// The initial local velocity of each particle.
/// </summary>
[HideInInspector] public Vector3[] initialLocalVelocity;
/// <summary>
/// The position of each particle.
/// </summary>
[HideInInspector] public Vector3[] position;
/// <summary>
/// The source position for each particle.
/// </summary>
[HideInInspector] public Vector3[] targetPosition;
/// <summary>
/// The source direction for each particle.
/// </summary>
[HideInInspector] public Vector3[] targetDirection;
/// <summary>
/// The previous source position for each particle (used to calculate delta movement).
/// </summary>
[HideInInspector] public Vector3[] previousTargetPosition;
/// <summary>
/// The previous calculated frame's particle position.
/// </summary>
[HideInInspector] public Vector3[] previousParticlePosition;
/// <summary>
/// The calculated particle position for collision (depending on collision stepper).
/// </summary>
[HideInInspector] public Vector3[] collisionParticlePosition;
/// <summary>
/// The delta to compensate for moving particles in local space.
/// </summary>
[HideInInspector] public Vector3[] localSpaceMovementCompensation;
/// <summary>
/// The scattered position to apply on each particle birth.
/// </summary>
[HideInInspector] public Vector3[] scatterPosition;
/// <summary>
/// The initial rotation of each particle.
/// </summary>
[HideInInspector] public float[] initialRotation;
/// <summary>
/// The rotation speed of each particle.
/// </summary>
[HideInInspector] public float[] rotationSpeed;
/// <summary>
/// The color of each particle.
/// </summary>
[HideInInspector] public Color32[] color;
/// <summary>
/// The color set from script of each particle.
/// </summary>
[HideInInspector] public Color32[] scriptedColor;
/// <summary>
/// The set source color.
/// </summary>
[HideInInspector] public Color32[] initialColor;
/// <summary>
/// The color gradient for each particle if Color Source is set to LifetimeColors.
/// </summary>
[HideInInspector] public int[] lifetimeColorId;
/// <summary>
/// The particle will no longer respond to any forces during its lifetime.
/// </summary>
[HideInInspector] public bool[] noForce;
/// <summary>
/// The particle is not birthed yet.
/// </summary>
[HideInInspector] public bool[] isNonBirthed;
/// <summary>
/// The particle is in its first loop.
/// </summary>
[HideInInspector] public bool[] isFirstLoop;
/// <summary>
/// The particle is set to simulate.
/// </summary>
[HideInInspector] public bool[] simulate;
/// <summary>
/// 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.
/// </summary>
[HideInInspector] public bool[] isCalculatedThisFrame;
// Manipulator specific arrays:
/// <summary>
/// The interaction with property manipulators of each particle.
/// </summary>
[HideInInspector] public bool[] changedByProperty;
/// <summary>
/// The interaction with property manipulators that change color of each particle.
/// </summary>
[HideInInspector] public bool[] changedByPropertyColor;
/// <summary>
/// The interaction with property manipulators that change color over time of each particle.
/// </summary>
[HideInInspector] public bool[] changedByPropertyColorLerp;
/// <summary>
/// The interaction with property manipulators that change color and wants to keep alpha.
/// </summary>
[HideInInspector] public bool[] changedByPropertyColorKeepAlpha;
/// <summary>
/// The interaction with property manipulators that change size of each particle.
/// </summary>
[HideInInspector] public bool[] changedByPropertySize;
/// <summary>
/// The interaction with property manipulators that change target of each particle.
/// </summary>
[HideInInspector] public bool[] changedByPropertyTarget;
/// <summary>
/// The interaction with death manipulators that forces a particle to a sooner end.
/// </summary>
[HideInInspector] public bool[] changedByPropertyDeath;
/// <summary>
/// The property target pointer for each particle.
/// </summary>
[HideInInspector] public int[] propertyTarget;
/// <summary>
/// The property target id for each particle (pairing a particle's target to a manipulator).
/// </summary>
[HideInInspector] public int[] propertyId;
/// <summary>
/// The property color id for each particles (pairing a particle's color to a manipulator.
/// </summary>
[HideInInspector] public int[] propertyColorId;
/// <summary>
/// The id of the last manipulator affecting each particle.
/// </summary>
[HideInInspector] public int[] manipulatorId;
/// <summary>
/// The id of manipulator to not affect each particle.
/// </summary>
[HideInInspector] public int[] excludeFromManipulatorId;
/// <summary>
/// The lifetime loss of each particle.
/// </summary>
[HideInInspector] public float[] lifetimeLoss;
/// <summary>
/// Determines if a particle is masked.
/// </summary>
[HideInInspector] public bool[] isMasked;
/// <summary>
/// The alpha of each masked particle.
/// </summary>
[HideInInspector] public float[] maskAlpha;
/// <summary>
/// The sorting of the masked particles.
/// </summary>
[HideInInspector] public int[] maskSorting;
/// <summary>
/// Copies this PlaygroundCache.
/// </summary>
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;
}
}
/// <summary>
/// The collision cache contains information of all particle collisions.
/// </summary>
[Serializable]
public class CollisionCache {
/// <summary>
/// Determines if a particle has collided with any colliders during its lifetime.
/// </summary>
[HideInInspector] public bool[] hasCollided;
/// <summary>
/// The position in world space a particle collided last. This will not update further during a particle's lifetime if using "sticky" particles.
/// </summary>
[HideInInspector] public Vector3[] collisionPosition;
/// <summary>
/// The collision normal of the surface the particle collided last. This will not update further during a particle's lifetime if using "sticky" particles.
/// </summary>
[HideInInspector] public Vector3[] collisionNormal;
/// <summary>
/// The transform of the object a particle collided with last. This will not update further during a particle's lifetime if using "sticky" particles.
/// </summary>
[HideInInspector] public Transform[] collisionTransform;
[HideInInspector] public GameObject[] collisionGameObject;
/// <summary>
/// The position seen by the collision object's transform based on InverseTransformPoint.
/// </summary>
[HideInInspector] public Vector3[] collisionTransformPosition;
/// <summary>
/// The sticky position of a particle. This contains the position for particles that stick to their collided surface.
/// </summary>
[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];
}
/// <summary>
/// Sets the specified collision position, normal and transform at index.
/// </summary>
/// <param name="index">Index.</param>
/// <param name="position">Position.</param>
/// <param name="normal">Normal.</param>
/// <param name="transform">Transform.</param>
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));
}
/// <summary>
/// UpdateStickyPosition sets the sticky position based on the collision transform for a particle. The sticky position will update automatically when using "sticky" particles.
/// </summary>
/// <param name="index">Index to update.</param>
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;
}
/// <summary>
/// Resets the specified index.
/// </summary>
/// <param name="index">Index.</param>
public void Reset (int index) {
index = index%hasCollided.Length;
hasCollided[index] = false;
}
/// <summary>
/// Clears out all the collisions. Use this if you want to toggle between having sticky and non-sticky behaviors.
/// </summary>
public void ClearCollisions () {
for(int i = 0; i<hasCollided.Length; i++)
hasCollided[i] = false;
}
/// <summary>
/// Copies this CollisionCache.
/// </summary>
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;
}
}
/// <summary>
/// The Simplex Noise algorithm applied in Turbulence.
/// </summary>
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
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
}
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
// c = 1/6.
double x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
double y1 = y0 - j1 + G3;
double z1 = z0 - k1 + G3;
double x2 = x0 - i2 + 2.0*G3; // Offsets for third corner in (x,y,z) coords
double y2 = y0 - j2 + 2.0*G3;
double z2 = z0 - k2 + 2.0*G3;
double x3 = x0 - 1.0 + 3.0*G3; // Offsets for last corner in (x,y,z) coords
double y3 = y0 - 1.0 + 3.0*G3;
double z3 = z0 - 1.0 + 3.0*G3;
// Work out the hashed gradient indices of the four simplex corners
int ii = i & 255;
int jj = j & 255;
int kk = k & 255;
int gi0 = perm[ii+perm[jj+perm[kk]]] % 12;
int gi1 = perm[ii+i1+perm[jj+j1+perm[kk+k1]]] % 12;
int gi2 = perm[ii+i2+perm[jj+j2+perm[kk+k2]]] % 12;
int gi3 = perm[ii+1+perm[jj+1+perm[kk+1]]] % 12;
// Calculate the contribution from the four corners
double t0 = 0.5 - x0*x0 - y0*y0 - z0*z0;
if(t0<0) n0 = 0.0;
else {
t0 *= t0;
n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0);
}
double t1 = 0.6 - x1*x1 - y1*y1 - z1*z1;
if(t1<0) n1 = 0.0;
else {
t1 *= t1;
n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1);
}
double t2 = 0.6 - x2*x2 - y2*y2 - z2*z2;
if(t2<0) n2 = 0.0;
else {
t2 *= t2;
n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2);
}
double t3 = 0.6 - x3*x3 - y3*y3 - z3*z3;
if(t3<0) n3 = 0.0;
else {
t3 *= t3;
n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to stay just inside [-1,1]
return 32.0*(n0 + n1 + n2 + n3);
}
// 4D simplex noise
public double noise(double x, double y, double z, double w) {
// The skewing and unskewing factors are hairy again for the 4D case
double F4 = (Mathf.Sqrt(5.0f)-1.0)/4.0;
double G4 = (5.0-Mathf.Sqrt(5.0f))/20.0;
double n0, n1, n2, n3, n4; // Noise contributions from the five corners
// Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
double s = (x + y + z + w) * F4; // Factor for 4D skewing
int i = fastfloor(x + s);
int j = fastfloor(y + s);
int k = fastfloor(z + s);
int l = fastfloor(w + s);
double t = (i + j + k + l) * G4; // Factor for 4D unskewing
double X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
double Y0 = j - t;
double Z0 = k - t;
double W0 = l - t;
double x0 = x - X0; // The x,y,z,w distances from the cell origin
double y0 = y - Y0;
double z0 = z - Z0;
double w0 = w - W0;
// For the 4D case, the simplex is a 4D shape I won't even try to describe.
// To find out which of the 24 possible simplices we're in, we need to
// determine the magnitude ordering of x0, y0, z0 and w0.
// The method below is a good way of finding the ordering of x,y,z,w and
// then find the correct traversal order for the simplex were in.
// First, six pair-wise comparisons are performed between each possible pair
// of the four coordinates, and the results are used to add up binary bits
// for an integer index.
int c1 = (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<z, y<w and x<w
// impossible. Only the 24 indices which have non-zero entries make any sense.
// We use a thresholding to set the coordinates in turn from the largest magnitude.
// The number 3 in the "simplex" array is at the position of the largest coordinate.
i1 = simplex[c][0]>=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);
}
}
}