Plugins.meta
New file @@ -0,0 +1,9 @@ fileFormatVersion: 2 guid: d463e02633381cd46902c21b8d51f83e folderAsset: yes timeCreated: 1537519875 licenseType: Pro DefaultImporter: userData: assetBundleName: assetBundleVariant: Plugins/Trails.meta
New file @@ -0,0 +1,9 @@ fileFormatVersion: 2 guid: 77d1032b5eb62ce4da86e600bc8884c8 folderAsset: yes timeCreated: 1537519883 licenseType: Pro DefaultImporter: userData: assetBundleName: assetBundleVariant: Plugins/Trails/CircularBuffer.cs
New file @@ -0,0 +1,287 @@ using System; using System.Collections; using System.Collections.Generic; namespace PigeonCoopToolkit.Utillities { public class CircularBuffer<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable { /// <summary> /// Creates a new instance of a <see cref="RingBuffer<T>"/> with a /// specified cache size. // http://florianreischl.blogspot.com/2010/01/generic-c-ringbuffer.html /// </summary> /// <param name="capacity">The maximal count of items to be stored within /// the ring buffer.</param> public CircularBuffer(int capacity) { // validate capacity if (capacity <= 0) throw new ArgumentException("Must be greater than zero", "capacity"); // set capacity and init the cache Capacity = capacity; _buffer = new T[capacity]; } /// <summary> /// the internal buffer /// </summary> T[] _buffer; /// <summary> /// The all-over position within the ring buffer. The position /// increases continously by adding new items to the buffer. This /// value is needed to calculate the current relative position within the /// buffer. /// </summary> int _position; /// <summary> /// The current version of the buffer, this is required for a correct /// exception handling while enumerating over the items of the buffer. /// </summary> long _version; /// <summary> /// Gets or sets an item for a specified position within the ring buffer. /// </summary> /// <param name="index">The position to get or set an item.</param> /// <returns>The fond item at the specified position within the ring buffer. /// </returns> /// <exception cref="IndexOutOfRangeException"></exception> public T this[int index] { get { // validate the index if (index < 0 || index >= Count) throw new IndexOutOfRangeException(); // calculate the relative position within the rolling base array int index2 = (_position - Count + index) % Capacity; return _buffer[index2]; } set { Insert(index, value); } } /// <summary> /// Gets the maximal count of items within the ring buffer. /// </summary> public int Capacity { get; private set; } /// <summary> /// Get the current count of items within the ring buffer. /// </summary> public int Count { get; private set; } /// <summary> /// Adds a new item to the buffer. /// </summary> /// <param name="item">The item to be added to the buffer.</param> public void Add(T item) { // add a new item to the current relative position within the // buffer and increase the position _buffer[_position++ % Capacity] = item; // increase the count if capacity is not yet reached if (Count < Capacity) Count++; // buffer changed; next version _version++; } /// <summary> /// Clears the whole buffer and releases all referenced objects /// currently stored within the buffer. /// </summary> public void Clear() { for (int i = 0; i < Count; i++) _buffer[i] = default(T); _position = 0; Count = 0; _version++; } /// <summary> /// Determines if a specified item is currently present within /// the buffer. /// </summary> /// <param name="item">The item to search for within the current /// buffer.</param> /// <returns>True if the specified item is currently present within /// the buffer; otherwise false.</returns> public bool Contains(T item) { int index = IndexOf(item); return index != -1; } /// <summary> /// Copies the current items within the buffer to a specified array. /// </summary> /// <param name="array">The target array to copy the items of /// the buffer to.</param> /// <param name="arrayIndex">The start position witihn the target /// array to start copying.</param> public void CopyTo(T[] array, int arrayIndex) { for (int i = 0; i < Count; i++) { array[i + arrayIndex] = _buffer[(_position - Count + i) % Capacity]; } } /// <summary> /// Gets an enumerator over the current items within the buffer. /// </summary> /// <returns>An enumerator over the current items within the buffer. /// </returns> public IEnumerator<T> GetEnumerator() { long version = _version; for (int i = 0; i < Count; i++) { if (version != _version) throw new InvalidOperationException("Collection changed"); yield return this[i]; } } /// <summary> /// Gets the position of a specied item within the ring buffer. /// </summary> /// <param name="item">The item to get the current position for.</param> /// <returns>The zero based index of the found item within the /// buffer. If the item was not present within the buffer, this /// method returns -1.</returns> public int IndexOf(T item) { // loop over the current count of items for (int i = 0; i < Count; i++) { // get the item at the relative position within the internal array T item2 = _buffer[(_position - Count + i) % Capacity]; // if both items are null, return true if (null == item && null == item2) return i; // if equal return the position if (item != null && item.Equals(item2)) return i; } // nothing found return -1; } /// <summary> /// Inserts an item at a specified position into the buffer. /// </summary> /// <param name="index">The position within the buffer to add /// the new item.</param> /// <param name="item">The new item to be added to the buffer.</param> /// <exception cref="IndexOutOfRangeException"></exception> /// <remarks> /// If the specified index is equal to the current count of items /// within the buffer, the specified item will be added. /// /// <b>Warning</b> /// Frequent usage of this method might become a bad idea if you are /// working with a large buffer capacity. The insertion of an item /// at a specified position within the buffer causes causes all present /// items below the specified position to be moved one position. /// </remarks> public void Insert(int index, T item) { // validate index if (index < 0 || index > Count) throw new IndexOutOfRangeException(); // add if index equals to count if (index == Count) { Add(item); return; } // get the maximal count of items to be moved int count = Math.Min(Count, Capacity - 1) - index; // get the relative position of the new item within the buffer int index2 = (_position - Count + index) % Capacity; // move all items below the specified position for (int i = index2 + count; i > index2; i--) { int to = i % Capacity; int from = (i - 1) % Capacity; _buffer[to] = _buffer[from]; } // set the new item _buffer[index2] = item; // adjust storage information if (Count < Capacity) { Count++; _position++; } // buffer changed; next version _version++; } /// <summary> /// Removes a specified item from the current buffer. /// </summary> /// <param name="item">The item to be removed.</param> /// <returns>True if the specified item was successfully removed /// from the buffer; otherwise false.</returns> /// <remarks> /// <b>Warning</b> /// Frequent usage of this method might become a bad idea if you are /// working with a large buffer capacity. The removing of an item /// requires a scan of the buffer to get the position of the specified /// item. If the item was found, the deletion requires a move of all /// items stored abouve the found position. /// </remarks> public bool Remove(T item) { // find the position of the specified item int index = IndexOf(item); // item was not found; return false if (index == -1) return false; // remove the item at the specified position RemoveAt(index); return true; } /// <summary> /// Removes an item at a specified position within the buffer. /// </summary> /// <param name="index">The position of the item to be removed.</param> /// <exception cref="IndexOutOfRangeException"></exception> /// <remarks> /// <b>Warning</b> /// Frequent usage of this method might become a bad idea if you are /// working with a large buffer capacity. The deletion requires a move /// of all items stored abouve the found position. /// </remarks> public void RemoveAt(int index) { // validate the index if (index < 0 || index >= Count) throw new IndexOutOfRangeException(); // move all items above the specified position one step // closer to zeri for (int i = index; i < Count - 1; i++) { // get the next relative target position of the item int to = (_position - Count + i) % Capacity; // get the next relative source position of the item int from = (_position - Count + i + 1) % Capacity; // move the item _buffer[to] = _buffer[from]; } // get the relative position of the last item, which becomes empty // after deletion and set the item as empty int last = (_position - 1) % Capacity; _buffer[last] = default(T); // adjust storage information _position--; Count--; // buffer changed; next version _version++; } /// <summary> /// Gets if the buffer is read-only. This method always returns false. /// </summary> bool ICollection<T>.IsReadOnly { get { return false; } } /// <summary> /// See generic implementation of <see cref="GetEnumerator"/>. /// </summary> /// <returns>See generic implementation of <see cref="GetEnumerator"/>. /// </returns> IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } } Plugins/Trails/CircularBuffer.cs.meta
New file @@ -0,0 +1,8 @@ fileFormatVersion: 2 guid: 51a19be087405654c8b4b50c67fa9200 MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: Plugins/Trails/GizmosExtra.cs
New file @@ -0,0 +1,33 @@ using UnityEngine; namespace PigeonCoopToolkit.Utillities { public static class GizmosExtra { public static void GizmosDrawCircle(Vector3 position, Vector3 up, float size, int divisions) { Vector3 offset = (Quaternion.Euler(90,0,0) * (up* size)) ; for (int i = 0; i < divisions; i++) { Vector3 newOffset = Quaternion.AngleAxis(360f / divisions, up) * offset; Gizmos.DrawLine(position + offset, position + newOffset); offset = newOffset; } } public static void GizmosDrawArrow(Vector3 from, Vector3 to, float arrowSize) { Gizmos.DrawLine(from, to); Vector3 dir = to - from; dir = dir.normalized*arrowSize; Gizmos.DrawLine(to, to - Quaternion.Euler(0, 0, 45)*dir); Gizmos.DrawLine(to, to - Quaternion.Euler(0, 0, -45)*dir); } } } Plugins/Trails/GizmosExtra.cs.meta
New file @@ -0,0 +1,8 @@ fileFormatVersion: 2 guid: ac8be081485ba1141bd28334fe9eabec MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: Plugins/Trails/Range.cs
New file @@ -0,0 +1,15 @@ namespace PigeonCoopToolkit.Utillities { [System.Serializable] public class Range { public float Min; public float Max; public bool WithinRange(float value) { return Min <= value && Max >= value; } } } Plugins/Trails/Range.cs.meta
New file @@ -0,0 +1,8 @@ fileFormatVersion: 2 guid: db4c92c149ebf004a83b3776345ca8d4 MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: Plugins/Trails/SmokePlume.cs
New file @@ -0,0 +1,74 @@ using UnityEngine; namespace PigeonCoopToolkit.Effects.Trails { [AddComponentMenu("Pigeon Coop Toolkit/Effects/Smoke Plume")] public class SmokePlume : TrailRenderer_Base { public float TimeBetweenPoints = 0.1f; public Vector3 ConstantForce = Vector3.up * 0.5f; public float RandomForceScale = 0.05f; public int MaxNumberOfPoints = 50; private float _timeSincePoint; protected override void Start() { base.Start(); _timeSincePoint = 0; } protected override void OnStartEmit() { _timeSincePoint = 0; } protected override void Reset() { base.Reset(); TrailData.SizeOverLife = new AnimationCurve(new Keyframe(0, 0), new Keyframe(0.5f, 0.2f), new Keyframe(1, 0.2f)); TrailData.Lifetime = 6f; ConstantForce = Vector3.up*0.5f; TimeBetweenPoints = 0.1f; RandomForceScale = 0.05f; MaxNumberOfPoints = 50; } protected override void Update() { if (_emit) { _timeSincePoint += _noDecay ? 0 : Time.deltaTime; if (_timeSincePoint >= TimeBetweenPoints) { AddPoint(new SmokeTrailPoint(), _t.position); _timeSincePoint = 0; } } base.Update(); } protected override void InitialiseNewPoint(PCTrailPoint newPoint) { ((SmokeTrailPoint)newPoint).RandomVec = Random.onUnitSphere * RandomForceScale; } protected override void UpdateTrail(PCTrail trail, float deltaTime) { if (_noDecay) return; foreach (PCTrailPoint point in trail.Points) { point.Position += ConstantForce * deltaTime; } } protected override int GetMaxNumberOfPoints() { return MaxNumberOfPoints; } } } Plugins/Trails/SmokePlume.cs.meta
New file @@ -0,0 +1,15 @@ fileFormatVersion: 2 guid: b5748bb800508004a81afe3d7526bcb1 labels: - BetterTrails - TrailRenderer - Smoke - Trail - Effects - skidmarks MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 1000 icon: {fileID: 2800000, guid: 9c8d55a9e91d98e4e925cd09f877adc5, type: 3} userData: Plugins/Trails/SmokeTrail.cs
New file @@ -0,0 +1,76 @@ using UnityEngine; namespace PigeonCoopToolkit.Effects.Trails { [AddComponentMenu("Pigeon Coop Toolkit/Effects/Smoke Trail")] public class SmokeTrail : TrailRenderer_Base { public float MinVertexDistance = 0.1f; public int MaxNumberOfPoints = 50; private Vector3 _lastPosition; private float _distanceMoved; public float RandomForceScale = 1; protected override void Start() { base.Start(); _lastPosition = _t.position; } protected override void Update() { if (_emit) { _distanceMoved += Vector3.Distance(_t.position, _lastPosition); if (_distanceMoved != 0 && _distanceMoved >= MinVertexDistance) { AddPoint(new SmokeTrailPoint(), _t.position); _distanceMoved = 0; } _lastPosition = _t.position; } base.Update(); } protected override void OnStartEmit() { _lastPosition = _t.position; _distanceMoved = 0; } protected override void Reset() { base.Reset(); MinVertexDistance = 0.1f; RandomForceScale = 1; } protected override void InitialiseNewPoint(PCTrailPoint newPoint) { ((SmokeTrailPoint)newPoint).RandomVec = Random.onUnitSphere * RandomForceScale; } protected override void OnTranslate(Vector3 t) { _lastPosition += t; } protected override int GetMaxNumberOfPoints() { return MaxNumberOfPoints; } } public class SmokeTrailPoint : PCTrailPoint { public Vector3 RandomVec; public override void Update(float deltaTime) { base.Update(deltaTime); Position += RandomVec * deltaTime; } } } Plugins/Trails/SmokeTrail.cs.meta
New file @@ -0,0 +1,15 @@ fileFormatVersion: 2 guid: 98335f20237e3de42a9f0b6415312e23 labels: - BetterTrails - TrailRenderer - Smoke - Trail - Effects - skidmarks MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 1000 icon: {fileID: 2800000, guid: f99224b2104474d47b91284e88f8af87, type: 3} userData: Plugins/Trails/SmoothTrail.cs
New file @@ -0,0 +1,313 @@ using PigeonCoopToolkit.Utillities; using UnityEngine; namespace PigeonCoopToolkit.Effects.Trails { [AddComponentMenu("Pigeon Coop Toolkit/Effects/Smooth Trail")] public class SmoothTrail : TrailRenderer_Base { public float MinControlPointDistance = 0.1f; public int MaxControlPoints = 15; public int PointsBetweenControlPoints = 4; private Vector3 _lastPosition; private float _distanceMoved; private CircularBuffer<ControlPoint> _controlPoints; private class ControlPoint { public Vector3 p; public Vector3 forward; } protected override void Start() { base.Start(); _lastPosition = _t.position; } protected override void Update() { if (_emit) { _distanceMoved += Vector3.Distance(_t.position, _lastPosition); if (!Mathf.Approximately(_distanceMoved, 0) && _distanceMoved >= MinControlPointDistance) { AddControlPoint(_t.position); _distanceMoved = 0; } else { _controlPoints[_controlPoints.Count - 1].p = _t.position; if (TrailData.UseForwardOverride) { _controlPoints[_controlPoints.Count - 1].forward = TrailData.ForwardOverrideRelative ? _t.TransformDirection(TrailData.ForwardOverride.normalized) : TrailData.ForwardOverride.normalized; } } _lastPosition = _t.position; } base.Update(); } /* void OnDrawGizmos() { if (Application.isPlaying && _controlPoints != null) { for (int i = 0; i < _controlPoints.Count; i++) { Gizmos.color = Color.red; Gizmos.DrawSphere(_controlPoints[i].p, 0.01f); if (i < _controlPoints.Count - 1) { Vector3 Handle1, Handle2; float distanceBetween = Vector3.Distance(_controlPoints[i].p, _controlPoints[i + 1].p) / 2; if (i == 0) { Handle1 = _controlPoints[i].p + (_controlPoints[i + 1].p - _controlPoints[i].p).normalized * distanceBetween; } else { Handle1 = _controlPoints[i].p + (_controlPoints[i + 1].p - _controlPoints[i - 1].p).normalized * distanceBetween; } int nextI = i + 1; if (nextI == _controlPoints.Count - 1) { Handle2 = _controlPoints[nextI].p + (_controlPoints[nextI - 1].p - _controlPoints[nextI].p).normalized * distanceBetween; } else { Handle2 = _controlPoints[nextI].p + (_controlPoints[nextI - 1].p - _controlPoints[nextI + 1].p).normalized * distanceBetween; } Gizmos.color = Color.green; Gizmos.DrawSphere(Handle1, 0.01f); Gizmos.DrawLine(_controlPoints[i].p, Handle1); Gizmos.color = Color.blue; Gizmos.DrawSphere(Handle2, 0.01f); Gizmos.DrawLine(_controlPoints[nextI].p, Handle2); Vector3 current = _controlPoints[i].p; for (int pointBetween = 0; pointBetween < PointsBetweenControlPoints; pointBetween++) { Vector3 next = GetPointAlongCurve(_controlPoints[i].p, Handle1, _controlPoints[i + 1].p, Handle2, ((pointBetween + 1) / ((float)PointsBetweenControlPoints + 1f)), 0.3f); Gizmos.DrawLine(current, next); Gizmos.color = Color.yellow; Gizmos.DrawSphere(next, 0.01f); current = next; } Gizmos.color = Color.blue; Gizmos.DrawLine(current, _controlPoints[i + 1].p); } } } } */ protected override void OnStartEmit() { _lastPosition = _t.position; _distanceMoved = 0; _controlPoints = new CircularBuffer<ControlPoint>(MaxControlPoints); _controlPoints.Add(new ControlPoint { p = _lastPosition }); if (TrailData.UseForwardOverride) { _controlPoints[0].forward = TrailData.ForwardOverrideRelative ? _t.TransformDirection(TrailData.ForwardOverride.normalized) : TrailData.ForwardOverride.normalized; } AddPoint(new PCTrailPoint(), _lastPosition); AddControlPoint(_lastPosition); } protected override void UpdateTrail(PCTrail trail, float deltaTime) { if (trail.IsActiveTrail == false) return; //TODO Must optimize this further, I don't need to keep recalculating point positions unless we are //dealng with the points between the last and 2nd last CP (+ the last CP itself) int trailPointIndex = 0; for (int i = 0; i < _controlPoints.Count; i++) { trail.Points[trailPointIndex].Position = _controlPoints[i].p; if (TrailData.UseForwardOverride) { trail.Points[trailPointIndex].Forward = _controlPoints[i].forward; } trailPointIndex++; if (i < _controlPoints.Count - 1) { Vector3 Handle1, Handle2; float distanceBetween = Vector3.Distance(_controlPoints[i].p, _controlPoints[i + 1].p) / 2; if (i == 0) { Handle1 = _controlPoints[i].p + (_controlPoints[i + 1].p - _controlPoints[i].p).normalized * distanceBetween; } else { Handle1 = _controlPoints[i].p + (_controlPoints[i + 1].p - _controlPoints[i - 1].p).normalized * distanceBetween; } int nextI = i + 1; if (nextI == _controlPoints.Count - 1) { Handle2 = _controlPoints[nextI].p + (_controlPoints[nextI - 1].p - _controlPoints[nextI].p).normalized * distanceBetween; } else { Handle2 = _controlPoints[nextI].p + (_controlPoints[nextI - 1].p - _controlPoints[nextI + 1].p).normalized * distanceBetween; } PCTrailPoint currentHandle = trail.Points[trailPointIndex-1]; PCTrailPoint nextHandle = trail.Points[(trailPointIndex-1) + PointsBetweenControlPoints+1]; for (int pointBetween = 0; pointBetween < PointsBetweenControlPoints; pointBetween++) { float t= (((float)pointBetween + 1f) / ((float)PointsBetweenControlPoints + 1f)); trail.Points[trailPointIndex].Position = GetPointAlongCurve(_controlPoints[i].p, Handle1, _controlPoints[i + 1].p, Handle2, t, 0.3f); trail.Points[trailPointIndex].SetTimeActive(Mathf.Lerp(currentHandle.TimeActive(), nextHandle.TimeActive(), t)); if (TrailData.UseForwardOverride) { trail.Points[trailPointIndex].Forward = Vector3.Lerp(currentHandle.Forward, nextHandle.Forward, t); } trailPointIndex++; } } } int lastControlPointPointIndex = ((_controlPoints.Count - 1) + ((_controlPoints.Count - 1) * PointsBetweenControlPoints)); int prevControlPointPointIndex = lastControlPointPointIndex - PointsBetweenControlPoints - 1; int activePointCount = lastControlPointPointIndex + 1; /* * * This is that optimisation mentioned above..sort of works, but smoothing isn't working right so i am leaving it out for now * * int lastControlPointIndex = _controlPoints.Count - 1; int prevControlPointIndex = _controlPoints.Count - 2; Vector3 Handle1, Handle2; float distanceBetween = Vector3.Distance(_controlPoints[lastControlPointIndex].p, _controlPoints[prevControlPointIndex].p) / 2; if (prevControlPointIndex == 0) Handle1 = _controlPoints[prevControlPointIndex].p + (_controlPoints[lastControlPointIndex].p - _controlPoints[prevControlPointIndex].p).normalized * distanceBetween; else Handle1 = _controlPoints[prevControlPointIndex].p + (_controlPoints[lastControlPointIndex].p - _controlPoints[prevControlPointIndex - 1].p).normalized * distanceBetween; Handle2 = _controlPoints[lastControlPointIndex].p + (_controlPoints[prevControlPointIndex].p - _controlPoints[lastControlPointIndex].p).normalized * distanceBetween; float timeActiveLastControlPoint = trail.Points[lastControlPointPointIndex].TimeActive(); float timeActivePrevControlPoint = trail.Points[prevControlPointPointIndex].TimeActive(); for (int pointBetween = 0; pointBetween < PointsBetweenControlPoints; pointBetween++) { float t= (((float)pointBetween + 1f) / ((float)PointsBetweenControlPoints + 1f)); trail.Points[prevControlPointPointIndex + pointBetween + 1].Position = GetPointAlongCurve(_controlPoints[prevControlPointIndex].p, Handle1, _controlPoints[lastControlPointIndex].p, Handle2, t, 0.3f); trail.Points[prevControlPointPointIndex + pointBetween + 1].SetTimeActive(Mathf.Lerp(timeActivePrevControlPoint, timeActiveLastControlPoint, t)); } trail.Points[lastControlPointPointIndex].Position = _controlPoints[lastControlPointIndex].p; */ float distanceFromStart = trail.Points[prevControlPointPointIndex].GetDistanceFromStart(); for (int i = prevControlPointPointIndex + 1; i < activePointCount; i++) { distanceFromStart += Vector3.Distance(trail.Points[i - 1].Position, trail.Points[i].Position); trail.Points[i].SetDistanceFromStart(distanceFromStart); } } protected override void Reset() { base.Reset(); MinControlPointDistance = 0.1f; MaxControlPoints = 15; PointsBetweenControlPoints = 4; } protected override void OnTranslate(Vector3 t) { _lastPosition += t; for (int i = 0; i < _controlPoints.Count; i++) _controlPoints[i].p += t; } private void AddControlPoint(Vector3 position) { for (int i = 0; i < PointsBetweenControlPoints; i++) { AddPoint(new PCTrailPoint(), position); } AddPoint(new PCTrailPoint(), position); ControlPoint newCP = new ControlPoint { p = position }; if (TrailData.UseForwardOverride) { newCP.forward = TrailData.ForwardOverrideRelative ? _t.TransformDirection(TrailData.ForwardOverride.normalized) : TrailData.ForwardOverride.normalized; } _controlPoints.Add(newCP); } protected override int GetMaxNumberOfPoints() { return ((MaxControlPoints) + ((MaxControlPoints) * PointsBetweenControlPoints)); } public Vector3 GetPointAlongCurve(Vector3 curveStart, Vector3 curveStartHandle, Vector3 curveEnd, Vector3 curveEndHandle, float t, float crease) { float oneMinT = 1 - t; float oneMinTPow3 = Mathf.Pow(oneMinT, 3); float oneMinTPow2 = Mathf.Pow(oneMinT, 2); float oneMinCrease = 1 - crease; return ((oneMinTPow3 * curveStart * oneMinCrease) + (3 * oneMinTPow2 * t * curveStartHandle * crease) + (3 * oneMinT * Mathf.Pow(t, 2) * curveEndHandle * crease) + (Mathf.Pow(t, 3) * curveEnd * oneMinCrease)) / ((oneMinTPow3 * oneMinCrease) + (3 * oneMinTPow2 * t * crease) + (3 * oneMinT * Mathf.Pow(t, 2) * crease) + (Mathf.Pow(t, 3) * oneMinCrease)); } } } Plugins/Trails/SmoothTrail.cs.meta
New file @@ -0,0 +1,15 @@ fileFormatVersion: 2 guid: 62cd7e0b3a08cf84e82658d9a3eaeb32 labels: - BetterTrails - TrailRenderer - Smoke - Trail - Effects - skidmarks MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 1000 icon: {fileID: 2800000, guid: 8d56c6f37ee95884baa3b4f7e4837399, type: 3} userData: Plugins/Trails/Trail.cs
New file @@ -0,0 +1,60 @@ using UnityEngine; namespace PigeonCoopToolkit.Effects.Trails { [AddComponentMenu("Pigeon Coop Toolkit/Effects/Trail")] public class Trail : TrailRenderer_Base { public float MinVertexDistance = 0.1f; public int MaxNumberOfPoints = 50; private Vector3 _lastPosition; private float _distanceMoved; protected override void Start() { base.Start(); _lastPosition = _t.position; } protected override void Update() { if(_emit) { _distanceMoved += Vector3.Distance(_t.position, _lastPosition); if (_distanceMoved != 0 && _distanceMoved >= MinVertexDistance) { AddPoint(new PCTrailPoint(), _t.position); _distanceMoved = 0; } _lastPosition = _t.position; } base.Update(); } protected override void OnStartEmit() { _lastPosition = _t.position; _distanceMoved = 0; } protected override void Reset() { base.Reset(); MinVertexDistance = 0.1f; } protected override void OnTranslate(Vector3 t) { _lastPosition += t; } protected override int GetMaxNumberOfPoints() { return MaxNumberOfPoints; } } } Plugins/Trails/Trail.cs.meta
New file @@ -0,0 +1,15 @@ fileFormatVersion: 2 guid: 5f742ccbd25717e438dd6fdea4d66bad labels: - BetterTrails - TrailRenderer - Smoke - Trail - Effects - skidmarks MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 1000 icon: {fileID: 2800000, guid: 888165c9c85ad6b429824b89f1f4f698, type: 3} userData: Plugins/Trails/TrailRenderer_Base.cs
New file @@ -0,0 +1,666 @@ using System.Collections.Generic; using System.Linq; using PigeonCoopToolkit.Utillities; using UnityEngine; using System; namespace PigeonCoopToolkit.Effects.Trails { public abstract class TrailRenderer_Base : MonoBehaviour { public PCTrailRendererData TrailData; public bool Emit = false; protected bool _emit; protected bool _noDecay; private PCTrail _activeTrail; private List<PCTrail> _fadingTrails; protected Transform _t; private static Dictionary<Material, List<PCTrail>> _matToTrailList; private static List<Mesh> _toClean; private static bool _hasRenderer = false; private static int GlobalTrailRendererCount = 0; protected virtual void Awake() { GlobalTrailRendererCount++; if(GlobalTrailRendererCount == 1) { _matToTrailList = new Dictionary<Material, List<PCTrail>>(); _toClean = new List<Mesh>(); } _fadingTrails = new List<PCTrail>(); _t = transform; _emit = Emit; if (_emit) { _activeTrail = new PCTrail(GetMaxNumberOfPoints()); _activeTrail.IsActiveTrail = true; OnStartEmit(); } } protected virtual void Start() { } protected virtual void LateUpdate() { if(_hasRenderer) return; _hasRenderer = true; foreach (KeyValuePair<Material, List<PCTrail>> keyValuePair in _matToTrailList) { CombineInstance[] combineInstances = new CombineInstance[keyValuePair.Value.Count]; for (int i = 0; i < keyValuePair.Value.Count; i++) { combineInstances[i] = new CombineInstance { mesh = keyValuePair.Value[i].Mesh, subMeshIndex = 0, transform = Matrix4x4.identity }; } Mesh combinedMesh = new Mesh(); combinedMesh.CombineMeshes(combineInstances, true, false); _toClean.Add(combinedMesh); DrawMesh(combinedMesh, keyValuePair.Key); keyValuePair.Value.Clear(); } } protected virtual void Update() { if (_hasRenderer) { _hasRenderer = false; if (_toClean.Count > 0) { foreach (Mesh mesh in _toClean) { if (Application.isEditor) DestroyImmediate(mesh, true); else Destroy(mesh); } } _toClean.Clear(); } if (_matToTrailList.ContainsKey(TrailData.TrailMaterial) == false) { _matToTrailList.Add(TrailData.TrailMaterial, new List<PCTrail>()); } if(_activeTrail != null) { UpdatePoints(_activeTrail, Time.deltaTime); UpdateTrail(_activeTrail, Time.deltaTime); GenerateMesh(_activeTrail); _matToTrailList[TrailData.TrailMaterial].Add(_activeTrail); } for (int i = _fadingTrails.Count-1; i >= 0; i--) { if (_fadingTrails[i] == null || _fadingTrails[i].Points.Any(a => a.TimeActive() < TrailData.Lifetime) == false) { if (_fadingTrails[i] != null) _fadingTrails[i].Dispose(); _fadingTrails.RemoveAt(i); continue; } UpdatePoints(_fadingTrails[i], Time.deltaTime); UpdateTrail(_fadingTrails[i], Time.deltaTime); GenerateMesh(_fadingTrails[i]); _matToTrailList[TrailData.TrailMaterial].Add(_fadingTrails[i]); } CheckEmitChange(); } protected virtual void OnDestroy() { GlobalTrailRendererCount--; if(GlobalTrailRendererCount == 0) { if(_toClean != null && _toClean.Count > 0) { foreach (Mesh mesh in _toClean) { if (Application.isEditor) DestroyImmediate(mesh, true); else Destroy(mesh); } } _toClean = null; _matToTrailList.Clear(); _matToTrailList = null; } if (_activeTrail != null) { _activeTrail.Dispose(); _activeTrail = null; } if (_fadingTrails != null) { foreach (PCTrail fadingTrail in _fadingTrails) { if (fadingTrail != null) fadingTrail.Dispose(); } _fadingTrails.Clear(); } } protected virtual void OnStopEmit() { } protected virtual void OnStartEmit() { } protected virtual void OnTranslate(Vector3 t) { } protected abstract int GetMaxNumberOfPoints(); protected virtual void Reset() { if(TrailData == null) TrailData = new PCTrailRendererData(); TrailData.Lifetime = 1; TrailData.UsingSimpleColor = false; TrailData.UsingSimpleSize = false; TrailData.ColorOverLife = new Gradient(); TrailData.SimpleColorOverLifeStart = Color.white; TrailData.SimpleColorOverLifeEnd = new Color(1, 1, 1, 0); TrailData.SizeOverLife = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1, 0)); TrailData.SimpleSizeOverLifeStart = 1; TrailData.SimpleSizeOverLifeEnd = 0; } protected virtual void InitialiseNewPoint(PCTrailPoint newPoint) { } protected virtual void UpdateTrail(PCTrail trail, float deltaTime) { } protected void AddPoint(PCTrailPoint newPoint, Vector3 pos) { if (_activeTrail == null) return; newPoint.Position = pos; newPoint.PointNumber = _activeTrail.Points.Count == 0 ? 0 : _activeTrail.Points[_activeTrail.Points.Count - 1].PointNumber + 1; InitialiseNewPoint(newPoint); newPoint.SetDistanceFromStart(_activeTrail.Points.Count == 0 ? 0 : _activeTrail.Points[_activeTrail.Points.Count - 1].GetDistanceFromStart() + Vector3.Distance(_activeTrail.Points[_activeTrail.Points.Count - 1].Position, pos)); if(TrailData.UseForwardOverride) { newPoint.Forward = TrailData.ForwardOverrideRelative ? _t.TransformDirection(TrailData.ForwardOverride.normalized) : TrailData.ForwardOverride.normalized; } _activeTrail.Points.Add(newPoint); } private void GenerateMesh(PCTrail trail) { trail.Mesh.Clear(false); Vector3 camForward = Camera.main != null ? Camera.main.transform.forward : Vector3.forward; if(TrailData.UseForwardOverride) { camForward = TrailData.ForwardOverride.normalized; } trail.activePointCount = NumberOfActivePoints(trail); if (trail.activePointCount < 2) return; int vertIndex = 0; for (int i = 0; i < trail.Points.Count; i++) { PCTrailPoint p = trail.Points[i]; float timeAlong = p.TimeActive()/TrailData.Lifetime; if(p.TimeActive() > TrailData.Lifetime) { continue; } if (TrailData.UseForwardOverride && TrailData.ForwardOverrideRelative) camForward = p.Forward; Vector3 cross = Vector3.zero; if (i < trail.Points.Count - 1) { cross = Vector3.Cross((trail.Points[i + 1].Position - p.Position).normalized, camForward). normalized; } else { cross = Vector3.Cross((p.Position - trail.Points[i - 1].Position).normalized, camForward). normalized; } //yuck! lets move these into their own functions some time Color c = TrailData.StretchColorToFit ? (TrailData.UsingSimpleColor ? Color.Lerp(TrailData.SimpleColorOverLifeStart, TrailData.SimpleColorOverLifeEnd, 1 - ((float)vertIndex / (float)trail.activePointCount / 2f)) : TrailData.ColorOverLife.Evaluate(1 - ((float)vertIndex / (float)trail.activePointCount / 2f))) : (TrailData.UsingSimpleColor ? Color.Lerp(TrailData.SimpleColorOverLifeStart,TrailData.SimpleColorOverLifeEnd,timeAlong) : TrailData.ColorOverLife.Evaluate(timeAlong)); float s = TrailData.StretchSizeToFit ? (TrailData.UsingSimpleSize ? Mathf.Lerp(TrailData.SimpleSizeOverLifeStart,TrailData.SimpleSizeOverLifeEnd,1 - ((float)vertIndex / (float)trail.activePointCount / 2f)) : TrailData.SizeOverLife.Evaluate(1 - ((float)vertIndex / (float)trail.activePointCount / 2f))) : (TrailData.UsingSimpleSize ? Mathf.Lerp(TrailData.SimpleSizeOverLifeStart,TrailData.SimpleSizeOverLifeEnd, timeAlong) : TrailData.SizeOverLife.Evaluate(timeAlong)); trail.verticies[vertIndex] = p.Position + cross * s; if(TrailData.MaterialTileLength <= 0) { trail.uvs[vertIndex] = new Vector2((float)vertIndex / (float)trail.activePointCount / 2f, 0); } else { trail.uvs[vertIndex] = new Vector2(p.GetDistanceFromStart() / TrailData.MaterialTileLength, 0); } trail.normals[vertIndex] = camForward; trail.colors[vertIndex] = c; vertIndex++; trail.verticies[vertIndex] = p.Position - cross * s; if (TrailData.MaterialTileLength <= 0) { trail.uvs[vertIndex] = new Vector2((float)vertIndex / (float)trail.activePointCount / 2f, 1); } else { trail.uvs[vertIndex] = new Vector2(p.GetDistanceFromStart() / TrailData.MaterialTileLength, 1); } trail.normals[vertIndex] = camForward; trail.colors[vertIndex] = c; vertIndex++; } Vector2 finalPosition = trail.verticies[vertIndex-1]; for(int i = vertIndex; i < trail.verticies.Length; i++) { trail.verticies[i] = finalPosition; } int indIndex = 0; for (int pointIndex = 0; pointIndex < 2 * (trail.activePointCount - 1); pointIndex++) { if(pointIndex%2==0) { trail.indicies[indIndex] = pointIndex; indIndex++; trail.indicies[indIndex] = pointIndex + 1; indIndex++; trail.indicies[indIndex] = pointIndex + 2; } else { trail.indicies[indIndex] = pointIndex + 2; indIndex++; trail.indicies[indIndex] = pointIndex + 1; indIndex++; trail.indicies[indIndex] = pointIndex; } indIndex++; } int finalIndex = trail.indicies[indIndex-1]; for (int i = indIndex; i < trail.indicies.Length; i++) { trail.indicies[i] = finalIndex; } trail.Mesh.vertices = trail.verticies; trail.Mesh.SetIndices(trail.indicies, MeshTopology.Triangles, 0); trail.Mesh.uv = trail.uvs; trail.Mesh.normals = trail.normals; trail.Mesh.colors = trail.colors; } private void DrawMesh(Mesh trailMesh, Material trailMaterial) { Graphics.DrawMesh(trailMesh, Matrix4x4.identity, trailMaterial, gameObject.layer); } private void UpdatePoints(PCTrail line, float deltaTime) { for (int i = 0; i < line.Points.Count; i++) { line.Points[i].Update(_noDecay ? 0 : deltaTime); } } [Obsolete("UpdatePoint is deprecated, you should instead override UpdateTrail and loop through the individual points yourself (See Smoke or Smoke Plume scripts for how to do this).", true)] protected virtual void UpdatePoint(PCTrailPoint pCTrailPoint, float deltaTime) { } private void CheckEmitChange() { if (_emit != Emit) { _emit = Emit; if (_emit) { _activeTrail = new PCTrail(GetMaxNumberOfPoints()); _activeTrail.IsActiveTrail = true; OnStartEmit(); } else { OnStopEmit(); _activeTrail.IsActiveTrail = false; _fadingTrails.Add(_activeTrail); _activeTrail = null; } } } private int NumberOfActivePoints(PCTrail line) { int count = 0; for (int index = 0; index < line.Points.Count; index++) { if (line.Points[index].TimeActive() < TrailData.Lifetime) count++; } return count; } [UnityEngine.ContextMenu("Toggle inspector size input method")] protected void ToggleSizeInputStyle() { TrailData.UsingSimpleSize = !TrailData.UsingSimpleSize; } [UnityEngine.ContextMenu("Toggle inspector color input method")] protected void ToggleColorInputStyle() { TrailData.UsingSimpleColor = !TrailData.UsingSimpleColor; } public void LifeDecayEnabled(bool enabled) { _noDecay = !enabled; } /// <summary> /// Translates every point in the vector t /// </summary> public void Translate(Vector3 t) { if (_activeTrail != null) { for (int i = 0; i < _activeTrail.Points.Count; i++) { _activeTrail.Points[i].Position += t; } } if (_fadingTrails != null) { foreach (PCTrail fadingTrail in _fadingTrails) { for (int i = 0; i < fadingTrail.Points.Count; i++) { fadingTrail.Points[i].Position += t; } } } OnTranslate(t); } /// <summary> /// Insert a trail into this trail renderer. /// </summary> /// <param name="from">The start position of the trail.</param> /// <param name="to">The end position of the trail.</param> /// <param name="distanceBetweenPoints">Distance between each point on the trail</param> public void CreateTrail(Vector3 from, Vector3 to, float distanceBetweenPoints) { float distanceBetween = Vector3.Distance(from, to); Vector3 dirVector = to - from; dirVector = dirVector.normalized; float currentLength = 0; CircularBuffer<PCTrailPoint> newLine = new CircularBuffer<PCTrailPoint>(GetMaxNumberOfPoints()); int pointNumber = 0; while (currentLength < distanceBetween) { PCTrailPoint newPoint = new PCTrailPoint(); newPoint.PointNumber = pointNumber; newPoint.Position = from + dirVector*currentLength; newLine.Add(newPoint); InitialiseNewPoint(newPoint); pointNumber++; if (distanceBetweenPoints <= 0) break; else currentLength += distanceBetweenPoints; } PCTrailPoint lastPoint = new PCTrailPoint(); lastPoint.PointNumber = pointNumber; lastPoint.Position = to; newLine.Add(lastPoint); InitialiseNewPoint(lastPoint); PCTrail newTrail = new PCTrail(GetMaxNumberOfPoints()); newTrail.Points = newLine; _fadingTrails.Add(newTrail); } /// <summary> /// Clears all active trails from the system. /// </summary> /// <param name="emitState">Desired emit state after clearing</param> public void ClearSystem(bool emitState) { if(_activeTrail != null) { _activeTrail.Dispose(); _activeTrail = null; } if (_fadingTrails != null) { foreach (PCTrail fadingTrail in _fadingTrails) { if (fadingTrail != null) fadingTrail.Dispose(); } _fadingTrails.Clear(); } Emit = emitState; _emit = !emitState; CheckEmitChange(); } /// <summary> /// Get the number of active seperate trail segments. /// </summary> public int NumSegments() { int num = 0; if (_activeTrail != null && NumberOfActivePoints(_activeTrail) != 0) num++; num += _fadingTrails.Count; return num; } } public class PCTrail : System.IDisposable { public CircularBuffer<PCTrailPoint> Points; public Mesh Mesh; public Vector3[] verticies; public Vector3[] normals; public Vector2[] uvs; public Color[] colors; public int[] indicies; public int activePointCount; public bool IsActiveTrail = false; public PCTrail(int numPoints) { Mesh = new Mesh(); Mesh.MarkDynamic(); verticies = new Vector3[2 * numPoints]; normals = new Vector3[2 * numPoints]; uvs = new Vector2[2 * numPoints]; colors = new Color[2 * numPoints]; indicies = new int[2 * (numPoints) * 3]; Points = new CircularBuffer<PCTrailPoint>(numPoints); } #region Implementation of IDisposable public void Dispose() { if(Mesh != null) { if(Application.isEditor) UnityEngine.Object.DestroyImmediate(Mesh, true); else UnityEngine.Object.Destroy(Mesh); } Points.Clear(); Points = null; } #endregion } public class PCTrailPoint { public Vector3 Forward; public Vector3 Position; public int PointNumber; private float _timeActive = 0; private float _distance; public virtual void Update(float deltaTime) { _timeActive += deltaTime; } public float TimeActive() { return _timeActive; } public void SetTimeActive(float time) { _timeActive = time; } public void SetDistanceFromStart(float distance) { _distance = distance; } public float GetDistanceFromStart() { return _distance; } } [System.Serializable] public class PCTrailRendererData { public Material TrailMaterial; public float Lifetime = 1; public bool UsingSimpleSize = false; public float SimpleSizeOverLifeStart; public float SimpleSizeOverLifeEnd; public AnimationCurve SizeOverLife = new AnimationCurve(); public bool UsingSimpleColor = false; public Color SimpleColorOverLifeStart; public Color SimpleColorOverLifeEnd; public Gradient ColorOverLife; public bool StretchSizeToFit; public bool StretchColorToFit; public float MaterialTileLength = 0; public bool UseForwardOverride; public Vector3 ForwardOverride; public bool ForwardOverrideRelative; } } Plugins/Trails/TrailRenderer_Base.cs.meta
New file @@ -0,0 +1,15 @@ fileFormatVersion: 2 guid: 87716fa8801130a4994e129a3b6b2fa6 labels: - BetterTrails - TrailRenderer - Smoke - Trail - Effects - skidmarks MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 1000 icon: {instanceID: 0} userData: Plugins/Trails/VersionInformation.cs
New file @@ -0,0 +1,42 @@ namespace PigeonCoopToolkit.Generic { [System.Serializable] public class VersionInformation { public string Name; public int Major = 1; public int Minor = 0; public int Patch = 0; public VersionInformation(string name, int major, int minor, int patch) { Name = name; Major = major; Minor = minor; Patch = patch; } public override string ToString() { return string.Format("{0} {1}.{2}.{3}", Name, Major, Minor, Patch); } public bool Match(VersionInformation other, bool looseMatch) { if(looseMatch) { return other.Name == Name && other.Major == Major && other.Minor == Minor; } else { return other.Name == Name && other.Major == Major && other.Minor == Minor && other.Patch == Patch; } } } } Plugins/Trails/VersionInformation.cs.meta
New file @@ -0,0 +1,8 @@ fileFormatVersion: 2 guid: 1d58a7e753bea0e4c84779d092563363 MonoImporter: serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: