// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Utilities { /// /// attaches a set of particles to the line /// [RequireComponent(typeof(ParticleSystem))] [AddComponentMenu("Scripts/MRTK/Core/ParticleSystemLineRenderer")] public class ParticleSystemLineRenderer : BaseMixedRealityLineRenderer { private const int GlobalMaxParticles = 2048; [Header("Particle Settings")] [SerializeField] private Material lineMaterial; public Material LineMaterial { get { return lineMaterial; } set { lineMaterial = value; } } [SerializeField] [Range(128, GlobalMaxParticles)] private int maxParticles = GlobalMaxParticles; public int MaxParticles { get { return maxParticles; } set { maxParticles = Mathf.Clamp(value, 128, GlobalMaxParticles); } } [Header("Noise settings")] [SerializeField] private bool particleNoiseOnDisabled = true; public bool ParticleNoiseOnDisabled { get { return particleNoiseOnDisabled; } set { particleNoiseOnDisabled = value; } } [SerializeField] private Vector3 noiseStrength = Vector3.one; public Vector3 NoiseStrength { get { return noiseStrength; } set { noiseStrength = value; } } [SerializeField] private float noiseFrequency = 1.2f; public float NoiseFrequency { get { return noiseFrequency; } set { noiseFrequency = value; } } [Range(1, 10)] [SerializeField] private int noiseOcatives = 3; public int NoiseOcatives { get { return noiseOcatives; } set { noiseOcatives = Mathf.Clamp(value, 1, 10); } } [SerializeField] [Range(-10f, 10f)] private float noiseSpeed = 1f; public float NoiseSpeed { get { return noiseSpeed; } set { noiseSpeed = Mathf.Clamp(value, -10f, 10f); } } [SerializeField] [Range(0.01f, 0.5f)] private float lifetimeAfterDisabled = 0.25f; public float LifetimeAfterDisabled { get { return lifetimeAfterDisabled; } set { lifetimeAfterDisabled = Mathf.Clamp(value, 0.01f, 0.5f); } } [SerializeField] private Gradient decayGradient = new Gradient(); public Gradient DecayGradient { get { return decayGradient; } set { decayGradient = value; } } [SerializeField] [HideInInspector] private ParticleSystem particles; [SerializeField] [HideInInspector] private ParticleSystemRenderer mainParticleRenderer; public ParticleSystemRenderer MainParticleRenderer { get { if (particles == null) { particles = gameObject.EnsureComponent(); } if (mainParticleRenderer == null) { mainParticleRenderer = particles.EnsureComponent(); } return mainParticleRenderer; } set { mainParticleRenderer = value; } } private readonly ParticleSystem.Particle[] mainParticleArray = new ParticleSystem.Particle[GlobalMaxParticles]; private ParticleSystem.NoiseModule mainNoiseModule; private float decayStartTime = 0f; private void OnEnable() { if (particles == null) { particles = gameObject.EnsureComponent(); } mainNoiseModule = particles.noise; ParticleSystem.EmissionModule emission = particles.emission; emission.rateOverTime = new ParticleSystem.MinMaxCurve(0); emission.rateOverDistance = new ParticleSystem.MinMaxCurve(0); emission.enabled = true; ParticleSystem.MainModule main = particles.main; main.loop = false; main.playOnAwake = false; main.maxParticles = Mathf.Min(maxParticles, GlobalMaxParticles); main.simulationSpace = ParticleSystemSimulationSpace.World; ParticleSystem.ShapeModule shape = particles.shape; shape.enabled = false; MainParticleRenderer.sharedMaterial = lineMaterial; MainParticleRenderer.enabled = true; // Initialize our particles for (int i = 0; i < mainParticleArray.Length; i++) { ParticleSystem.Particle particle = mainParticleArray[i]; particle.startColor = Color.white; particle.startSize = 1f; particle.startLifetime = float.MaxValue; particle.remainingLifetime = float.MaxValue; particle.velocity = Vector3.zero; particle.angularVelocity = 0; mainParticleArray[i] = particle; } } /// protected override void UpdateLine() { if (!LineDataSource.enabled) { mainNoiseModule.enabled = particleNoiseOnDisabled; mainNoiseModule.strengthX = noiseStrength.x; mainNoiseModule.strengthY = noiseStrength.y; mainNoiseModule.strengthZ = noiseStrength.z; mainNoiseModule.octaveCount = noiseOcatives; mainNoiseModule.scrollSpeed = noiseSpeed; mainNoiseModule.frequency = noiseFrequency; if (decayStartTime < 0) { decayStartTime = Time.unscaledTime; } } else { mainNoiseModule.enabled = false; decayStartTime = -1; } if (LineDataSource.enabled) { for (int i = 0; i < LineStepCount; i++) { float normalizedDistance = GetNormalizedPointAlongLine(i); ParticleSystem.Particle particle = mainParticleArray[i]; particle.position = LineDataSource.GetPoint(normalizedDistance); particle.startColor = GetColor(normalizedDistance); particle.startSize = GetWidth(normalizedDistance); mainParticleArray[i] = particle; } } else { int numDecayingParticles = particles.GetParticles(mainParticleArray); for (int i = 0; i < numDecayingParticles; i++) { float normalizedDistance = (1f / (LineStepCount - 1)) * i; mainParticleArray[i].startColor = decayGradient.Evaluate((Time.unscaledTime - decayStartTime) / lifetimeAfterDisabled) * GetColor(normalizedDistance); } } particles.SetParticles(mainParticleArray, LineStepCount); } private void OnDisable() { MainParticleRenderer.enabled = false; } } }