Added Particle Playground library, fixed cinema director deprecated code.

This commit is contained in:
Alex.Kirel 2023-07-27 15:38:45 +05:00
parent 06bef12662
commit ae34b2d806
528 changed files with 184639 additions and 54 deletions

View file

@ -17,15 +17,15 @@ namespace CinemaDirector
/// </summary>
void Awake()
{
if (guiTexture == null)
if (GetComponent<GUITexture>() == null)
{
gameObject.transform.position = Vector3.zero;
gameObject.transform.localScale = new Vector3(100, 100, 100);
gameObject.AddComponent<GUITexture>();
guiTexture.enabled = false;
guiTexture.texture = new Texture2D(1, 1);
guiTexture.pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
guiTexture.color = Color.clear;
GetComponent<GUITexture>().enabled = false;
GetComponent<GUITexture>().texture = new Texture2D(1, 1);
GetComponent<GUITexture>().pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
GetComponent<GUITexture>().color = Color.clear;
}
}
@ -34,9 +34,9 @@ namespace CinemaDirector
/// </summary>
public override void Trigger()
{
guiTexture.enabled = true;
guiTexture.pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
guiTexture.color = From;
GetComponent<GUITexture>().enabled = true;
GetComponent<GUITexture>().pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
GetComponent<GUITexture>().color = From;
}
/// <summary>
@ -67,12 +67,12 @@ namespace CinemaDirector
{
if (time >= 0 && time <= Duration)
{
guiTexture.enabled = true;
GetComponent<GUITexture>().enabled = true;
UpdateTime(time, deltaTime);
}
else if (guiTexture.enabled)
else if (GetComponent<GUITexture>().enabled)
{
guiTexture.enabled = false;
GetComponent<GUITexture>().enabled = false;
}
}
@ -81,7 +81,7 @@ namespace CinemaDirector
/// </summary>
public override void End()
{
guiTexture.enabled = false;
GetComponent<GUITexture>().enabled = false;
}
/// <summary>
@ -89,9 +89,9 @@ namespace CinemaDirector
/// </summary>
public override void ReverseEnd()
{
guiTexture.enabled = true;
guiTexture.pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
guiTexture.color = To;
GetComponent<GUITexture>().enabled = true;
GetComponent<GUITexture>().pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
GetComponent<GUITexture>().color = To;
}
/// <summary>
@ -99,9 +99,9 @@ namespace CinemaDirector
/// </summary>
public override void Stop()
{
if (guiTexture != null)
if (GetComponent<GUITexture>() != null)
{
guiTexture.enabled = false;
GetComponent<GUITexture>().enabled = false;
}
}
@ -113,7 +113,7 @@ namespace CinemaDirector
/// <param name="transition">the Lerp transition value</param>
private void FadeToColor(Color from, Color to, float transition)
{
guiTexture.color = Color.Lerp(from, to, transition);
GetComponent<GUITexture>().color = Color.Lerp(from, to, transition);
}
}
}

View file

@ -17,15 +17,15 @@ namespace CinemaDirector
/// </summary>
void Awake()
{
if (guiTexture == null)
if (GetComponent<GUITexture>() == null)
{
gameObject.transform.position = Vector3.zero;
gameObject.transform.localScale = new Vector3(100, 100, 100);
gameObject.AddComponent<GUITexture>();
guiTexture.enabled = false;
guiTexture.texture = new Texture2D(1, 1);
guiTexture.pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
guiTexture.color = Color.clear;
GetComponent<GUITexture>().enabled = false;
GetComponent<GUITexture>().texture = new Texture2D(1, 1);
GetComponent<GUITexture>().pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
GetComponent<GUITexture>().color = Color.clear;
}
}
@ -34,9 +34,9 @@ namespace CinemaDirector
/// </summary>
public override void Trigger()
{
guiTexture.enabled = true;
guiTexture.pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
guiTexture.color = From;
GetComponent<GUITexture>().enabled = true;
GetComponent<GUITexture>().pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
GetComponent<GUITexture>().color = From;
}
/// <summary>
@ -67,12 +67,12 @@ namespace CinemaDirector
{
if (time >= 0 && time <= Duration)
{
guiTexture.enabled = true;
GetComponent<GUITexture>().enabled = true;
UpdateTime(time, deltaTime);
}
else if (guiTexture.enabled)
else if (GetComponent<GUITexture>().enabled)
{
guiTexture.enabled = false;
GetComponent<GUITexture>().enabled = false;
}
}
@ -81,7 +81,7 @@ namespace CinemaDirector
/// </summary>
public override void End()
{
guiTexture.enabled = false;
GetComponent<GUITexture>().enabled = false;
}
/// <summary>
@ -89,9 +89,9 @@ namespace CinemaDirector
/// </summary>
public override void ReverseEnd()
{
guiTexture.enabled = true;
guiTexture.pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
guiTexture.color = To;
GetComponent<GUITexture>().enabled = true;
GetComponent<GUITexture>().pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
GetComponent<GUITexture>().color = To;
}
/// <summary>
@ -99,9 +99,9 @@ namespace CinemaDirector
/// </summary>
public override void Stop()
{
if (guiTexture != null)
if (GetComponent<GUITexture>() != null)
{
guiTexture.enabled = false;
GetComponent<GUITexture>().enabled = false;
}
}
@ -113,7 +113,7 @@ namespace CinemaDirector
/// <param name="transition">the Lerp transition value</param>
private void FadeToColor(Color from, Color to, float transition)
{
guiTexture.color = Color.Lerp(from, to, transition);
GetComponent<GUITexture>().color = Color.Lerp(from, to, transition);
}
}

View file

@ -17,15 +17,15 @@ namespace CinemaDirector
/// </summary>
void Awake()
{
if (guiTexture == null)
if (GetComponent<GUITexture>() == null)
{
gameObject.transform.position = Vector3.zero;
gameObject.transform.localScale = new Vector3(100, 100, 100);
gameObject.AddComponent<GUITexture>();
guiTexture.enabled = false;
guiTexture.texture = new Texture2D(1, 1);
guiTexture.pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
guiTexture.color = Color.clear;
GetComponent<GUITexture>().enabled = false;
GetComponent<GUITexture>().texture = new Texture2D(1, 1);
GetComponent<GUITexture>().pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
GetComponent<GUITexture>().color = Color.clear;
}
}
@ -34,9 +34,9 @@ namespace CinemaDirector
/// </summary>
public override void Trigger()
{
guiTexture.enabled = true;
guiTexture.pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
guiTexture.color = From;
GetComponent<GUITexture>().enabled = true;
GetComponent<GUITexture>().pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
GetComponent<GUITexture>().color = From;
}
/// <summary>
@ -67,12 +67,12 @@ namespace CinemaDirector
{
if (time >= 0 && time <= Duration)
{
guiTexture.enabled = true;
GetComponent<GUITexture>().enabled = true;
UpdateTime(time, deltaTime);
}
else if (guiTexture.enabled)
else if (GetComponent<GUITexture>().enabled)
{
guiTexture.enabled = false;
GetComponent<GUITexture>().enabled = false;
}
}
@ -81,7 +81,7 @@ namespace CinemaDirector
/// </summary>
public override void End()
{
guiTexture.enabled = false;
GetComponent<GUITexture>().enabled = false;
}
/// <summary>
@ -89,9 +89,9 @@ namespace CinemaDirector
/// </summary>
public override void ReverseEnd()
{
guiTexture.enabled = true;
guiTexture.pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
guiTexture.color = To;
GetComponent<GUITexture>().enabled = true;
GetComponent<GUITexture>().pixelInset = new Rect(0f, 0f, Screen.width, Screen.height);
GetComponent<GUITexture>().color = To;
}
/// <summary>
@ -99,9 +99,9 @@ namespace CinemaDirector
/// </summary>
public override void Stop()
{
if (guiTexture != null)
if (GetComponent<GUITexture>() != null)
{
guiTexture.enabled = false;
GetComponent<GUITexture>().enabled = false;
}
}
@ -113,7 +113,7 @@ namespace CinemaDirector
/// <param name="transition">the Lerp transition value</param>
private void FadeToColor(Color from, Color to, float transition)
{
guiTexture.color = Color.Lerp(from, to, transition);
GetComponent<GUITexture>().color = Color.Lerp(from, to, transition);
}
}
}

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 6e4f0fc9ae1da904a83344cd4ecd4adb
folderAsset: yes
timeCreated: 1451984948
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 1c30893a4ccf6094fba8c75c3fce2cae
folderAsset: yes
timeCreated: 1451984948
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,307 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace ParticlePlayground {
public class PlaygroundFollow : MonoBehaviour {
/// <summary>
/// Reference to the particle system.
/// </summary>
public PlaygroundParticlesC particles;
/// <summary>
/// Reference to an existing GameObject. This will be cloned to be used on every particle.
/// </summary>
public GameObject referenceObject;
/// <summary>
/// The lifetime of the followers. Set 0 to follow during each particle's individual lifetime.
/// </summary>
public float followerLifetime = 0;
/// <summary>
/// The size of the cache. Set 0 to automatically set the needed amount.
/// </summary>
public int cacheSize = 0;
/// <summary>
/// Determines if the Playground Followers should broadcast to any event listeners.
/// </summary>
public bool sendEvents = false;
/// <summary>
/// This event occurs when followers are born and sendEvents are set to true.
/// </summary>
public event OnPlaygroundFollower followerEventBirth;
/// <summary>
/// This event occurs when follower dies and sendEvents are set to true.
/// </summary>
public event OnPlaygroundFollower followerEventDeath;
/// <summary>
/// The reference to the trail renderer (if existing)
/// </summary>
TrailRenderer referenceTrailRenderer;
/// <summary>
/// If the follower has a Trail Renderer component, this sets trail time once the follower is active again.
/// </summary>
float trailTime = 0;
/// <summary>
/// The list of active followers.
/// </summary>
List<PlaygroundFollower> followers = new List<PlaygroundFollower>();
/// <summary>
/// As Playground is running in a multithreaded environment we need a queue for instantiation (which cannot be called from a different thread).
/// </summary>
List<PlaygroundFollower> waitingFollowers = new List<PlaygroundFollower>();
PlaygroundFollower[] referenceObjectsCache;
PlaygroundFollower[] queue = new PlaygroundFollower[0];
int cacheIndex = 0;
PlaygroundEventC birthEvent;
PlaygroundEventC deathEvent;
Transform followerParent;
void Start ()
{
if (referenceObject == null || particles == null)
return;
// Create and setup the birth event
birthEvent = PlaygroundC.CreateEvent(particles);
birthEvent.broadcastType = EVENTBROADCASTC.EventListeners;
birthEvent.eventType = EVENTTYPEC.Birth;
// Create and setup the death event
deathEvent = PlaygroundC.CreateEvent(particles);
deathEvent.broadcastType = EVENTBROADCASTC.EventListeners;
deathEvent.eventType = EVENTTYPEC.Death;
// Hook up the event listeners to the delegates
birthEvent.particleEvent += OnParticleDidBirth;
deathEvent.particleEvent += OnParticleDidDie;
// Create a parent for all followers (for Hierarchy convenience)
followerParent = new GameObject("Followers").transform;
followerParent.parent = transform;
// Get the trail renderer (if available) and its time
referenceTrailRenderer = referenceObject.GetComponent<TrailRenderer>();
if (referenceTrailRenderer!=null)
trailTime = referenceTrailRenderer.time;
// Set an extra amount of followers if required (a trail's time will exceed a particle's)
int extra = followerLifetime<=0?
Mathf.CeilToInt(Mathf.Abs (particles.lifetime-trailTime)+(trailTime-particles.lifetime))+2 :
Mathf.CeilToInt(Mathf.Abs (particles.lifetime-followerLifetime)+(followerLifetime-particles.lifetime))+2 ;
if (particles.lifetime<=1f) extra++;
// Create the follower cache (this will be iterated through and reused whenever a particle rebirths)
referenceObjectsCache = new PlaygroundFollower[cacheSize>0? cacheSize : particles.particleCount+Mathf.CeilToInt(particles.particleCount*extra)];
for (int i = 0; i<referenceObjectsCache.Length; i++) {
GameObject clone = (GameObject)Instantiate(referenceObject);
referenceObjectsCache[i] = new PlaygroundFollower(clone.transform, clone, clone.GetComponent<TrailRenderer>(), 0, 0);
referenceObjectsCache[i].transform.parent = followerParent;
if (referenceObjectsCache[i].trailRenderer!=null)
referenceObjectsCache[i].trailRenderer.time = 0;
referenceObjectsCache[i].gameObject.SetActive(false);
}
}
/// <summary>
/// Event listener for particle birth.
/// </summary>
/// <param name="particle">Particle.</param>
void OnParticleDidBirth (PlaygroundEventParticle particle)
{
waitingFollowers.Add (new PlaygroundFollower(null, null, null, followerLifetime<=0? particle.totalLifetime+trailTime : followerLifetime, particle.particleId));
}
/// <summary>
/// Event listener for particle death.
/// </summary>
/// <param name="particle">Particle.</param>
void OnParticleDidDie (PlaygroundEventParticle particle)
{
int followerId = GetFollowerWithId(particle.particleId);
if (followerId<0) return;
followers[followerId].enabled = false;
}
/// <summary>
/// Gets the follower which has the passed in particle identifier.
/// </summary>
/// <returns>The follower with particle identifier.</returns>
/// <param name="particleId">Particle identifier.</param>
int GetFollowerWithId (int particleId)
{
float lowestLife = 999f;
int returnIndex = -1;
for (int i = 0; i<followers.Count; i++)
if (followers[i].particleId==particleId && followers[i].lifetime<lowestLife)
returnIndex = i;
return returnIndex;
}
void Update ()
{
if (waitingFollowers.Count>0)
{
queue = waitingFollowers.ToArray();
}
}
void LateUpdate ()
{
UpdateFollowers();
}
void UpdateFollowers ()
{
// Follow, lifetime, remove
for (int i = 0; i<followers.Count; i++)
{
// Follow particle
if (followers[i].enabled)
followers[i].transform.position = particles.particleCache[followers[i].particleId].position;
// Subtract lifetime
followers[i].lifetime -= Time.deltaTime;
// Remove if no lifetime left
if (followers[i].lifetime<=0) {
RemoveFollower(i);
continue;
}
}
// Add any waiting followers to the live follower list. The waiting list may change during iteration!
if (queue.Length>0)
{
if (queue.Length!=waitingFollowers.Count) return;
int inQueueThisFrame = waitingFollowers.Count;
foreach (PlaygroundFollower wFollower in queue)
{
AddFollower (wFollower, followers.Count-1);
}
if (inQueueThisFrame==waitingFollowers.Count)
waitingFollowers = new List<PlaygroundFollower>();
else waitingFollowers.RemoveRange (0, inQueueThisFrame-1);
queue = new PlaygroundFollower[0];
}
}
void AddFollower (PlaygroundFollower follower, int i)
{
if (follower==null) return;
followers.Add (follower.Clone());
followers[followers.Count-1].enabled = true;
followers[followers.Count-1].gameObject = referenceObjectsCache[cacheIndex].gameObject;
followers[followers.Count-1].gameObject.SetActive(true);
followers[followers.Count-1].transform = referenceObjectsCache[cacheIndex].transform;
followers[followers.Count-1].trailRenderer = referenceObjectsCache[cacheIndex].trailRenderer;
followers[followers.Count-1].particleId = follower.particleId;
followers[followers.Count-1].transform.position = particles.playgroundCache.position[followers[followers.Count-1].particleId];
if (followers[followers.Count-1].trailRenderer!=null)
followers[followers.Count-1].trailRenderer.time = trailTime;
if (sendEvents && followerEventBirth!=null)
followerEventBirth(followers[followers.Count-1]);
NextCacheIndex();
}
void RemoveFollower (int i)
{
if (sendEvents && followerEventDeath!=null)
followerEventDeath(followers[i]);
followers[i].enabled = false;
if (followers[i].trailRenderer!=null)
followers[i].trailRenderer.time = 0;
followers[i].gameObject.SetActive(false);
followers.RemoveAt(i);
}
void NextCacheIndex ()
{
cacheIndex = (cacheIndex+1)%referenceObjectsCache.Length;
}
/// <summary>
/// Gets an active follower at index. This will only return active followers.
/// </summary>
/// <returns>The active follower.</returns>
/// <param name="index">Index.</param>
public PlaygroundFollower GetActiveFollower (int index)
{
index = Mathf.Clamp (index, 0, followers.Count);
return followers[index];
}
/// <summary>
/// Gets a cached follower at index. This can return inactive followers waiting for their turn to be enabled.
/// </summary>
/// <returns>The cached follower.</returns>
/// <param name="index">Index.</param>
public PlaygroundFollower GetCachedFollower (int index)
{
index = Mathf.Clamp (index, 0, referenceObjectsCache.Length);
return referenceObjectsCache[index];
}
/// <summary>
/// Gets the amount of active followers.
/// </summary>
/// <returns>The active followers count.</returns>
public int GetActiveFollowersCount ()
{
return followers.Count;
}
/// <summary>
/// Gets the amount of cached followers.
/// </summary>
/// <returns>The cached followers count.</returns>
public int GetCachedFollowersCount ()
{
return referenceObjectsCache.Length;
}
}
/// <summary>
/// Playground follower class.
/// </summary>
public class PlaygroundFollower {
public bool enabled = true;
public float lifetime;
public Transform transform;
public GameObject gameObject;
public TrailRenderer trailRenderer;
public int particleId;
/// <summary>
/// Initializes a new instance of the <see cref="PlaygroundFollower"/> class.
/// </summary>
/// <param name="setTransform">Transform to reposition.</param>
/// <param name="setLifetime">Start lifetifetime.</param>
/// <param name="setParticleId">Particle identifier to follow.</param>
public PlaygroundFollower (Transform setTransform, GameObject setGameObject, TrailRenderer setTrailRenderer, float setLifetime, int setParticleId)
{
transform = setTransform;
gameObject = setGameObject;
trailRenderer = setTrailRenderer;
lifetime = setLifetime;
particleId = setParticleId;
}
/// <summary>
/// Clones this instance.
/// </summary>
public PlaygroundFollower Clone ()
{
return new PlaygroundFollower (transform, gameObject, trailRenderer, lifetime, particleId);
}
}
/// <summary>
/// Event delegate for sending a PlaygroundFollower to any event listeners.
/// </summary>
public delegate void OnPlaygroundFollower(PlaygroundFollower follower);
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 69a4e0a274d7945b59605c627b7dbf90
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: fca2eae157067c046ae30a5902fcdcc8
folderAsset: yes
timeCreated: 1451984949
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 9d7ebe11a5a45774f9e587f3626d6e5a
folderAsset: yes
timeCreated: 1451984949
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,83 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
using ParticlePlayground;
using ParticlePlaygroundLanguage;
[CustomEditor (typeof(PlaygroundFollow))]
public class PlaygroundFollowInspector : Editor {
PlaygroundFollow follow;
SerializedObject s_follow;
SerializedProperty s_particles;
SerializedProperty s_referenceObject;
SerializedProperty s_followerLifetime;
SerializedProperty s_cacheSize;
SerializedProperty s_sendEvents;
// GUI
public static GUIStyle boxStyle;
public static PlaygroundSettingsC playgroundSettings;
public static PlaygroundLanguageC playgroundLanguage;
public void OnEnable ()
{
follow = target as PlaygroundFollow;
s_follow = new SerializedObject(follow);
s_particles = s_follow.FindProperty("particles");
s_referenceObject = s_follow.FindProperty("referenceObject");
s_followerLifetime = s_follow.FindProperty("followerLifetime");
s_cacheSize = s_follow.FindProperty("cacheSize");
s_sendEvents = s_follow.FindProperty("sendEvents");
playgroundSettings = PlaygroundSettingsC.GetReference();
playgroundLanguage = PlaygroundSettingsC.GetLanguage();
}
public override void OnInspectorGUI ()
{
if (boxStyle==null)
boxStyle = GUI.skin.FindStyle("box");
s_follow.UpdateIfDirtyOrScript();
bool hasParticleSystem = follow.particles != null;
if (!hasParticleSystem)
EditorGUILayout.HelpBox(playgroundLanguage.missingParticleSystemWarning, MessageType.Warning);
EditorGUILayout.BeginVertical (boxStyle);
playgroundSettings.playgroundFollowFoldout = GUILayout.Toggle(playgroundSettings.playgroundFollowFoldout, playgroundLanguage.playgroundFollow, EditorStyles.foldout);
if (playgroundSettings.playgroundFollowFoldout)
{
EditorGUILayout.BeginVertical (boxStyle);
// Followers foldout
int activeFollowers = follow.GetActiveFollowersCount();
if (GUILayout.Button(playgroundLanguage.followers+" ("+(follow.referenceObject==null?playgroundLanguage.unassigned : activeFollowers.ToString())+")", EditorStyles.toolbarDropDown)) playgroundSettings.followFollowersFoldout=!playgroundSettings.followFollowersFoldout;
if (playgroundSettings.followFollowersFoldout)
{
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(s_referenceObject, new GUIContent(playgroundLanguage.referenceObject));
EditorGUILayout.Separator();
}
if (GUILayout.Button(playgroundLanguage.advanced, EditorStyles.toolbarDropDown)) playgroundSettings.followAdvancedFoldout=!playgroundSettings.followAdvancedFoldout;
if (playgroundSettings.followAdvancedFoldout)
{
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(s_particles, new GUIContent(playgroundLanguage.particleSystem, "The particle system to follow within the scene."));
EditorGUILayout.PropertyField(s_followerLifetime, new GUIContent(playgroundLanguage.lifetime, "The lifetime of the followers, when set to 0 the followers will get automatically controlled lifetime based on the particles."));
EditorGUILayout.PropertyField(s_cacheSize, new GUIContent(playgroundLanguage.cacheSize, "The object pool of the followers, when set to 0 the pool will be sized to match their lifetime."));
EditorGUILayout.PropertyField(s_sendEvents, new GUIContent(playgroundLanguage.sendEvents, "Determines if the followers should broadcast events upon birth and death."));
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
s_follow.ApplyModifiedProperties();
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c8afa032381aa42a1a9c9162ea41d166
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 27a1b15eb416264479979c2f2f014038
folderAsset: yes
timeCreated: 1451984948
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,82 @@
using UnityEngine;
using System.Collections;
namespace ParticlePlayground {
/// <summary>
/// The Particle Playground Multi Recorder enables playback and scrubbing of multiple recorded synchronized particle systems.
/// </summary>
public class PlaygroundMultiRecorder : MonoBehaviour {
/// <summary>
/// The Particle Playground Recorders you wish to record/playback from.
/// </summary>
public PlaygroundRecorder[] playgroundRecorders;
public void StartRecording (float frameIntervalInSeconds)
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].StartRecording(frameIntervalInSeconds);
}
public void StartRecording (float recordingLength, float frameIntervalInSeconds)
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].StartRecording(recordingLength, frameIntervalInSeconds);
}
public void RecordOneFrame ()
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].RecordOneFrame();
}
public void InsertOneFrame (int frame)
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].InsertOneFrame(frame);
}
public void StopRecording ()
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].StopRecording();
}
public void ClearRecording ()
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].ClearRecording();
}
public void Play (float speed)
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].Play(speed);
}
public void Play (float fromNormalizedTime, float speed, bool repeat)
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].Play(fromNormalizedTime, speed, repeat);
}
public void Stop ()
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].Stop();
}
public void Pause ()
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].Pause();
}
public void Scrub (float normalizedTime)
{
for (int i = 0; i<playgroundRecorders.Length; i++)
playgroundRecorders[i].Scrub(normalizedTime);
}
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: f58689db6f7e049468c12a0774b78a88
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 862e30d6db16744f0b75c4c2b1a6418c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: fb029d4f11194b9488546682b95a95f5
folderAsset: yes
timeCreated: 1451984949
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 307516dc12f418a49a2fc0045bfc9dde
folderAsset: yes
timeCreated: 1451984949
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,294 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using ParticlePlayground;
using ParticlePlaygroundLanguage;
[CustomEditor (typeof(PlaygroundRecorder))]
public class PlaygroundRecorderInspector : Editor {
// References
PlaygroundRecorder recorder;
// GUI
public static GUIStyle boxStyle;
public static PlaygroundSettingsC playgroundSettings;
public static PlaygroundLanguageC playgroundLanguage;
// Trimming
private bool _inTrimming;
private float _leftTrimPos = 0;
private float _rightTrimPos = 1f;
private bool _leftWasLastMoved = true;
void OnEnable ()
{
// Set references
recorder = target as PlaygroundRecorder;
// Load settings
playgroundSettings = PlaygroundSettingsC.GetReference();
// Load language
playgroundLanguage = PlaygroundSettingsC.GetLanguage();
// Load data
if (recorder.recorderData != null)
recorder.LoadAsync();
else
recorder.recordedFrames = new List<RecordedFrame>();
}
public override void OnInspectorGUI ()
{
if (boxStyle==null)
boxStyle = GUI.skin.FindStyle("box");
bool hasParticleSystem = recorder.playgroundSystem != null;
if (!hasParticleSystem)
EditorGUILayout.HelpBox(playgroundLanguage.missingParticleSystemWarning, MessageType.Warning);
bool hasRecorderData = recorder.recorderData != null;
EditorGUILayout.BeginVertical (boxStyle);
playgroundSettings.playgroundRecorderFoldout = GUILayout.Toggle(playgroundSettings.playgroundRecorderFoldout, playgroundLanguage.playgroundRecorder, EditorStyles.foldout);
if (playgroundSettings.playgroundRecorderFoldout)
{
EditorGUILayout.BeginVertical (boxStyle);
// Playback & Recorder foldout
if (GUILayout.Button(playgroundLanguage.player, EditorStyles.toolbarDropDown)) playgroundSettings.recorderPlaybackFoldout=!playgroundSettings.recorderPlaybackFoldout;
if (playgroundSettings.recorderPlaybackFoldout)
{
EditorGUILayout.Separator();
if (!hasRecorderData)
{
EditorGUILayout.BeginVertical(boxStyle);
EditorGUILayout.HelpBox(playgroundLanguage.missingRecorderDataWarning, MessageType.Warning);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(playgroundLanguage.createNew, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
{
PlaygroundRecorderData newData = CreateNewRecorderDataDialogue();
if (newData!=null)
recorder.recorderData = newData;
}
recorder.recorderData = (PlaygroundRecorderData)EditorGUILayout.ObjectField(recorder.recorderData, typeof(PlaygroundRecorderData), false);
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
}
EditorGUILayout.BeginHorizontal();
GUI.enabled = !recorder.IsRecording() && hasParticleSystem && recorder.HasRecordedFrames();
if (GUILayout.Button (recorder.IsReplaying()? playgroundLanguage.pauseSymbol : playgroundLanguage.playSymbol, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
{
if (recorder.IsReplaying())
recorder.Pause();
else
recorder.Play(recorder.playHead, recorder.playbackSpeed, recorder.loopPlayback);
}
GUI.enabled = hasParticleSystem && recorder.HasRecordedFrames();
if (GUILayout.Button (playgroundLanguage.stopSymbol, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
{
if (recorder.IsInPlayback())
recorder.SetParticleSystemAsCurrentPlayback();
else
{
recorder.StopAndSerialize();
recorder.playHead = 0;
}
}
GUI.enabled = hasParticleSystem;
if (recorder.IsRecording())
GUI.color = Color.red;
GUILayout.Space(4f);
if (GUILayout.Button (playgroundLanguage.recordSymbol, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
{
if (!recorder.IsRecording())
recorder.StartRecording();
else
{
recorder.StopAndSerialize();
recorder.playHead = 0;
}
}
GUI.color = Color.white;
GUI.enabled = recorder.HasRecordedFrames() && hasParticleSystem;
if (GUILayout.Button (playgroundLanguage.clear, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
{
if (EditorUtility.DisplayDialog(playgroundLanguage.clearRecording, playgroundLanguage.clearRecordingMsg, playgroundLanguage.yes, playgroundLanguage.no))
recorder.ClearRecording();
}
GUILayout.FlexibleSpace();
_inTrimming = GUILayout.Toggle (_inTrimming, playgroundLanguage.trim, EditorStyles.toolbarButton);
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
EditorGUILayout.Separator();
GUI.enabled = recorder.HasRecordedFrames();
float currentPlayHead = recorder.playHead;
if (!_inTrimming)
recorder.playHead = EditorGUILayout.Slider(playgroundLanguage.playHeadPosition, recorder.playHead, 0, 1f);
else
{
GUILayout.BeginHorizontal();
EditorGUILayout.Separator();
if (GUILayout.Button (playgroundLanguage.trimOuter, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
{
string framesRemoved = "[0-"+recorder.GetFrameAtTime(_leftTrimPos)+"] - ["+recorder.GetFrameAtTime(_rightTrimPos)+"-"+recorder.FrameCount()+"]";
if (EditorUtility.DisplayDialog(playgroundLanguage.trim, playgroundLanguage.trimMsg+framesRemoved+"?", playgroundLanguage.yes, playgroundLanguage.no))
if (recorder.Trim(_leftTrimPos, _rightTrimPos))
{
_leftTrimPos = 0;
_rightTrimPos = 1f;
}
}
if (GUILayout.Button (playgroundLanguage.trimInner, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
{
string framesRemoved = "["+recorder.GetFrameAtTime(_leftTrimPos)+"-"+recorder.GetFrameAtTime(_rightTrimPos)+"]";
if (EditorUtility.DisplayDialog(playgroundLanguage.trim, playgroundLanguage.trimMsg+framesRemoved+"?", playgroundLanguage.yes, playgroundLanguage.no))
{
recorder.TrimInner(_leftTrimPos, _rightTrimPos);
_leftTrimPos = 0;
_rightTrimPos = 1f;
}
}
GUILayout.EndHorizontal();
float l = _leftTrimPos;
float r = _rightTrimPos;
EditorGUILayout.MinMaxSlider(ref _leftTrimPos, ref _rightTrimPos, 0, 1f);
if (!Mathf.Approximately(_leftTrimPos, l))
_leftWasLastMoved = true;
if (!Mathf.Approximately(_rightTrimPos, r))
_leftWasLastMoved = false;
if (!recorder.IsReplaying())
recorder.playHead = _leftWasLastMoved? _leftTrimPos : _rightTrimPos;
else
_leftWasLastMoved = true;
if (recorder.playHead >= _rightTrimPos && recorder.IsReplaying())
recorder.playHead = _leftTrimPos;
if (recorder.playHead < _leftTrimPos && recorder.IsReplaying() && recorder.playbackSpeed>0)
recorder.playHead = _leftTrimPos;
if (recorder.playHead <= _leftTrimPos && (!_leftWasLastMoved || recorder.IsReplaying() && recorder.playbackSpeed<0))
recorder.playHead = _rightTrimPos;
}
if (currentPlayHead != recorder.playHead)
recorder.Scrub (recorder.playHead);
string playbackStatus = "No Recording";
string playbackData = "";
float recordedSeconds = ((recorder.FrameCount()*1f) * recorder.keyframeInterval) / recorder.playbackSpeed;
if (_inTrimming)
{
playbackStatus = "TRIMMING";
GUI.color = Color.yellow;
}
else if (recorder.IsRecording())
{
playbackStatus = "RECORDING";
GUI.color = Color.red;
}
else if (recorder.IsInPlayback())
{
playbackStatus = "In Playback";
GUI.color = Color.green;
}
else if (!recorder.IsInPlayback() && recorder.HasRecordedFrames())
{
playbackStatus = "Live Particles";
GUI.color = Color.cyan;
}
if (recorder.HasRecordedFrames() && recorder.IsInPlayback())
{
if (_inTrimming)
playbackData = " (Left: " + recorder.GetFrameAtTime(_leftTrimPos) + " | Right: " + recorder.GetFrameAtTime(_rightTrimPos) + ")";
else if (!recorder.IsRecording())
playbackData = " (" + (recordedSeconds*recorder.playHead).ToString("F1") + "/" + recordedSeconds.ToString("F1") + " s)";
else if (!_inTrimming)
playbackData = " (" + (recordedSeconds).ToString("F1") + " s)";
}
PlaybackBar(recorder.HasRecordedFrames()? (recorder.IsRecording()? 1f : recorder.playHead) : 0, playbackStatus + playbackData, Screen.width - 56f);
GUI.color = Color.white;
GUI.backgroundColor = Color.white;
GUILayout.Space (-10f);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Time: " + (recorder.HasRecordedFrames()?(recordedSeconds*recorder.playHead).ToString("F1") + " / " + recordedSeconds.ToString("F1") + " s" + " (" + recorder.playbackSpeed.ToString("F1") + "x)" : "-"), EditorStyles.objectFieldThumb, GUILayout.Width ((Screen.width/2f)-30f));
EditorGUILayout.LabelField("Frame: " + (recorder.HasRecordedFrames()?recorder.GetFrameAtTime(recorder.playHead).ToString() + " / " + recorder.FrameCount() : "-"), EditorStyles.objectFieldThumb, GUILayout.Width ((Screen.width/2f)-30f));
EditorGUILayout.EndHorizontal();
GUI.enabled = true;
EditorGUILayout.Separator();
}
// Advanced foldout
if (GUILayout.Button(playgroundLanguage.advanced, EditorStyles.toolbarDropDown)) playgroundSettings.recorderAdvancedFoldout=!playgroundSettings.recorderAdvancedFoldout;
if (playgroundSettings.recorderAdvancedFoldout)
{
EditorGUILayout.Separator();
recorder.playgroundSystem = (PlaygroundParticlesC)EditorGUILayout.ObjectField(playgroundLanguage.particleSystem, recorder.playgroundSystem, typeof(PlaygroundParticlesC), true);
recorder.recorderData = (PlaygroundRecorderData)EditorGUILayout.ObjectField(playgroundLanguage.recorderData, recorder.recorderData, typeof(PlaygroundRecorderData), false);
recorder.keyframeInterval = EditorGUILayout.FloatField(playgroundLanguage.keyframeInterval, recorder.keyframeInterval);
recorder.playbackSpeed = EditorGUILayout.FloatField(playgroundLanguage.playbackSpeed, recorder.playbackSpeed);
recorder.loopPlayback = GUILayout.Toggle (recorder.loopPlayback, playgroundLanguage.loop);
recorder.fadeIn = GUILayout.Toggle (recorder.fadeIn, playgroundLanguage.fadeIn);
recorder.sizeIn = GUILayout.Toggle (recorder.sizeIn, playgroundLanguage.sizeIn);
recorder.skipInterpolationOnEndFrames = GUILayout.Toggle (recorder.skipInterpolationOnEndFrames, playgroundLanguage.skipInterpolationOnEndFrames);
recorder.localSpaceOnPlayback = GUILayout.Toggle (recorder.localSpaceOnPlayback, playgroundLanguage.setLocalSpaceOnPlayback);
recorder.multithreading = GUILayout.Toggle (recorder.multithreading, playgroundLanguage.multithreading);
EditorGUILayout.Separator();
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
}
public void PlaybackBar (float val, string label, float width) {
Rect rect = GUILayoutUtility.GetRect (18, 18, "TextField");
rect.width = width;
rect.height = 16;
if (val<0) val = 0;
EditorGUI.ProgressBar (rect, val, label);
EditorGUILayout.Space ();
}
public static PlaygroundRecorderData CreateNewRecorderDataDialogue ()
{
string dataPath = EditorUtility.SaveFilePanelInProject(playgroundLanguage.newPlaygroundRecording, "PlaygroundRecording", "asset", playgroundLanguage.newPlaygroundRecordingMsg);
if (dataPath.Length>0)
{
PlaygroundRecorderData newData = PlaygroundRecorderData.New();
AssetDatabase.CreateAsset(newData, dataPath);
AssetDatabase.Refresh();
return newData;
}
return null;
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 18629149a4cb64676be45a3a0031828a
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,40 @@
#if UNITY_WSA && !UNITY_EDITOR
#else
using UnityEngine;
using System;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
namespace ParticlePlayground {
public class PlaygroundCompression
{
public static byte[] SerializeAndCompress(object obj)
{
using (MemoryStream ms = new MemoryStream())
{
using (GZipStream zs = new GZipStream(ms, CompressionMode.Compress, true))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(zs, obj);
}
return ms.ToArray();
}
}
public static T DecompressAndDeserialize<T>(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data))
{
using (GZipStream zs = new GZipStream(ms, CompressionMode.Decompress, true))
{
BinaryFormatter bf = new BinaryFormatter();
return (T) bf.Deserialize(zs);
}
}
}
}
}
#endif

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 9ea72fd6ac8fa4185a40b701662e4bbb
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,60 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using ParticlePlayground;
[Serializable]
public class PlaygroundRecorderData : ScriptableObject
{
[HideInInspector] public float version;
public SerializedFrame[] serializedFrames;
public void Serialize (List<RecordedFrame> recordedFrames)
{
version = PlaygroundC.version;
serializedFrames = null;
serializedFrames = new SerializedFrame[recordedFrames.Count];
for (int i = 0; i<serializedFrames.Length; i++)
serializedFrames[i] = recordedFrames[i].CloneAsSerializedFrame();
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
#endif
}
public void SerializeAsync (List<RecordedFrame> recordedFrames)
{
version = PlaygroundC.version;
PlaygroundC.RunAsync(() => {
serializedFrames = null;
serializedFrames = new SerializedFrame[recordedFrames.Count];
for (int i = 0; i<serializedFrames.Length; i++)
serializedFrames[i] = recordedFrames[i].CloneAsSerializedFrame();
});
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
#endif
}
public List<RecordedFrame> CloneAsRecordedFrames ()
{
if (serializedFrames == null)
return null;
List<RecordedFrame> recordedFrames = new List<RecordedFrame>();
for (int i = 0; i<serializedFrames.Length; i++)
{
recordedFrames.Add(serializedFrames[i].CloneAsRecordedFrame());
}
return recordedFrames;
}
public void Clear ()
{
serializedFrames = null;
}
public static PlaygroundRecorderData New () {
PlaygroundRecorderData newData = ScriptableObject.CreateInstance<PlaygroundRecorderData>();
return newData;
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c8586836ef5a445d4b801f769230e702
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,93 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace ParticlePlayground {
/// <summary>
/// The RecordedFrame class contain information of a single frame of recorded particles for the Playground Recorder's recorded data.
/// </summary>
[Serializable]
public class RecordedFrame {
/// <summary>
/// The array of particle data. PlaybackParticle is a struct and therefore not serialized due to performance.
/// </summary>
[HideInInspector] public PlaybackParticle[] particles;
/// <summary>
/// The time during simulation this frame was made.
/// </summary>
[HideInInspector] public float timeStamp;
/// <summary>
/// The keyframe interval setting when this frame was made.
/// </summary>
[HideInInspector] public float keyframeInterval;
/// <summary>
/// The type of this frame (FrameType.Start, FrameType.Middle or FrameType.End).
/// </summary>
[HideInInspector] public FrameType frameType = FrameType.Middle;
public RecordedFrame () {}
public RecordedFrame (PlaygroundParticlesC playgroundParticles, float keyframeInterval) {
particles = new PlaybackParticle[playgroundParticles.particleCache.Length];
for (int i = 0; i<particles.Length; i++)
{
particles[i] = new PlaybackParticle(
playgroundParticles.playgroundCache.position[i],
playgroundParticles.playgroundCache.velocity[i],
playgroundParticles.playgroundCache.rotation[i],
playgroundParticles.playgroundCache.size[i],
playgroundParticles.particleCache[i].lifetime,
playgroundParticles.particleCache[i].startLifetime,
playgroundParticles.playgroundCache.life[i],
playgroundParticles.playgroundCache.birth[i],
playgroundParticles.playgroundCache.death[i],
playgroundParticles.playgroundCache.lifetimeSubtraction[i],
playgroundParticles.playgroundCache.color[i],
playgroundParticles.playgroundCache.targetPosition[i],
playgroundParticles.playgroundCache.initialSize[i]
);
}
timeStamp = Time.realtimeSinceStartup;
this.keyframeInterval = keyframeInterval;
}
public RecordedFrame Clone ()
{
RecordedFrame recordedFrame = new RecordedFrame();
recordedFrame.particles = (PlaybackParticle[])particles.Clone();
recordedFrame.timeStamp = timeStamp;
recordedFrame.frameType = frameType;
return recordedFrame;
}
public ParticleSystem.Particle[] CloneAsParticles ()
{
ParticleSystem.Particle[] p = new ParticleSystem.Particle[particles.Length];
for (int i = 0; i<p.Length; i++)
{
p[i] = particles[i].CloneAsParticle();
}
return p;
}
public SerializedFrame CloneAsSerializedFrame ()
{
SerializedFrame serializedFrame = new SerializedFrame();
serializedFrame.serializedParticles = CloneAsSerializedParticles();
serializedFrame.keyframeInterval = keyframeInterval;
serializedFrame.timeStamp = timeStamp;
serializedFrame.frameType = frameType;
return serializedFrame;
}
public SerializedParticle[] CloneAsSerializedParticles ()
{
SerializedParticle[] serializedParticles = new SerializedParticle[particles.Length];
for (int i = 0; i<serializedParticles.Length; i++)
serializedParticles[i] = particles[i].CloneAsSerializedParticle();
return serializedParticles;
}
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: b98b0f52a1a5d4b12a4f9ce463c35185
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,49 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace ParticlePlayground {
/// <summary>
/// A serialized frame holds information about one recorded frame of particles.
/// </summary>
[Serializable]
public class SerializedFrame
{
/// <summary>
/// The array of particle data.
/// </summary>
public SerializedParticle[] serializedParticles;
/// <summary>
/// The time during simulation this frame was made.
/// </summary>
public float timeStamp;
/// <summary>
/// The keyframe interval setting when this frame was made.
/// </summary>
public float keyframeInterval;
/// <summary>
/// The type of this frame (FrameType.Start, FrameType.Middle or FrameType.End).
/// </summary>
public FrameType frameType = FrameType.Middle;
public RecordedFrame CloneAsRecordedFrame ()
{
RecordedFrame recordedFrame = new RecordedFrame();
recordedFrame.particles = CloneAsPlaybackParticles();
recordedFrame.keyframeInterval = keyframeInterval;
recordedFrame.timeStamp = timeStamp;
recordedFrame.frameType = frameType;
return recordedFrame;
}
public PlaybackParticle[] CloneAsPlaybackParticles ()
{
PlaybackParticle[] recordedParticles = new PlaybackParticle[serializedParticles.Length];
for (int i = 0; i<serializedParticles.Length; i++)
recordedParticles[i] = serializedParticles[i].CloneAsPlaybackParticle();
return recordedParticles;
}
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d40e09164bba5450ea41104c1cfa3658
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,82 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace ParticlePlayground {
/// <summary>
/// A serialized particle holds information for one single serializable particle inside a recorded frame for the Playground Recorder's recorded data.
/// </summary>
[Serializable]
public class SerializedParticle
{
public Vector3 position;
public Vector3 velocity;
public float rotation;
public float size;
public float lifetime;
public float startLifetime;
public float playgroundLife;
public float playgroundStartLifetime;
public float playgroundEndLifetime;
public float playgroundLifetimeSubtraction;
public Color32 color;
public Vector3 sourcePosition;
public float startingSize;
public SerializedParticle (Vector3 position,
Vector3 velocity,
float rotation,
float size,
float lifetime,
float startLifetime,
float playgroundLife,
float playgroundStartLifetime,
float playgroundEndLifetime,
float playgroundLifetimeSubtraction,
Color32 color,
Vector3 sourcePosition,
float startingSize
)
{
this.position = position;
this.velocity = velocity;
this.rotation = rotation;
this.size = size;
this.lifetime = lifetime;
this.startLifetime = startLifetime;
this.playgroundLife = playgroundLife;
this.playgroundStartLifetime = playgroundStartLifetime;
this.playgroundEndLifetime = playgroundEndLifetime;
this.playgroundLifetimeSubtraction = playgroundLifetimeSubtraction;
this.color = color;
this.sourcePosition = sourcePosition;
this.startingSize = startingSize;
}
public PlaybackParticle CloneAsPlaybackParticle ()
{
PlaybackParticle particle = new PlaybackParticle(
position,
velocity,
rotation,
size,
lifetime,
startLifetime,
playgroundLife,
playgroundStartLifetime,
playgroundEndLifetime,
playgroundLifetimeSubtraction,
color,
sourcePosition,
startingSize
);
return particle;
}
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0ed99384141a34198b4a872162f33568
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: b248ca5b01a7c4140b1506e64e18065e
folderAsset: yes
timeCreated: 1451984948
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: ea1c64bb5c0b49d409932c31a1af37c3
folderAsset: yes
timeCreated: 1451984949
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,520 @@
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using PlaygroundSplines;
using ParticlePlayground;
using ParticlePlaygroundLanguage;
[CustomEditor(typeof(PlaygroundSpline))]
public class PlaygroundSplineInspector : Editor {
private const int stepsPerCurve = 10;
private const float directionScale = 0.5f;
private const float handleSize = 0.04f;
private const float pickSize = 0.06f;
private PlaygroundSpline spline;
private Transform handleTransform;
private Quaternion handleRotation;
private Quaternion bezierHandleRotation;
private int selectedIndex = -1;
private int selectedNode = -1;
private int selectedBezier = -1;
private bool selectedIndexIsNode;
private UnityEditor.Tool lastActiveTool = UnityEditor.Tool.None;
public static PlaygroundSettingsC playgroundSettings;
public static PlaygroundLanguageC playgroundLanguage;
public static GUIStyle boxStyle;
void OnEnable () {
spline = target as PlaygroundSpline;
playgroundSettings = PlaygroundSettingsC.GetReference();
playgroundLanguage = PlaygroundSettingsC.GetLanguage();
lastActiveTool = UnityEditor.Tools.current;
UpdateUserList();
}
void OnDisable () {
UnityEditor.Tools.current = lastActiveTool;
}
void UpdateUserList () {
// Check that any user isn't null
for (int i = 0; i<spline.usedBy.Count; i++) {
if (spline.usedBy[i]==null || !spline.usedBy[i].GetComponent<PlaygroundParticlesC>().splines.Contains(spline)) {
spline.usedBy.RemoveAt (i);
i--;
}
}
}
public override void OnInspectorGUI () {
if (boxStyle==null)
boxStyle = GUI.skin.FindStyle("box");
EditorGUILayout.BeginVertical(boxStyle);
playgroundSettings.playgroundSplineFoldout = GUILayout.Toggle(playgroundSettings.playgroundSplineFoldout, playgroundLanguage.playgroundSpline, EditorStyles.foldout);
if (playgroundSettings.playgroundSplineFoldout) {
selectedNode = (selectedIndex+1)/3;
selectedBezier = selectedIndex<3?0:(((selectedIndex)%3))%2;
selectedIndexIsNode = selectedIndex==0||selectedIndex%3==0;
EditorGUILayout.Separator();
bool currentLoop = spline.Loop;
GUI.enabled = spline.NodeCount>1;
bool loop = EditorGUILayout.Toggle(playgroundLanguage.loop, spline.Loop);
GUI.enabled = true;
spline.reverse = EditorGUILayout.Toggle(playgroundLanguage.reverse, spline.reverse);
spline.timeOffset = EditorGUILayout.Slider (playgroundLanguage.timeOffset, spline.timeOffset, 0, 1f);
spline.positionOffset = EditorGUILayout.Vector3Field (playgroundLanguage.positionOffset, spline.positionOffset);
EditorGUILayout.Separator();
EditorGUILayout.BeginVertical(boxStyle);
if (selectedIndex < spline.ControlPointCount) {
DrawSelectedPointInspector();
}
if (currentLoop!=loop) {
Undo.RecordObject(spline, "Toggle Loop");
EditorUtility.SetDirty(spline);
spline.Loop = loop;
}
EditorGUILayout.EndVertical();
// List of nodes
if (GUILayout.Button(playgroundLanguage.nodes+" ("+(spline.NodeCount+1)+")", EditorStyles.toolbarDropDown)) playgroundSettings.nodesFoldout=!playgroundSettings.nodesFoldout;
if (playgroundSettings.nodesFoldout) {
EditorGUILayout.Separator();
EditorGUILayout.BeginVertical(boxStyle, GUILayout.MinHeight(26));
for (int i = 0; i<=spline.NodeCount; i++) {
if (i==selectedNode && selectedIndex>-1) GUI.backgroundColor = new Color(1f,1f,.2f);
EditorGUILayout.BeginVertical(boxStyle);
GUI.backgroundColor = Color.white;
EditorGUILayout.BeginHorizontal();
// Node
if (i==selectedNode && selectedIndexIsNode)
EditorGUILayout.LabelField("", EditorStyles.foldout, GUILayout.Width(14));
else if (i==selectedNode && selectedIndex!=-1) GUILayout.Space (19f);
if (GUILayout.Button(playgroundLanguage.node+" "+i+" ("+spline.GetControlPointMode(i*3).ToString()+")", EditorStyles.label, GUILayout.MaxWidth(130)))
selectedIndex = SelectIndex(i*3);
GUILayout.Space(3f);
if (GUILayout.Button(spline.transformNodes[i*3].enabled?playgroundLanguage.transform:playgroundLanguage.vector3, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))){
spline.transformNodes[i*3].enabled = !spline.transformNodes[i*3].enabled;
}
EditorGUI.BeginChangeCheck();
EditorGUIUtility.labelWidth = 1f;
Vector3 point;
if (spline.transformNodes[i*3].enabled) {
spline.transformNodes[i*3].transform = (Transform)EditorGUILayout.ObjectField(spline.transformNodes[i*3].transform, typeof(Transform), true);
if (spline.transformNodes[i*3].IsAvailable())
point = spline.transformNodes[i*3].GetPosition();
else point = spline.GetControlPoint(i*3);
} else point = EditorGUILayout.Vector3Field(" ", spline.GetControlPoint(i*3));
EditorGUIUtility.labelWidth = 0;
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(spline, "Move Point");
EditorUtility.SetDirty(spline);
spline.SetControlPoint(i*3, point);
}
if(GUILayout.Button(playgroundLanguage.upSymbol, EditorStyles.toolbarButton, new GUILayoutOption[]{GUILayout.Width(18), GUILayout.Height(16)})){
}
if(GUILayout.Button(playgroundLanguage.downSymbol, EditorStyles.toolbarButton, new GUILayoutOption[]{GUILayout.Width(18), GUILayout.Height(16)})){
}
EditorGUI.BeginChangeCheck();
if (GUILayout.Button("+", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
Undo.RecordObject(spline, "Add Node");
spline.AddNode(i);
EditorUtility.SetDirty(spline);
selectedIndex = SelectIndex((i+1)*3);
}
GUI.enabled = (!spline.Loop&&spline.NodeCount>1||spline.Loop&&spline.NodeCount>2);
EditorGUI.BeginChangeCheck();
if (GUILayout.Button("-", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
Undo.RecordObject(spline, "Remove Node");
spline.RemoveNode(i);
EditorUtility.SetDirty(spline);
selectedIndex-=3;
if (selectedIndex<0)
selectedIndex = SelectIndex(0);
return;
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
if (i==selectedNode && selectedIndex>-1) {
int thisBez;
// Bezier 0
if (selectedNode>0) {
thisBez = selectedNode>0?(i*3)-1:(spline.NodeCount*3)-1;
EditorGUILayout.BeginHorizontal();
if (selectedBezier==0 && !selectedIndexIsNode)
EditorGUILayout.LabelField("", EditorStyles.foldout, GUILayout.Width(14));
else GUILayout.Space (19f);
if (GUILayout.Button(playgroundLanguage.bezier+" 0", EditorStyles.label, GUILayout.MaxWidth(133)))
selectedIndex = SelectIndex(thisBez);
EditorGUI.BeginChangeCheck();
EditorGUIUtility.labelWidth = 1f;
if (GUILayout.Button(spline.transformNodes[thisBez].enabled?playgroundLanguage.transform:playgroundLanguage.vector3, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))){
spline.transformNodes[thisBez].enabled = !spline.transformNodes[thisBez].enabled;
}
if (spline.transformNodes[thisBez].enabled) {
spline.transformNodes[thisBez].transform = (Transform)EditorGUILayout.ObjectField(spline.transformNodes[thisBez].transform, typeof(Transform), true);
if (spline.transformNodes[thisBez].IsAvailable())
point = spline.transformNodes[thisBez].GetPosition();
else point = spline.GetControlPoint(thisBez);
} else point = EditorGUILayout.Vector3Field(" ", spline.GetControlPoint(thisBez));
GUILayout.Space (35f);
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(spline, "Move Point");
EditorUtility.SetDirty(spline);
spline.SetControlPoint(selectedNode>0?(i*3)-1:(spline.NodeCount*3)-1, point);
}
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = 0;
}
// Bezier 1
if (selectedNode<spline.NodeCount) {
thisBez = selectedNode<spline.NodeCount?(i*3)+1:1;
EditorGUILayout.BeginHorizontal();
if ((selectedIndex==1||selectedBezier==1) && !selectedIndexIsNode)
EditorGUILayout.LabelField("", EditorStyles.foldout, GUILayout.Width(14));
else GUILayout.Space (19f);
if (GUILayout.Button(selectedNode>0?playgroundLanguage.bezier+" 1":playgroundLanguage.bezier+" 0", EditorStyles.label, GUILayout.MaxWidth(133)))
selectedIndex = SelectIndex(thisBez);
EditorGUI.BeginChangeCheck();
EditorGUIUtility.labelWidth = 1f;
if (GUILayout.Button(spline.transformNodes[thisBez].enabled?playgroundLanguage.transform:playgroundLanguage.vector3, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))){
spline.transformNodes[thisBez].enabled = !spline.transformNodes[thisBez].enabled;
}
if (spline.transformNodes[thisBez].enabled) {
spline.transformNodes[thisBez].transform = (Transform)EditorGUILayout.ObjectField(spline.transformNodes[thisBez].transform, typeof(Transform), true);
if (spline.transformNodes[thisBez].IsAvailable())
point = spline.transformNodes[thisBez].GetPosition();
else point = spline.GetControlPoint(thisBez);
} else point = EditorGUILayout.Vector3Field(" ", spline.GetControlPoint(thisBez));
GUILayout.Space (35f);
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(spline, "Move Point");
EditorUtility.SetDirty(spline);
spline.SetControlPoint(thisBez, point);
}
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = 0;
}
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(playgroundLanguage.create, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
Undo.RecordObject(spline, "Add Node");
selectedNode = spline.NodeCount;
spline.AddNode(selectedNode);
EditorUtility.SetDirty(spline);
selectedIndex = SelectIndex((selectedNode+1)*3);
}
EditorGUILayout.Separator();
if (GUILayout.Button(playgroundLanguage.convertAllToTransforms, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
Transform pTrans = new GameObject("Nodes").transform;
pTrans.parent = spline.splineTransform;
pTrans.localPosition = Vector3.zero;
Transform[] transforms = spline.ExportToTransforms();
spline.positionOffset = Vector3.zero;
for (int i = 0; i<transforms.Length; i++) {
spline.transformNodes[i].transform = transforms[i];
spline.transformNodes[i].enabled = true;
if (transforms[i].parent==spline.splineTransform)
transforms[i].parent = pTrans;
}
Selection.activeTransform = pTrans;
}
if (GUILayout.Button(playgroundLanguage.convertAllToVector3, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
spline.SetPoints(spline.ExportToVector3());
spline.positionOffset = Vector3.zero;
for (int i = 0; i<spline.ControlPointCount; i++) {
spline.transformNodes[i].enabled = false;
}
}
EditorGUILayout.Separator();
if (GUILayout.Button(playgroundLanguage.reverseAllNodes, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
spline.ReverseAllNodes();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
// List of users
if (GUILayout.Button(playgroundLanguage.usedBy+" ("+spline.usedBy.Count+")", EditorStyles.toolbarDropDown)) playgroundSettings.usedByFoldout=!playgroundSettings.usedByFoldout;
if (playgroundSettings.usedByFoldout) {
EditorGUILayout.Separator();
if (spline.usedBy.Count>0) {
EditorGUILayout.BeginVertical(boxStyle, GUILayout.MinHeight(26));
for (int i = 0; i<spline.usedBy.Count; i++) {
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(spline.usedBy[i].name, EditorStyles.label))
Selection.activeTransform = spline.usedBy[i];
EditorGUILayout.Separator();
if (GUILayout.Button("-", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
if (EditorUtility.DisplayDialog(playgroundLanguage.removeUserTitle, playgroundLanguage.removeUserMessage, playgroundLanguage.yes, playgroundLanguage.no)) {
PlaygroundParticlesC ps = spline.usedBy[i].GetComponent<PlaygroundParticlesC>();
Undo.RecordObjects(new Object[]{spline,ps}, "Remove User");
if (ps!=null) {
Transform user = spline.usedBy[i];
if (ps.splines.Contains(spline)) {
spline.RemoveUser (user);
ps.splines.Remove(spline);
}
foreach (ManipulatorObjectC m in ps.manipulators) {
ManipulatorPropertyC mp = m.property;
if (mp.splineTarget!=null && mp.splineTarget==spline)
spline.RemoveUser (user);
foreach (ManipulatorPropertyC mps in m.properties)
if (mps.splineTarget!=null && mps.splineTarget==spline)
spline.RemoveUser (user);
}
}
}
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
} else {
EditorGUILayout.HelpBox(playgroundLanguage.noSplineUserMessage, MessageType.Info);
}
}
// Advanced
if (GUILayout.Button(playgroundLanguage.advanced, EditorStyles.toolbarDropDown)) playgroundSettings.splineAdvancedFoldout=!playgroundSettings.splineAdvancedFoldout;
if (playgroundSettings.splineAdvancedFoldout) {
EditorGUILayout.Separator();
spline.fixedVelocityOnNewNode = EditorGUILayout.FloatField (playgroundLanguage.velocityOnNewNode, spline.fixedVelocityOnNewNode);
spline.moveTransformsAsBeziers = EditorGUILayout.Toggle (playgroundLanguage.moveTransformsAsBeziers, spline.moveTransformsAsBeziers);
spline.exportWithNodeStructure = EditorGUILayout.Toggle (playgroundLanguage.exportWithNodeStructure, spline.exportWithNodeStructure);
EditorGUILayout.Separator();
spline.drawGizmo = EditorGUILayout.Toggle (playgroundLanguage.drawBezierGizmo, spline.drawGizmo);
spline.bezierWidth = EditorGUILayout.FloatField (playgroundLanguage.bezierWidth, spline.bezierWidth);
if (spline.bezierWidth<0) spline.bezierWidth = 0;
}
}
EditorGUILayout.EndVertical();
SceneView.RepaintAll();
}
private void DrawSelectedPointInspector() {
if (selectedIndex>=spline.ControlPointCount) selectedIndex = SelectIndex(spline.ControlPointCount-1);
EditorGUILayout.PrefixLabel(playgroundLanguage.selection);
EditorGUILayout.BeginHorizontal(boxStyle);
if (GUILayout.Button(playgroundLanguage.spline, EditorStyles.label, GUILayout.ExpandWidth(false))) {
selectedIndex = SelectIndex(-1);
SceneView.RepaintAll();
}
if (selectedIndex>=0) {
GUILayout.Label(">", EditorStyles.label, GUILayout.MaxWidth(16));
if (GUILayout.Button(playgroundLanguage.node+" "+selectedNode.ToString(), EditorStyles.label, GUILayout.ExpandWidth(false))) {
selectedIndex = SelectIndex(selectedNode*3);
}
}
if (selectedIndex>=0 && !selectedIndexIsNode) {
GUILayout.Label(">", EditorStyles.label, GUILayout.MaxWidth(16));
GUILayout.Label(playgroundLanguage.bezier+" "+selectedBezier.ToString(), EditorStyles.label, GUILayout.ExpandWidth(false));
}
if (selectedIndex>=0) {
if (spline.transformNodes[selectedIndex].IsAvailable()) {
if (GUILayout.Button(" ("+playgroundLanguage.transform+") ", EditorStyles.label, GUILayout.ExpandWidth(false)))
Selection.activeTransform = spline.transformNodes[selectedIndex].transform;
} else {
GUILayout.Label(" ("+playgroundLanguage.vector3+")", EditorStyles.label, GUILayout.ExpandWidth(false));
}
}
EditorGUILayout.Separator();
if (GUILayout.Button ("<", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
selectedIndex = SelectIndex(selectedIndex-1);
if (selectedIndex<0)
selectedIndex = SelectIndex(spline.ControlPointCount-1);
}
if (GUILayout.Button (">", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
selectedIndex = SelectIndex(selectedIndex+1);
if (selectedIndex>spline.ControlPointCount-1)
selectedIndex = SelectIndex(0);
}
EditorGUILayout.EndHorizontal();
EditorGUI.BeginChangeCheck();
// A node or bezier is selected
if (selectedIndex>=0) {
Vector3 point = EditorGUILayout.Vector3Field(playgroundLanguage.position, spline.GetControlPoint(selectedIndex));
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(spline, "Move Point");
EditorUtility.SetDirty(spline);
spline.SetControlPoint(selectedIndex, point);
}
EditorGUI.BeginChangeCheck();
PlaygroundSplines.BezierControlPointMode mode = (PlaygroundSplines.BezierControlPointMode)EditorGUILayout.EnumPopup(playgroundLanguage.bezierMode, spline.GetControlPointMode(selectedIndex));
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(spline, "Change Point Mode");
spline.SetControlPointMode(selectedIndex, mode);
EditorUtility.SetDirty(spline);
}
}
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(playgroundLanguage.addNode, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
Undo.RecordObject(spline, "Add Node");
if (selectedIndex<0) selectedNode = spline.NodeCount;
spline.AddNode(selectedNode);
EditorUtility.SetDirty(spline);
selectedIndex = SelectIndex((selectedNode+1)*3);
}
EditorGUILayout.Separator();
GUI.enabled = (!spline.Loop&&spline.NodeCount>1||spline.Loop&&spline.NodeCount>2);
if (GUILayout.Button(playgroundLanguage.removeSelectedNode, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) {
Undo.RecordObject(spline, "Remove Node");
spline.RemoveNode(selectedNode);
EditorUtility.SetDirty(spline);
selectedIndex-=3;
if (selectedIndex<0)
selectedIndex = SelectIndex(0);
}
EditorGUILayout.EndHorizontal();
GUI.enabled = true;
}
private int SelectIndex (int newIndex) {
if (newIndex>-1)
UnityEditor.Tools.current = UnityEditor.Tool.None;
else UnityEditor.Tools.current = lastActiveTool;
selectedNode = (newIndex+1)/3;
selectedBezier = newIndex<3?0:(((newIndex)%3))%2;
selectedIndexIsNode = newIndex==0||newIndex%3==0;
return newIndex;
}
private int foldoutHeight = 0;
private bool toolboxFoldout = true;
private bool callForAddNode = false;
private bool callForRemoveNode = false;
private void OnSceneGUI () {
callForAddNode = (selectedIndex>-1 && Event.current.control && !Event.current.shift && Event.current.type==EventType.mouseUp);
callForRemoveNode = (selectedIndex>-1 && Event.current.control && Event.current.shift && Event.current.type==EventType.mouseUp);
handleTransform = spline.transform;
handleRotation = UnityEditor.Tools.pivotRotation == PivotRotation.Local ? handleTransform.rotation : Quaternion.identity;
bezierHandleRotation = Camera.current.transform.rotation;
Event e = Event.current;
if (toolboxFoldout)
if (selectedIndex<0)
foldoutHeight = 68;
else foldoutHeight = 120;
else
foldoutHeight = 0;
Rect toolboxRect = new Rect(10f,Screen.height-(70f+foldoutHeight),300f,103f+foldoutHeight);
// Don't deselect upon click
if (toolboxFoldout && e.type == EventType.Layout) {
HandleUtility.AddDefaultControl(0);
}
// Toolbox
Handles.BeginGUI();
GUILayout.BeginArea(toolboxRect);
if (boxStyle==null)
boxStyle = GUI.skin.FindStyle("box");
GUILayout.BeginVertical(boxStyle);
toolboxFoldout = GUILayout.Toggle(toolboxFoldout, playgroundLanguage.playgroundSpline, EditorStyles.foldout);
if (toolboxFoldout) {
DrawSelectedPointInspector();
}
GUILayout.EndVertical();
GUILayout.EndArea();
Handles.EndGUI();
Vector3 p0 = ShowPoint(0);
for (int i = 1; i < spline.ControlPointCount; i += 3) {
Vector3 p1 = ShowPoint(i);
Vector3 p2 = ShowPoint(i + 1);
Vector3 p3 = ShowPoint(i + 2);
Handles.color = new Color(1f,.8f,0f);
Handles.DrawLine(p0, p1);
Handles.DrawLine(p2, p3);
p0 = p3;
}
if (callForAddNode) {
Undo.RecordObject(spline, "Add Node");
spline.AddNode(selectedNode);
EditorUtility.SetDirty(spline);
selectedIndex = SelectIndex((selectedNode+1)*3);
}
if (callForRemoveNode) {
Undo.RecordObject(spline, "Remove Node");
spline.RemoveNode(selectedNode);
EditorUtility.SetDirty(spline);
if (selectedIndex>=spline.ControlPointCount)
selectedIndex = SelectIndex(spline.ControlPointCount-1);
}
}
private void ShowDirections () {
Handles.color = Color.green;
Vector3 point = spline.GetPoint(0f);
Handles.DrawLine(point, point + spline.GetDirection(0f) * directionScale);
int steps = stepsPerCurve * spline.NodeCount;
for (int i = 1; i <= steps; i++) {
point = spline.GetPoint(i / (float)steps);
Handles.DrawLine(point, point + spline.GetDirection(i / (float)steps) * directionScale);
}
}
private Vector3 ShowPoint (int index) {
Vector3 pointWithOffset = spline.transformNodes[index].IsAvailable()? spline.GetPoint(index)+spline.positionOffset : handleTransform.TransformPoint(spline.GetInversePoint(index)+spline.positionOffset);
float size = HandleUtility.GetHandleSize(pointWithOffset);
if (index == 0 && spline.Loop) {
size *= 2f;
}
Handles.color = new Color(1f,.5f,0f);
if ((index==0||index%3==0)) {
if (Handles.Button(pointWithOffset, handleRotation, size * handleSize, size * pickSize, Handles.DotCap)) {
selectedIndex = SelectIndex(index);
Repaint();
}
} else {
if (Handles.Button(pointWithOffset, bezierHandleRotation, size * handleSize, size * pickSize, Handles.CircleCap)) {
selectedIndex = SelectIndex(index);
Repaint();
}
}
if (selectedIndex == index) {
EditorGUI.BeginChangeCheck();
pointWithOffset = Handles.DoPositionHandle(pointWithOffset, handleRotation);
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(spline, "Move Point");
EditorUtility.SetDirty(spline);
spline.SetControlPoint(index, spline.transformNodes[index].IsAvailable()?pointWithOffset:handleTransform.InverseTransformPoint(pointWithOffset), spline.positionOffset);
}
}
return pointWithOffset;
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 213dc41afb5744746a7e99b5b709b841
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,762 @@
using UnityEngine;
using System;
using System.Collections.Generic;
/// <summary>
/// Collection of methods for working with splines.
/// This is based on the great learning tutorial Curves and Splines by Jasper Flick.
///
/// References:
/// http://catlikecoding.com/unity/tutorials/curves-and-splines/
/// http://answers.unity3d.com/questions/374333/positioning-an-object-on-a-spline-relative-to-play.html
/// </summary>
namespace PlaygroundSplines {
/// <summary>
/// Holds information about a spline and contains functions for working with the nodes and bezier handles.
/// </summary>
[ExecuteInEditMode()]
public class PlaygroundSpline : MonoBehaviour {
/// <summary>
/// The list of nodes and bezier handles making the spline.
/// </summary>
[SerializeField]
private List<Vector3> points = new List<Vector3>();
/// <summary>
/// The modes of the bezier handles.
/// </summary>
[SerializeField]
private List<BezierControlPointMode> modes = new List<BezierControlPointMode>();
/// <summary>
/// Determines if the spline is looping.
/// </summary>
[SerializeField]
private bool loop;
/// <summary>
/// The list of transform nodes to set positions live of an existing node.
/// </summary>
[HideInInspector] public List<TransformNode> transformNodes = new List<TransformNode>();
/// <summary>
/// Determines if the spline time should be reversed. If you'd like to physically reverse the arrays making the spline then call ReverseAllNodes().
/// </summary>
[HideInInspector] public bool reverse;
/// <summary>
/// The time offset of the spline.
/// </summary>
[HideInInspector] public float timeOffset;
/// <summary>
/// The position offset of the spline in relation to its transform.
/// </summary>
[HideInInspector] public Vector3 positionOffset;
[HideInInspector] public Transform splineTransform;
[HideInInspector] public Matrix4x4 splineTransformMx;
[HideInInspector] public List<Transform> usedBy = new List<Transform>();
[HideInInspector] public float fixedVelocityOnNewNode = .5f;
[HideInInspector] public bool moveTransformsAsBeziers = false;
[HideInInspector] public bool exportWithNodeStructure = false;
// Gizmos
public static bool drawSplinePreviews = true;
[HideInInspector] public bool drawGizmo = true;
[HideInInspector] public float bezierWidth = 2f;
#if UNITY_EDITOR
void OnDrawGizmos () {
if (drawSplinePreviews && drawGizmo) {
Color innerBezier = new Color(1f,1f,1f,1f);
Color outerBezier = new Color(.5f,.5f,0,.2f);
Vector3 p0 = ShowPoint(0);
for (int i = 1; i < ControlPointCount; i += 3) {
Vector3 p1 = ShowPoint(i);
Vector3 p2 = ShowPoint(i + 1);
Vector3 p3 = ShowPoint(i + 2);
UnityEditor.Handles.DrawBezier(p0, p3, p1, p2, innerBezier, null, bezierWidth);
UnityEditor.Handles.DrawBezier(p0, p3, p1, p2, outerBezier, null, bezierWidth*10f);
p0 = p3;
}
}
}
Vector3 ShowPoint (int index) {
return transformNodes[index].IsAvailable()? GetPoint(index)+positionOffset : splineTransform.TransformPoint(GetInversePoint(index)+positionOffset);
}
#endif
Vector3 previousPosition;
Quaternion previousRotation;
Vector3 previousScale;
bool isReady;
public bool IsReady () {
return isReady;
}
/// <summary>
/// Adds a user to the spline. This helps keeping track of which objects are using the spline.
/// </summary>
/// <returns><c>true</c>, if user was added, <c>false</c> otherwise.</returns>
/// <param name="">.</param>
public bool AddUser (Transform thisTransform) {
if (!usedBy.Contains(thisTransform)) {
usedBy.Add (thisTransform);
return true;
}
return false;
}
/// <summary>
/// Removes a user from the spline. This helps keeping track of which objects are using the spline.
/// </summary>
/// <returns><c>true</c>, if user was removed, <c>false</c> otherwise.</returns>
/// <param name="">.</param>
public bool RemoveUser (Transform thisTransform) {
if (usedBy.Contains(thisTransform)) {
usedBy.Remove (thisTransform);
return true;
}
return false;
}
/// <summary>
/// Determines whether this spline has the user of passed in transform.
/// </summary>
/// <returns><c>true</c> if this spline has the user of the passed in transform; otherwise, <c>false</c>.</returns>
/// <param name="thisTransform">This transform.</param>
public bool HasUser (Transform thisTransform) {
return usedBy.Contains (thisTransform);
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="PlaygroundSplines.PlaygroundSpline"/> is set to loop.
/// </summary>
/// <value><c>true</c> if set to loop; otherwise, <c>false</c>.</value>
public bool Loop {
get {
return loop;
}
set {
loop = value;
if (value == true && NodeCount>1) {
modes[modes.Count - 1] = modes[0];
SetControlPoint(0, points[0]);
}
}
}
/// <summary>
/// Gets the control point count.
/// </summary>
/// <value>The control point count.</value>
public int ControlPointCount {
get {
return points.Count;
}
}
/// <summary>
/// Gets the control point.
/// </summary>
/// <returns>The control point.</returns>
/// <param name="index">Index.</param>
public Vector3 GetControlPoint (int index) {
return GetPoint(index);
}
/// <summary>
/// Sets the control point and withdraws the offset.
/// </summary>
/// <param name="index">Index.</param>
/// <param name="point">Point.</param>
/// <param name="offset">Offset.</param>
public void SetControlPoint (int index, Vector3 point, Vector3 offset) {
SetControlPoint(index, point-offset);
}
/// <summary>
/// Sets the control point.
/// </summary>
/// <param name="index">Index.</param>
/// <param name="point">Position.</param>
public void SetControlPoint (int index, Vector3 point) {
if (index<0) index = 0;
if (index % 3 == 0) {
Vector3 delta = (point - GetPoint(index));
Vector3 v;
if (loop) {
if (index == 0) {
//if (!PointHasTransform(1))
{v = GetPoint(1); SetPoint(1, v+delta);}
//if (!PointHasTransform(points.Count-2))
{v = GetPoint(points.Count-2); SetPoint(points.Count-2, v+delta);}
if (moveTransformsAsBeziers || !PointHasTransform(points.Count-1))
{SetPoint(points.Count-1, point);}
} else
if (index == points.Count - 1) {
//if (!PointHasTransform(0))
{SetPoint(0, point);}
//if (!PointHasTransform(1))
{v = GetPoint(1); SetPoint(1, v+delta);}
//if (!PointHasTransform(index-1))
{v = GetPoint(index-1); SetPoint(index-1, v+delta);}
} else {
//if (!PointHasTransform(index-1))
{v = GetPoint(index-1); SetPoint(index-1, v+delta);}
//if (!PointHasTransform(index+1))
{v = GetPoint(index+1); SetPoint(index+1, v+delta);}
}
} else {
if (index > 0) {
if (moveTransformsAsBeziers || !PointHasTransform(index-1))
{v = GetPoint(index-1); SetPoint(index-1, v+delta);}
}
if (index + 1 < points.Count) {
if (moveTransformsAsBeziers || !PointHasTransform(index+1))
{v = GetPoint(index+1); SetPoint(index+1, v+delta);}
}
}
}
SetPoint(index, point);
EnforceMode(index);
}
/// <summary>
/// Sets all points from an array. Please ensure the same length of your passed in vectors as PlaygroundSpline.ControlPointCount.
/// </summary>
/// <param name="vectors">Vectors.</param>
public void SetPoints (Vector3[] vectors) {
if (vectors.Length!=points.Count) {
Debug.Log ("Please ensure the same length of your passed in vectors ("+vectors.Length+") as the current points ("+points.Count+"). Use PlaygroundSpline.ControlPointCount to get the current count.");
return;
}
for (int i = 0; i<points.Count; i++) {
points[i] = vectors[i];
}
}
public bool PointHasTransform (int index) {
return transformNodes[index].IsAvailable();
}
/// <summary>
/// Moves the entire spline separate from its transform component. Use this if you'd like to offset the spline from its transform separately from the positionOffset.
/// </summary>
/// <param name="translation">The amount to move the spline in Units.</param>
public void TranslateSpline (Vector3 translation) {
for (int i = 0; i<points.Count; i++) {
points[i] += translation;
}
}
public Vector3 GetTransformPosition () {
return previousPosition;
}
public Quaternion GetTransformRotation () {
return previousRotation;
}
public Vector3 GetTransformScale () {
return previousScale;
}
public BezierControlPointMode GetControlPointMode (int index) {
return modes[(index + 1) / 3];
}
public void SetControlPointMode (int index, BezierControlPointMode mode) {
int modeIndex = (index + 1) / 3;
modes[modeIndex] = mode;
if (loop) {
if (modeIndex == 0) {
modes[modes.Count - 1] = mode;
}
else if (modeIndex == modes.Count - 1) {
modes[0] = mode;
}
}
EnforceMode(index);
}
private void EnforceMode (int index) {
int modeIndex = (index+1) / 3;
BezierControlPointMode mode = modes[modeIndex];
if (mode == BezierControlPointMode.Free || !loop && (modeIndex == 0 || modeIndex == modes.Count - 1)) {
return;
}
int middleIndex = modeIndex * 3;
int fixedIndex, enforcedIndex;
if (index <= middleIndex) {
fixedIndex = middleIndex - 1;
if (fixedIndex < 0) {
fixedIndex = points.Count - 2;
}
enforcedIndex = middleIndex + 1;
if (enforcedIndex >= points.Count) {
enforcedIndex = 1;
}
}
else {
fixedIndex = middleIndex + 1;
if (fixedIndex >= points.Count) {
fixedIndex = 1;
}
enforcedIndex = middleIndex - 1;
if (enforcedIndex < 0) {
enforcedIndex = points.Count - 2;
}
}
Vector3 middle = GetPoint(middleIndex);
Vector3 enforcedTangent = middle - GetPoint(fixedIndex);
if (mode == BezierControlPointMode.Aligned) {
enforcedTangent = enforcedTangent.normalized * Vector3.Distance(middle, GetPoint(enforcedIndex));
}
if (moveTransformsAsBeziers || !PointHasTransform(enforcedIndex))
SetPoint(enforcedIndex, middle + enforcedTangent);
}
public int NodeCount {
get {
return (points.Count - 1) / 3;
}
}
/// <summary>
/// Get position from time.
/// </summary>
/// <returns>The point in world space.</returns>
/// <param name="t">Time.</param>
public Vector3 GetPoint (float t) {
int i;
if (reverse) {
t = 1f-t;
t = (t-timeOffset)%1f;
if (t<0)
t = 1f+t;
} else t = (t+timeOffset)%1f;
if (t >= 1f) {
//t = 1f;
i = points.Count - 4;
}
else {
t = Mathf.Clamp01(t) * NodeCount;
i = (int)t;
t -= i;
i *= 3;
}
return splineTransformMx.MultiplyPoint3x4(Bezier.GetPoint(GetInversePoint(i), GetInversePoint(i + 1), GetInversePoint(i + 2), GetInversePoint(i + 3), t)+positionOffset);
}
public Vector3 GetVelocity (float t) {
int i;
if (reverse)
t = 1f-t;
t = (t+timeOffset)%1f;
if (t >= 1f) {
t = 1f;
i = points.Count - 4;
}
else {
t = Mathf.Clamp01(t) * NodeCount;
i = (int)t;
t -= i;
i *= 3;
}
return splineTransformMx.MultiplyPoint3x4(Bezier.GetFirstDerivative(GetInversePoint(i), GetInversePoint(i + 1), GetInversePoint(i + 2), GetInversePoint(i + 3), t)+positionOffset) - previousPosition;
}
/// <summary>
/// Get position from node index in the spline. If the node consists of an available transform its position will be returned, otherwise the user-specified Vector3 position.
/// </summary>
/// <returns>The point in world space.</returns>
/// <param name="index">Index.</param>
public Vector3 GetPoint (int index) {
if (transformNodes[index].IsAvailable())
return transformNodes[index].GetPosition();
else return points[index];
}
public Vector3 GetInversePoint (int index) {
if (transformNodes[index].IsAvailable())
return transformNodes[index].GetInvsersePosition();
else return points[index];
}
public Vector3 GetPointWorldSpace (int index) {
if (transformNodes[index].IsAvailable())
return transformNodes[index].GetPosition();
else return splineTransformMx.MultiplyPoint3x4(points[index]+positionOffset);
}
/// <summary>
/// Sets a point to specified position.
/// </summary>
/// <param name="index">Index.</param>
/// <param name="position">Position.</param>
void SetPoint (int index, Vector3 position) {
if (transformNodes[index].IsAvailable())
transformNodes[index].SetPosition(position);
else points[index] = position;
}
/// <summary>
/// Translates a point.
/// </summary>
/// <param name="index">Index.</param>
/// <param name="translation">Translation.</param>
void TranslatePoint (int index, Vector3 translation) {
if (transformNodes[index].IsAvailable())
transformNodes[index].Translate(translation);
else points[index] += translation;
}
// Calculates the best fitting time in the given interval
private float CPOB(Vector3 aP, float aStart, float aEnd, int aSteps)
{
aStart = Mathf.Clamp01(aStart);
aEnd = Mathf.Clamp01(aEnd);
float step = (aEnd-aStart) / (float)aSteps;
float Res = 0;
float Ref = float.MaxValue;
for (int i = 0; i < aSteps; i++)
{
float t = aStart + step*i;
float L = (GetPoint(t)-aP).sqrMagnitude;
if (L < Ref)
{
Ref = L;
Res = t;
}
}
return Res;
}
public float ClosestTimeFromPoint (Vector3 aP) {
float t = CPOB(aP, 0, 1, 10);
float delta = 1.0f / 10.0f;
for (int i = 0; i < 4; i++)
{
t = CPOB(aP, t - delta, t + delta, 10);
delta /= 9;
}
return t;
}
public Vector3 ClosestPointFromPosition (Vector3 aP) {
return GetPoint(ClosestTimeFromPoint(aP));
}
public Vector3 GetDirection (float t) {
return (GetPoint(t+.001f)-GetPoint(t)).normalized;
}
/// <summary>
/// Adds a node at the last position of the node index.
/// </summary>
public void AddNode () {
AddNode ((points.Count-1)/3);
}
/// <summary>
/// Adds a node at specified node index.
/// </summary>
/// <param name="index">Index.</param>
public void AddNode (int index) {
int nodeIndex = index*3;
Vector3 point = GetPoint(nodeIndex);
Vector3 direction;
if (index>0) {
direction = GetPoint(nodeIndex)-GetPoint(nodeIndex-1);
} else direction = GetPoint(nodeIndex+1)-GetPoint(nodeIndex);
direction*=fixedVelocityOnNewNode;
points.InsertRange(nodeIndex+1, new Vector3[3]);
point += direction;
points[nodeIndex+2] = point;
point += direction;
points[nodeIndex+1] = point;
point += direction;
points[nodeIndex+3] = point;
transformNodes.InsertRange(nodeIndex+1, new TransformNode[]{new TransformNode(), new TransformNode(), new TransformNode()});
BezierControlPointMode currentIndexMode = modes[index];
modes.Insert (index, new BezierControlPointMode());
modes[index] = currentIndexMode;
EnforceMode(index);
SetControlPoint((index+1)*3, GetPoint((index+1)*3));
if (loop) {
points[points.Count - 1] = points[0];
modes[modes.Count - 1] = modes[0];
EnforceMode(0);
}
}
/// <summary>
/// Removes the first node in the node index.
/// </summary>
public void RemoveFirst () {
RemoveNode(0);
}
/// <summary>
/// Removes the last node in the node index.
/// </summary>
public void RemoveLast () {
RemoveNode((points.Count-1)/3);
}
/// <summary>
/// Removes a node at specified node index.
/// </summary>
/// <param name="index">Index.</param>
public void RemoveNode (int index) {
index = Mathf.Clamp (index, 0, points.Count-1);
int pointIndex = index*3;
if (points.Count<=4) return;
if (pointIndex<points.Count-1) {
points.RemoveRange(pointIndex, 3);
transformNodes.RemoveRange(pointIndex, 3);
} else {
points.RemoveRange(pointIndex-2, 3);
transformNodes.RemoveRange(pointIndex-2, 3);
}
modes.RemoveAt (index);
EnforceMode (index-1);
if (index>0)
SetControlPoint((index-1)*3, GetPoint((index-1)*3));
else
SetControlPoint(0, GetPoint(0));
}
/// <summary>
/// Reverses all nodes in the node index.
/// </summary>
public void ReverseAllNodes () {
points.Reverse();
transformNodes.Reverse();
modes.Reverse();
}
public void SwapNodes (int from, int to) {
Vector3[] fromPoints = points.GetRange (from, 3).ToArray();
Vector3[] toPoints = points.GetRange (to, 3).ToArray();
TransformNode[] fromTnode = transformNodes.GetRange (from, 3).ToArray();
TransformNode[] toTnode = transformNodes.GetRange (to, 3).ToArray();
BezierControlPointMode fromMode = modes[from];
BezierControlPointMode toMode = modes[to];
for (int i = from; i<3; i++) {
points[i] = toPoints[i];
transformNodes[i] = toTnode[i];
}
for (int i = to; i<3; i++) {
points[i] = fromPoints[i];
transformNodes[i] = fromTnode[i];
}
modes[from] = toMode;
modes[to] = fromMode;
}
/// <summary>
/// Exports all nodes to Transform[]. Enable exportWithNodeStructure to parent each bezier handle to their node.
/// </summary>
/// <returns>A built-in array of Transforms.</returns>
public Transform[] ExportToTransforms () {
Transform[] transforms = new Transform[points.Count];
for (int i = 0; i<points.Count; i++) {
int iNode = (i+1)/3;
int iBezier = i<3?0:(((i)%3))%2;
bool iIsNode = i==0||i%3==0;
transforms[i] = new GameObject(iIsNode?"Node "+iNode:"Node "+iNode+" - Bezier "+iBezier).transform;
transforms[i].parent = splineTransform;
transforms[i].position = splineTransform.TransformPoint(GetInversePoint(i)+positionOffset);
}
if (exportWithNodeStructure) {
for (int i = 2; i<transforms.Length; i++) {
int iBezier = i<3?0:(((i)%3))%2;
bool iIsNode = i==0||i%3==0;
if (!iIsNode)
transforms[i].parent = transforms[iBezier==0?i+1:i-1];
}
transforms[1].parent = transforms[0];
}
return transforms;
}
/// <summary>
/// Exports all nodes to Vector3[].
/// </summary>
/// <returns>A built-in array of Vector3</returns>
public Vector3[] ExportToVector3 () {
Vector3[] vectors = new Vector3[points.Count];
for (int i = 0; i<points.Count; i++) {
vectors[i] = GetPoint(i)+positionOffset;
}
return vectors;
}
/// <summary>
/// Reset this Playground Spline. Two nodes and two bezier handles will be created.
/// </summary>
public void Reset () {
points = new List<Vector3> {
new Vector3(1f, 0f, 0f),
new Vector3(2f, 0f, 0f),
new Vector3(3f, 0f, 0f),
new Vector3(4f, 0f, 0f)
};
modes = new List<BezierControlPointMode> {
BezierControlPointMode.Aligned,
BezierControlPointMode.Aligned
};
transformNodes = new List<TransformNode> {
new TransformNode(),
new TransformNode(),
new TransformNode(),
new TransformNode()
};
}
/*************************************************************************************************************************************************
MonoBehaviours
*************************************************************************************************************************************************/
void OnEnable () {
isReady = false;
splineTransform = transform;
SetMatrix();
}
void Update () {
SetMatrix();
for (int i = 0; i<transformNodes.Count; i++)
transformNodes[i].Update(splineTransform);
}
void SetMatrix () {
if (previousPosition!=splineTransform.position || previousRotation!=splineTransform.rotation || previousScale!=splineTransform.localScale)
splineTransformMx.SetTRS(splineTransform.position, splineTransform.rotation, splineTransform.localScale);
previousPosition = splineTransform.position;
previousRotation = splineTransform.rotation;
previousScale = splineTransform.localScale;
isReady = true;
}
}
/// <summary>
/// Class for common bezier operations on a spline.
/// </summary>
public static class Bezier {
public static Vector3 GetPoint (Vector3 p0, Vector3 p1, Vector3 p2, float t) {
t = Mathf.Clamp01(t);
float oneMinusT = 1f - t;
return
oneMinusT * oneMinusT * p0 +
2f * oneMinusT * t * p1 +
t * t * p2;
}
public static Vector3 GetFirstDerivative (Vector3 p0, Vector3 p1, Vector3 p2, float t) {
return
2f * (1f - t) * (p1 - p0) +
2f * t * (p2 - p1);
}
public static Vector3 GetPoint (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
t = Mathf.Clamp01(t);
float OneMinusT = 1f - t;
return
OneMinusT * OneMinusT * OneMinusT * p0 +
3f * OneMinusT * OneMinusT * t * p1 +
3f * OneMinusT * t * t * p2 +
t * t * t * p3;
}
public static Vector3 GetFirstDerivative (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) {
t = Mathf.Clamp01(t);
float oneMinusT = 1f - t;
return
3f * oneMinusT * oneMinusT * (p1 - p0) +
6f * oneMinusT * t * (p2 - p1) +
3f * t * t * (p3 - p2);
}
}
[Serializable]
public class TransformNode {
public bool enabled;
public Transform transform;
bool isAvailable;
Vector3 position;
Vector3 inversePosition;
Vector3 previousPosition;
public bool Update (Transform splineTransform) {
if (enabled && transform!=null) {
previousPosition = position;
position = transform.position;
inversePosition = splineTransform.InverseTransformPoint(transform.position);
isAvailable = true;
return true;
}
isAvailable = false;
return false;
}
public bool IsAvailable () {
return enabled&&isAvailable;
}
public Vector3 GetPosition () {
return position;
}
public Vector3 GetInvsersePosition () {
return inversePosition;
}
public void SetPosition (Vector3 newPosition) {
if (transform==null) return;
transform.position = newPosition;
}
public void Translate (Vector3 translation) {
if (transform==null) return;
transform.position += translation;
}
public Vector3 GetPositionDelta () {
return previousPosition-position;
}
}
public enum SplineMode {
Vector3,
Transform
}
/// <summary>
/// The bezier mode for a spline node. This controls how one bezier handle acts in relation to the other.
/// </summary>
public enum BezierControlPointMode {
/// <summary>
/// Align the angle between the two bezier handles but keep individual lengths. Has a differential smooth in and out angle.
/// </summary>
Aligned,
/// <summary>
/// Align the angle and length between the two bezier handles. Has an equally smooth in and out angle.
/// </summary>
Mirrored,
/// <summary>
/// Bezier handles are freely aligned without consideration to the other. Ables you to have sharp angles.
/// </summary>
Free
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: bd22d17ed7f31487db6f1305d10dfeef
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,203 @@
using UnityEngine;
using System.Collections;
namespace PlaygroundSplines {
/// <summary>
/// The PlaygroundSplineMesh class lets you create a mesh from a Playground Spline.
/// </summary>
[ExecuteInEditMode()]
public class PlaygroundSplineMesh : MonoBehaviour {
public PlaygroundSpline spline;
[Range(2,1000)]
public int points = 100;
[Range(.01f, 100f)]
public float width = 1f;
public bool noise = false;
[Range(.01f, 100f)]
public float noiseStrength = 1f;
public Vector2 noiseScale = new Vector2(1f,1f);
public bool noiseDistribution;
public AnimationCurve noiseDistributionL;
public AnimationCurve noiseDistributionR;
public bool followSurface = false;
public bool followSurfaceRotation = false;
public float surfaceOffset = .1f;
public Vector3 meshUpDirection = Vector3.up;
public Vector3 surfaceDirection = Vector3.down;
int prevPoints;
float prevWidth;
bool prevNoise;
float prevNoiseStrength;
Vector2 prevNoiseScale;
bool prevNoiseDistribution;
bool prevFollowSurface;
bool prevFollowSurfaceRotation;
float prevSurfaceOffset;
Vector3 prevMeshUpDirection;
Vector3 prevSurfaceDirection;
void OnEnable ()
{
if (noiseDistributionL == null || noiseDistributionL.keys.Length==0)
SetAnimationCurveVals(ref noiseDistributionL);
if (noiseDistributionR == null || noiseDistributionR.keys.Length==0)
SetAnimationCurveVals(ref noiseDistributionR);
if (GetComponent<Renderer>() == null)
gameObject.AddComponent<MeshRenderer>();
if (spline == null)
spline = GetComponent<PlaygroundSpline>();
if (spline != null)
BuildSplineMesh(spline, points, width);
SetVals();
}
void Update ()
{
if (NeedsUpdate ())
{
if (spline != null)
BuildSplineMesh(spline, points, width);
SetVals ();
}
}
bool NeedsUpdate ()
{
return prevPoints!=points ||
prevWidth!=width ||
prevNoise!=noise ||
noise && (prevNoiseScale!=noiseScale || prevNoiseStrength!=noiseStrength || prevNoiseDistribution!=noiseDistribution) ||
prevFollowSurface!=followSurface ||
prevFollowSurfaceRotation!=followSurfaceRotation ||
prevSurfaceOffset!=surfaceOffset ||
prevMeshUpDirection!=meshUpDirection ||
prevSurfaceDirection!=surfaceDirection;
}
void SetVals ()
{
prevPoints = points;
prevWidth = width;
prevNoise = noise;
prevNoiseStrength = noiseStrength;
prevNoiseScale = noiseScale;
prevNoiseDistribution = noiseDistribution;
prevFollowSurface = followSurface;
prevFollowSurfaceRotation = followSurfaceRotation;
prevSurfaceOffset = surfaceOffset;
prevMeshUpDirection = meshUpDirection;
prevSurfaceDirection = surfaceDirection;
}
void SetAnimationCurveVals (ref AnimationCurve curve)
{
Keyframe[] reset = new Keyframe[2];
reset[0].time = 0;
reset[1].time = 1f;
reset[0].value = 1f;
reset[1].value = 1f;
curve.keys = reset;
}
public void BuildSplineMesh (PlaygroundSpline spline, int points, float width)
{
if (points<2)
points = 2;
int totalVertices = points*2;
MeshFilter _mf = GetComponent<MeshFilter>()!=null? GetComponent<MeshFilter>() : gameObject.AddComponent<MeshFilter>();
Mesh _m = new Mesh();
Vector3[] verts = new Vector3[totalVertices];
Vector2[] uvs = new Vector2[totalVertices];
int[] tris = new int[(points-1)*6];
Vector3 up = meshUpDirection;
// Construct the mesh
for (int i = 0; i<points; i++)
{
// Create a normalized time value
float t = (i*1f)/(points-1);
float tNext = ((i*1f)+1)/(points-1);
if (t>=1 && !spline.Loop)
t = .9999f;
if (tNext>=1 && !spline.Loop)
tNext = .99999f;
// Get the current and next position from the spline on time
Vector3 currentPosition = spline.GetPoint (t);
Vector3 nextPosition = spline.GetPoint (tNext);
// Raycast down to determine surface (especially practical for roads / rivers)
if (followSurface || followSurfaceRotation)
{
RaycastHit hit;
if (Physics.Raycast (currentPosition, surfaceDirection, out hit))
{
if (followSurfaceRotation)
up = hit.normal;
if (followSurface)
currentPosition = hit.point + (hit.normal * surfaceOffset);
}
if (followSurface)
{
if (Physics.Raycast (nextPosition, surfaceDirection, out hit))
{
nextPosition = hit.point + (hit.normal * surfaceOffset);
}
}
}
// Calculate noise (if enabled)
float noiseAmountL = noise? Mathf.PerlinNoise((t%1f)*noiseScale.x, 0)*noiseStrength : 0;
float noiseAmountR = noise? Mathf.PerlinNoise((t%1f)*noiseScale.y, 0)*noiseStrength : 0;
if (noise && noiseDistribution)
{
noiseAmountL *= noiseDistributionL.Evaluate(t);
noiseAmountR *= noiseDistributionR.Evaluate(t);
}
// Create two width point references based on current and next position
Vector3 dir = (Vector3.Cross(up, nextPosition - currentPosition)).normalized;
Vector3 lPoint = currentPosition + dir * ((width/2)+noiseAmountL);
Vector3 rPoint = currentPosition - dir * ((width/2)+noiseAmountR);
// Draw debug
Debug.DrawLine(lPoint, rPoint);
verts[i*2] = lPoint;
verts[(i*2)+1] = rPoint;
uvs[i*2] = new Vector2(t,0);
uvs[(i*2)+1] = new Vector2(t,1f);
if (i>0)
{
int triIndex = (i-1)*6;
int vertIndex = i*2;
tris[triIndex] = vertIndex-2;
tris[triIndex+1] = vertIndex-1;
tris[triIndex+2] = vertIndex;
tris[triIndex+3] = vertIndex;
tris[triIndex+4] = vertIndex-1;
tris[triIndex+5] = vertIndex+1;
}
}
// Assign the data to the mesh
_m.vertices = verts;
_m.uv = uvs;
_m.triangles = tris;
_m.RecalculateNormals();
// Assign the mesh to the MeshFilter
_mf.mesh = _m;
}
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 4531c25db4bb643a79ea6ef810dcf35a
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c14f65469986a904ab38a5e365e690dd
folderAsset: yes
timeCreated: 1451984948
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 189dbdd3248575540a24aa065579f63f
folderAsset: yes
timeCreated: 1451984948
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c126173dfb3fe1f4fb85a5ce09cdaf26
folderAsset: yes
timeCreated: 1451984949
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 88769265a64f9d04a8b3ae61fcd97460
folderAsset: yes
timeCreated: 1451984949
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,174 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
using ParticlePlayground;
using ParticlePlaygroundLanguage;
[CustomEditor (typeof(PlaygroundTrails))]
public class PlaygroundTrailsInspector : Editor {
PlaygroundTrails trails;
SerializedObject s_trails;
SerializedProperty s_playgroundSystem;
SerializedProperty s_lifetimeColor;
SerializedProperty s_material;
SerializedProperty s_renderMode;
SerializedProperty s_colorMode;
SerializedProperty s_uvMode;
SerializedProperty s_pointArrayAlpha;
SerializedProperty s_billboardTransform;
SerializedProperty s_customRenderScale;
SerializedProperty s_time;
SerializedProperty s_timeWidth;
SerializedProperty s_widthScale;
SerializedProperty s_minVertexDistance;
SerializedProperty s_maxVertexDistance;
SerializedProperty s_maxPathDeviation;
SerializedProperty s_createPointsOnCollision;
SerializedProperty s_maxPoints;
SerializedProperty s_createFirstPointOnParticleBirth;
SerializedProperty s_createLastPointOnParticleDeath;
SerializedProperty s_multithreading;
SerializedProperty s_receiveShadows;
SerializedProperty s_castShadows;
// GUI
public static GUIStyle boxStyle;
public static PlaygroundSettingsC playgroundSettings;
public static PlaygroundLanguageC playgroundLanguage;
public void OnEnable ()
{
trails = target as PlaygroundTrails;
s_trails = new SerializedObject(trails);
s_time = s_trails.FindProperty("time");
s_material = s_trails.FindProperty("material");
s_lifetimeColor = s_trails.FindProperty("lifetimeColor");
s_colorMode = s_trails.FindProperty("colorMode");
s_uvMode = s_trails.FindProperty("uvMode");
s_pointArrayAlpha = s_trails.FindProperty("pointArrayAlpha");
s_renderMode = s_trails.FindProperty("renderMode");
s_billboardTransform = s_trails.FindProperty("billboardTransform");
s_customRenderScale = s_trails.FindProperty("customRenderScale");
s_timeWidth = s_trails.FindProperty("timeWidth");
s_widthScale = s_trails.FindProperty("widthScale");
s_minVertexDistance = s_trails.FindProperty("minVertexDistance");
s_maxVertexDistance = s_trails.FindProperty("maxVertexDistance");
s_maxPathDeviation = s_trails.FindProperty("maxPathDeviation");
s_createPointsOnCollision = s_trails.FindProperty("createPointsOnCollision");
s_maxPoints = s_trails.FindProperty("maxPoints");
s_createFirstPointOnParticleBirth = s_trails.FindProperty("createFirstPointOnParticleBirth");
s_createLastPointOnParticleDeath = s_trails.FindProperty("createLastPointOnParticleDeath");
s_playgroundSystem = s_trails.FindProperty("playgroundSystem");
s_multithreading = s_trails.FindProperty("multithreading");
s_receiveShadows = s_trails.FindProperty("receiveShadows");
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6
s_castShadows = s_trails.FindProperty("castShadows");
#else
s_castShadows = s_trails.FindProperty("shadowCastingMode");
#endif
playgroundSettings = PlaygroundSettingsC.GetReference();
playgroundLanguage = PlaygroundSettingsC.GetLanguage();
// Issue a quick refresh
if (!EditorApplication.isPlaying && Selection.activeTransform!=null)
{
trails.ResetTrails();
}
}
public override void OnInspectorGUI ()
{
if (boxStyle==null)
boxStyle = GUI.skin.FindStyle("box");
s_trails.UpdateIfDirtyOrScript();
bool hasParticleSystem = trails.playgroundSystem != null;
if (!hasParticleSystem)
EditorGUILayout.HelpBox(playgroundLanguage.missingParticleSystemWarning, MessageType.Warning);
EditorGUILayout.BeginVertical (boxStyle);
playgroundSettings.playgroundTrailsFoldout = GUILayout.Toggle(playgroundSettings.playgroundTrailsFoldout, playgroundLanguage.playgroundTrails, EditorStyles.foldout);
if (playgroundSettings.playgroundTrailsFoldout)
{
EditorGUILayout.BeginVertical (boxStyle);
// Time foldout
if (GUILayout.Button(playgroundLanguage.time+" ("+s_time.floatValue.ToString ("F1")+")", EditorStyles.toolbarDropDown)) playgroundSettings.trailsTimeFoldout=!playgroundSettings.trailsTimeFoldout;
if (playgroundSettings.trailsTimeFoldout)
{
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(s_time, new GUIContent(playgroundLanguage.time));
EditorGUILayout.Separator();
}
// Width foldout
if (GUILayout.Button(playgroundLanguage.width+" ("+s_widthScale.floatValue.ToString ("F1")+")", EditorStyles.toolbarDropDown)) playgroundSettings.trailsWidthFoldout=!playgroundSettings.trailsWidthFoldout;
if (playgroundSettings.trailsWidthFoldout)
{
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(s_timeWidth, new GUIContent(playgroundLanguage.timeWidth));
EditorGUILayout.PropertyField(s_widthScale, new GUIContent(playgroundLanguage.widthScale));
EditorGUILayout.Separator();
}
// Rendering foldout
if (GUILayout.Button(playgroundLanguage.rendering+" ("+trails.renderMode+")", EditorStyles.toolbarDropDown)) playgroundSettings.trailsRenderingFoldout=!playgroundSettings.trailsRenderingFoldout;
if (playgroundSettings.trailsRenderingFoldout)
{
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(s_material, new GUIContent(playgroundLanguage.material));
EditorGUILayout.PropertyField(s_lifetimeColor, new GUIContent(playgroundLanguage.lifetimeColor));
EditorGUILayout.PropertyField(s_colorMode, new GUIContent(playgroundLanguage.colorMode));
if (trails.colorMode == TrailColorMode.Lifetime)
EditorGUILayout.PropertyField(s_pointArrayAlpha, new GUIContent(playgroundLanguage.pointArrayAlpha));
EditorGUILayout.PropertyField(s_uvMode, new GUIContent(playgroundLanguage.uvMode));
EditorGUILayout.PropertyField(s_renderMode, new GUIContent(playgroundLanguage.renderMode));
if (trails.renderMode == TrailRenderMode.Billboard)
EditorGUILayout.PropertyField(s_billboardTransform, new GUIContent(playgroundLanguage.billboardTransform));
else if (trails.renderMode == TrailRenderMode.CustomRenderScale)
EditorGUILayout.PropertyField(s_customRenderScale, new GUIContent(playgroundLanguage.customRenderScale));
EditorGUILayout.PropertyField(s_castShadows, new GUIContent(playgroundLanguage.castShadows));
EditorGUILayout.PropertyField(s_receiveShadows, new GUIContent(playgroundLanguage.receiveShadows));
EditorGUILayout.Separator();
}
// Point creation foldout
if (GUILayout.Button(playgroundLanguage.pointCreation+" ("+s_maxPoints.intValue+")", EditorStyles.toolbarDropDown)) playgroundSettings.trailsPointCreationFoldout=!playgroundSettings.trailsPointCreationFoldout;
if (playgroundSettings.trailsPointCreationFoldout)
{
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(s_maxPoints, new GUIContent(playgroundLanguage.maximumPoints));
EditorGUILayout.PropertyField(s_minVertexDistance, new GUIContent(playgroundLanguage.minimumVertexDistance));
EditorGUILayout.PropertyField(s_maxVertexDistance, new GUIContent(playgroundLanguage.maximumVertexDistance));
EditorGUILayout.PropertyField(s_maxPathDeviation, new GUIContent(playgroundLanguage.maximumPathDeviation));
EditorGUILayout.PropertyField(s_createFirstPointOnParticleBirth, new GUIContent(playgroundLanguage.createFirstPointOnParticleBirth));
EditorGUILayout.PropertyField(s_createLastPointOnParticleDeath, new GUIContent(playgroundLanguage.createLastPointOnParticleDeath));
EditorGUILayout.PropertyField(s_createPointsOnCollision, new GUIContent(playgroundLanguage.createPointsOnCollision));
EditorGUILayout.Separator();
}
// Advanced foldout
if (GUILayout.Button(playgroundLanguage.advanced, EditorStyles.toolbarDropDown)) playgroundSettings.trailsAdvancedFoldout=!playgroundSettings.trailsAdvancedFoldout;
if (playgroundSettings.trailsAdvancedFoldout)
{
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(s_playgroundSystem, new GUIContent(playgroundLanguage.particleSystem));
EditorGUILayout.PropertyField(s_multithreading, new GUIContent(playgroundLanguage.multithreading));
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
s_trails.ApplyModifiedProperties();
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 302cc09ed3fd7488693e16d806a7665f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,425 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace ParticlePlayground {
[Serializable]
public class ParticlePlaygroundTrail
{
/// <summary>
/// Determines if this ParticlePlaygroundTrail should update.
/// </summary>
[HideInInspector] public bool update = true;
/// <summary>
/// The GameObject of this trail.
/// </summary>
[HideInInspector] public GameObject trailGameObject;
/// <summary>
/// The Transform of this trail.
/// </summary>
[HideInInspector] public Transform trailTransform;
/// <summary>
/// The Mesh Renderer component of this trail.
/// </summary>
[HideInInspector] public MeshRenderer trailRenderer;
/// <summary>
/// The Mesh Filter component of this trail.
/// </summary>
[HideInInspector] public MeshFilter trailMeshFilter;
/// <summary>
/// The Mesh component of this trail.
/// </summary>
[HideInInspector] public Mesh trailMesh;
/// <summary>
/// The particle this trail is following.
/// </summary>
[HideInInspector] public int particleId;
/// <summary>
/// The minimum point cache limit.
/// </summary>
[HideInInspector] public int minPointCache = 2;
/// <summary>
/// The maximum point cache limit.
/// </summary>
[HideInInspector] public int maxPointCache = 32767;
[NonSerialized] public List<TrailPoint> trailPoints = new List<TrailPoint>();
[NonSerialized] public Vector3[] meshVerticesCache;
[NonSerialized] public Vector3[] meshNormalsCache;
[NonSerialized] public Vector2[] meshUvsCache;
[NonSerialized] public int[] meshTrianglesCache;
[NonSerialized] public Color32[] meshColorsCache;
private int _pointCache = 200;
private int _birthIterator;
private int _deathIterator;
private float _particleTime;
private Vector3 _particlePosition;
private Vector3 _previousParticlePosition;
private Vector3 _particleDirection;
private Vector3 _lastAddedPointPosition;
private Vector3 _lastAddedPointDirection;
private bool _isDead = false;
private bool _isReady = false;
private float _timeCached;
/// <summary>
/// Initializes a new instance of the <see cref="ParticlePlayground.ParticlePlaygroundTrail"/> class.
/// </summary>
public ParticlePlaygroundTrail ()
{
UpdateCache();
}
/// <summary>
/// Initializes a new instance of the <see cref="ParticlePlayground.ParticlePlaygroundTrail"/> class and specifies the available point cache.
/// The mesh's available vertices, normals, uvs, triangles and colors will be based on the specified point cache.
/// </summary>
/// <param name="pointCache">Mesh point cache (vertices, normals and uvs will be multiplied by two, triangles by six).</param>
public ParticlePlaygroundTrail (int pointCache)
{
_pointCache = Mathf.Clamp (pointCache, minPointCache, maxPointCache);
UpdateCache();
}
/// <summary>
/// Updates the mesh cache. This is done upon creation where cache lengths will be based on the point cache.
/// </summary>
public void UpdateCache ()
{
_timeCached = PlaygroundC.globalTime;
meshVerticesCache = new Vector3[_pointCache*2];
meshNormalsCache = new Vector3[_pointCache*2];
meshUvsCache = new Vector2[_pointCache*2];
meshTrianglesCache = new int[(_pointCache-1)*6];
meshColorsCache = new Color32[_pointCache*2];
_isReady = true;
}
/// <summary>
/// Updates the trail mesh. This is done each frame from the main-thread.
/// </summary>
public void UpdateMesh ()
{
trailMesh.Clear();
trailMesh.vertices = meshVerticesCache;
trailMesh.uv = meshUvsCache;
trailMesh.triangles = meshTrianglesCache;
trailMesh.colors32 = meshColorsCache;
trailMesh.normals = meshNormalsCache;
}
/// <summary>
/// Recalculates the bounds on the trail mesh. This is done automatically when triangles are set into a mesh.
/// </summary>
public void RecalculateBounds ()
{
trailMesh.RecalculateBounds();
}
/// <summary>
/// Clears the trail mesh.
/// </summary>
public void ClearMesh ()
{
_birthIterator = 0;
_deathIterator = 0;
meshVerticesCache = new Vector3[0];
meshNormalsCache = new Vector3[0];
meshUvsCache = new Vector2[0];
meshTrianglesCache = new int[0];
meshColorsCache = new Color32[0];
}
/// <summary>
/// Sets the first point in the trail. This will add two initial points, one starting point and one ending point which will follow the assigned particle.
/// </summary>
/// <param name="pos">Position.</param>
/// <param name="dir">Direction.</param>
/// <param name="startWidth">Start width.</param>
/// <param name="lifetime">Lifetime.</param>
public void SetFirstPoint (Vector3 pos, Vector3 dir, float startWidth, float lifetime, float creationTime)
{
_particleDirection = dir;
AddPoint (pos, startWidth, lifetime, creationTime);
AddPoint (pos, startWidth, lifetime, creationTime);
_birthIterator = 2;
_deathIterator = 0;
}
/// <summary>
/// Sets the last point in the trail and then kills it. This will add one last point which won't follow the assigned particle.
/// </summary>
/// <param name="pos">Position.</param>
/// <param name="dir">Direction.</param>
/// <param name="startWidth">Start width.</param>
/// <param name="lifetime">Lifetime.</param>
public void SetLastPoint (Vector3 pos, Vector3 dir, float startWidth, float lifetime, float creationTime)
{
_particleDirection = dir;
AddPoint (pos, startWidth, lifetime, creationTime);
Die ();
}
/// <summary>
/// Adds a point into the end of the trail.
/// </summary>
/// <param name="position">Position.</param>
/// <param name="width">Width.</param>
/// <param name="lifetime">Lifetime.</param>
public void AddPoint (Vector3 position, float width, float lifetime, float creationTime)
{
if (_birthIterator >= _pointCache || _isDead)
return;
trailPoints.Add (new TrailPoint(position, lifetime, width, creationTime));
AddPoint(position);
}
/// <summary>
/// Adds a point into the end of the trail.
/// </summary>
/// <param name="position">Position.</param>
/// <param name="velocity">Initial Velocity.</param>
/// <param name="width">Width.</param>
/// <param name="lifetime">Lifetime.</param>
public void AddPoint (Vector3 position, Vector3 velocity, float width, float lifetime, float creationTime)
{
if (_birthIterator >= _pointCache || _isDead)
return;
trailPoints.Add (new TrailPoint(position, velocity, lifetime, width, creationTime));
AddPoint(position);
}
void AddPoint (Vector3 position)
{
_lastAddedPointDirection = position-_previousParticlePosition;
_lastAddedPointPosition = position;
meshVerticesCache[_birthIterator*2] = position;
meshVerticesCache[(_birthIterator*2)+1] = position;
meshNormalsCache[_birthIterator*2] = -Vector3.forward;
meshNormalsCache[(_birthIterator*2)+1] = -Vector3.forward;
meshUvsCache[_birthIterator*2] = new Vector2(1f, 0f);
meshUvsCache[(_birthIterator*2)+1] = new Vector2(0f, 1f);
meshColorsCache[_birthIterator*2] = new Color32();
meshColorsCache[(_birthIterator*2)+1] = new Color32();
if (trailPoints.Count>1) {
int vertexIndex = (_birthIterator)*2;
int triIndex = (_birthIterator-1)*6;
meshTrianglesCache[triIndex] = vertexIndex -2;
meshTrianglesCache[triIndex+1] = vertexIndex -1;
meshTrianglesCache[triIndex+2] = vertexIndex;
meshTrianglesCache[triIndex+3] = vertexIndex;
meshTrianglesCache[triIndex+4] = vertexIndex -1;
meshTrianglesCache[triIndex+5] = vertexIndex +1;
}
NextPoint();
}
public void RemovePoint (int index)
{
if (_deathIterator < _birthIterator-2)
{
meshVerticesCache[_deathIterator*2] = meshVerticesCache[(_deathIterator*2)+2];
meshVerticesCache[(_deathIterator*2)+1] = meshVerticesCache[(_deathIterator*2)+3];
meshColorsCache[_deathIterator*2] = new Color32();
meshColorsCache[(_deathIterator*2)+1] = new Color32();
_deathIterator++;
}
}
/// <summary>
/// Sets the color at specified index. This is done automatically through the trail calculation loop.
/// </summary>
/// <param name="index">Index.</param>
/// <param name="color">Color.</param>
public void SetColor (int index, Color32 color)
{
meshColorsCache[index*2] = color;
meshColorsCache[(index*2)+1] = color;
}
/// <summary>
/// Makes the iterator jump to next point. The iterator controls which point is following the particle.
/// </summary>
public void NextPoint ()
{
_birthIterator++;
}
/// <summary>
/// Gets the paired particle's current time.
/// </summary>
/// <returns>The paired particle's current time.</returns>
public float GetParticleTime ()
{
return _particleTime;
}
/// <summary>
/// Sets the paired particle's current time.
/// </summary>
/// <param name="time">Time.</param>
public void SetParticleTime (float time)
{
_particleTime = time;
}
/// <summary>
/// Gets the paired particle's current position.
/// </summary>
/// <returns>The paired particle's position.</returns>
public Vector3 GetParticlePosition ()
{
return _particlePosition;
}
/// <summary>
/// Sets the paired particle's position.
/// </summary>
/// <param name="position">Position.</param>
public void SetParticlePosition (Vector3 position)
{
if (_isDead)
return;
_previousParticlePosition = _particlePosition;
_particlePosition = position;
_particleDirection = (position-_previousParticlePosition).normalized;
}
/// <summary>
/// Sets the paired particle's direction.
/// </summary>
/// <param name="direction">Direction.</param>
public void SetParticleDirection (Vector3 direction)
{
_particleDirection = direction;
}
/// <summary>
/// Gets the last added point position.
/// </summary>
/// <returns>The last added point position.</returns>
public Vector3 GetLastAddedPointPosition ()
{
return _lastAddedPointPosition;
}
/// <summary>
/// Gets the direction of the particle.
/// </summary>
/// <returns>The particle direction.</returns>
public Vector3 GetParticleDirection ()
{
return _particleDirection;
}
public Vector3 GetLastAddedPointDirection ()
{
return _lastAddedPointDirection;
}
/// <summary>
/// Gets the current path deviation angle based on the last added point direction and the current direction of the particle.
/// </summary>
/// <returns>The path deviation angle.</returns>
public float GetPathDeviation () {
return Vector3.Angle(_lastAddedPointDirection, _particleDirection);
}
/// <summary>
/// Gets the birth iterator used for iterating through the mesh arrays to set new vertex positions.
/// </summary>
/// <returns>The birth iterator.</returns>
public int GetBirthIterator ()
{
return _birthIterator;
}
/// <summary>
/// Gets the death iterator used for iterating through the mesh arrays to remove old vertex positions.
/// </summary>
/// <returns>The death iterator.</returns>
public int GetDeathIterator ()
{
return _deathIterator;
}
/// <summary>
/// Gets the point cache amount. The point cache is set at the trail's creation and determines how many vertices, triangles, uvs, normals and colors the trail mesh can have.
/// </summary>
/// <returns>The point cache amount.</returns>
public int GetPointCache ()
{
return _pointCache;
}
/// <summary>
/// Gets the time this trail was cached.
/// </summary>
/// <returns>The cached time.</returns>
public float TimeCached ()
{
return _timeCached;
}
/// <summary>
/// Determines whether this trail can remove a point at specified index.
/// </summary>
/// <returns><c>true</c> if this trail can remove a point at the specified index; otherwise, <c>false</c>.</returns>
/// <param name="point">Point.</param>
public bool CanRemovePoint (int point)
{
return trailPoints[point].CanRemove();
}
/// <summary>
/// Determines whether this trail can be removed. This will check if the last point in the trail has reached the end of its lifetime.
/// </summary>
/// <returns><c>true</c> if this trail can be removed; otherwise, <c>false</c>.</returns>
public bool CanRemoveTrail ()
{
if (trailPoints.Count == 0 || !_isReady)
return false;
return trailPoints[trailPoints.Count-1].CanRemove();
}
/// <summary>
/// Wakes up the trail from being dead.
/// </summary>
public void WakeUp ()
{
_isDead = false;
}
/// <summary>
/// Makes the trail stop following its assigned particle.
/// </summary>
public void Die ()
{
_isDead = true;
}
/// <summary>
/// Determines whether this trail is dead.
/// </summary>
/// <returns><c>true</c> if this trail is dead; otherwise, <c>false</c>.</returns>
public bool IsDead ()
{
return _isDead;
}
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 69a95d91e31314835b480a1e0e499ed5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,77 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace ParticlePlayground {
public class TrailPoint
{
public Vector3 position;
public Vector3 velocity;
public float lifetime;
public float startLifetime;
public float width;
bool _canRemove = false;
float _lastTimeUpdated;
public TrailPoint (Vector3 position, float startLifetime, float creationTime)
{
this.position = position;
this.lifetime = startLifetime;
this.startLifetime = startLifetime;
this.width = 0;
_lastTimeUpdated = creationTime;
}
public TrailPoint (Vector3 position, float startLifetime, float width, float creationTime)
{
this.position = position;
this.lifetime = startLifetime;
this.startLifetime = startLifetime;
this.width = width;
_lastTimeUpdated = creationTime;
}
public TrailPoint (Vector3 position, Vector3 velocity, float startLifetime, float width, float creationTime)
{
this.position = position;
this.lifetime = startLifetime;
this.startLifetime = startLifetime;
this.width = width;
this.velocity = velocity;
_lastTimeUpdated = creationTime;
}
public void Update (float updateTime, float width)
{
lifetime -= updateTime-_lastTimeUpdated;
if (lifetime <= 0)
{
_canRemove = true;
lifetime = 0;
}
this.width = width;
_lastTimeUpdated = updateTime;
}
/// <summary>
/// Gets the normalized lifetime of this trail point.
/// </summary>
/// <returns>The normalized lifetime.</returns>
public float GetNormalizedLifetime () {
return 1f-(lifetime/startLifetime);
}
/// <summary>
/// Determines whether this point can be removed.
/// </summary>
/// <returns><c>true</c> if this point can be removed; otherwise, <c>false</c>.</returns>
public bool CanRemove () {
return _canRemove;
}
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d13961b3ce5d04e079a2f25f5b542a50
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,25 @@
using UnityEngine;
using System.Collections;
using ParticlePlayground;
[ExecuteInEditMode()]
public class PlaygroundTrailParent : MonoBehaviour {
public PlaygroundTrails trailsReference;
private GameObject _gameObject;
void Awake () {
_gameObject = gameObject;
}
void Update () {
if (_gameObject != trailsReference.GetParentGameObject())
{
if (Application.isPlaying)
Destroy (_gameObject);
else
DestroyImmediate(_gameObject);
}
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 500796a8d01a74b6aae149cd39a714f8
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,853 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace ParticlePlayground {
[ExecuteInEditMode()]
public class PlaygroundTrails : MonoBehaviour {
/// <summary>
/// The particle system this Playground Trail will follow.
/// </summary>
[HideInInspector] public PlaygroundParticlesC playgroundSystem;
/// <summary>
/// The material of each trail.
/// </summary>
[HideInInspector] public Material material;
/// <summary>
/// The lifetime color of all trails.
/// </summary>
[HideInInspector] public Gradient lifetimeColor = new Gradient();
/// <summary>
/// The point array alpha determines the alpha level over the trail points. This is a normalized value where 1 on the x-axis means all points, 1 on the y-axis means full alpha.
/// </summary>
[HideInInspector] public AnimationCurve pointArrayAlpha;
/// <summary>
/// The mode to color the trails with. If TrailColorMode.Lifetime is selected the coloring will be based on each point's lifetime. If TrailColorMode.PointArray is selected the coloring will be based on the points in the array.
/// </summary>
[HideInInspector] public TrailColorMode colorMode;
/// <summary>
/// The uv mode.
/// </summary>
[HideInInspector] public TrailUvMode uvMode;
/// <summary>
/// Determines the render mode of the trail. This sets the rotation direction of the trail points.
/// </summary>
[HideInInspector] public TrailRenderMode renderMode;
/// <summary>
/// The transform to billboard towards if renderMode is set to TrailRenderMode.Billboard. If none is set this will default to the Main Camera transform.
/// </summary>
[HideInInspector] public Transform billboardTransform;
/// <summary>
/// The custom render scale if renderMode is set to TrailRenderMode.CustomRenderScale. This ables you to set the normal direction (with multiplier) of the trails.
/// </summary>
[HideInInspector] public Vector3 customRenderScale = Vector3.one;
/// <summary>
/// Determines if the trails should receive shadows. Note that the shader of the material needs to support this.
/// </summary>
[HideInInspector] public bool receiveShadows;
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6
/// <summary>
/// Determines if the trails should cast shadows (Unity 4). Note that the shader of the material needs to support this.
/// </summary>
[HideInInspector] public bool castShadows = false;
#else
/// <summary>
/// Determines if the trails should cast shadows. Note that the shader of the material needs to support this.
/// </summary>
[HideInInspector] public UnityEngine.Rendering.ShadowCastingMode shadowCastingMode;
#endif
/// <summary>
/// The time vertices is living on the trail (determines length).
/// </summary>
[HideInInspector] public float time = 3f;
/// <summary>
/// The width over normalized lifetime.
/// </summary>
public AnimationCurve timeWidth;
/// <summary>
/// The scale of start- and end width.
/// </summary>
public float widthScale = .1f;
/// <summary>
/// The minimum distance before new vertices can be created.
/// </summary>
public float minVertexDistance = .1f;
/// <summary>
/// The maximum distance before forcing new vertices.
/// </summary>
public float maxVertexDistance = 100f;
/// <summary>
/// The maximum forward path deviation before forcing new vertices.
/// </summary>
public float maxPathDeviation = 1f;
/// <summary>
/// Determines if points should be created upon particle collision.
/// </summary>
public bool createPointsOnCollision = false;
/// <summary>
/// The maximum available points able to be created by this Playground Trail. This will determine the generation of built-in arrays needed to remain efficient in memory consumption.
/// The trail is made out of points where vertices are drawn in between, two points is the minimum to be able to draw a trail, this represents 4 vertices and 6 triangles.
/// </summary>
public int maxPoints = 100;
/// <summary>
/// Determines if first point should be created immediately on particle birth, otherwise this will be created during the trail calculation routine.
/// This has affect on when the trail starts as the particle may have moved when the first point is created. If your particle source is moving you may want to leave this setting off to not create a first skewed trail point.
/// </summary>
public bool createFirstPointOnParticleBirth = false;
/// <summary>
/// Determines if a last point on the trail should be created when its assigned particle dies.
/// </summary>
public bool createLastPointOnParticleDeath = false;
/// <summary>
/// Determines if the Playground Trails should run asynchronously on a separate thread. This will go through the selected Thread Pool Method in the Playground Manager (PlaygroundC).
/// </summary>
public bool multithreading = true;
/// <summary>
/// The reference to the birth event on the assigned Particle Playground system.
/// </summary>
[HideInInspector] public PlaygroundEventC birthEvent;
/// <summary>
/// The reference to the death event on the assigned Particle Playground system.
/// </summary>
[HideInInspector] public PlaygroundEventC deathEvent;
/// <summary>
/// The reference to the collision event on the assigned Particle Playground system.
/// </summary>
[HideInInspector] public PlaygroundEventC collisionEvent;
/// <summary>
/// The list of trails following each particle.
/// </summary>
private List<ParticlePlaygroundTrail> _trails = new List<ParticlePlaygroundTrail>();
private Transform _parentTransform;
private GameObject _parentGameObject;
private Material _materialCache;
private float _calculationStartTime;
private int _currentParticleCount;
private float _currentParticleMinLifetime;
private float _currentParticleMaxLifetime;
private bool _localSpace;
private Vector3 _billboardTransformPosition;
private object _locker = new object();
private bool _isDoneThread = true;
private Matrix4x4 _localMatrix;
/// <summary>
/// The birth queue of trails. This will be added to whenever a particle births. As a Particle Playground system can birth particles and send particle events asynchronously a thread safe queue is needed to create the trails.
/// </summary>
readonly Queue<TrailParticleInfo> _birthQueue = new Queue<TrailParticleInfo>();
/****************************************************************************
Monobehaviours
****************************************************************************/
void OnEnable ()
{
// Cache reference to the Particle Playground system
if (playgroundSystem == null)
playgroundSystem = GetComponent<PlaygroundParticlesC>();
// Cache a reference to the Main Camera if billboardTransform isn't assigned
if (billboardTransform == null)
billboardTransform = Camera.main.transform;
// Set the initial material
if (material == null)
{
material = new Material(Shader.Find("Playground/Vertex Color"));
_materialCache = material;
}
// Reset the trails
ResetTrails();
// Add the required birth/death/collision events
AddRequiredParticleEvents();
// Setup default time width keys
if (timeWidth == null)
timeWidth = new AnimationCurve(DefaultWidthKeys());
// Setup default point array alpha keys
if (pointArrayAlpha == null)
pointArrayAlpha = new AnimationCurve(DefaultWidthKeys());
_isDoneThread = true;
}
void OnDisable ()
{
// Destroy all trails
DestroyAllTrails();
// Remove the required events
RemoveRequiredEvents();
}
void OnDestroy ()
{
// Destroy all trails
DestroyAllTrails();
// Remove the required events
RemoveRequiredEvents();
}
void Update ()
{
// Clamp values
maxPoints = Mathf.Clamp (maxPoints, 2, 32767);
// Set asynchronous available values
if (billboardTransform != null)
_billboardTransformPosition = billboardTransform.position;
// Early out if no particles exist yet
if (playgroundSystem == null || !playgroundSystem.IsReady() || playgroundSystem.IsSettingParticleCount() || playgroundSystem.IsSettingLifetime() || playgroundSystem.particleCache == null || playgroundSystem.particleCache.Length == 0)
return;
// Reset trails if a crucial state is changed
if (_currentParticleCount != playgroundSystem.particleCount || _currentParticleMinLifetime != playgroundSystem.lifetimeMin || _currentParticleMaxLifetime != playgroundSystem.lifetime || _localSpace != (playgroundSystem.shurikenParticleSystem.simulationSpace == ParticleSystemSimulationSpace.Local))
ResetTrails();
// Set calculation matrix if this is local space
if (_localSpace)
_localMatrix.SetTRS(playgroundSystem.particleSystemTransform.position, playgroundSystem.particleSystemTransform.rotation, playgroundSystem.particleSystemTransform.lossyScale);
// Check material
if (material != _materialCache)
SetMaterial(material);
// Remove any trails that has ended
if (_isDoneThread)
{
for (int i = 0; i<_trails.Count; i++)
{
if (_trails[i].trailPoints != null && _trails[i].trailPoints.Count > 1 && _trails[i].trailPoints[_trails[i].trailPoints.Count-1] != null && _trails[i].CanRemoveTrail())
{
RemoveTrail(i);
i--;
if (i<0) i = 0;
continue;
}
}
}
// Consume the particle birth queue
while (_birthQueue.Count>0)
AddTrail(_birthQueue.Dequeue());
// Update all trail meshes and their render settings
for (int i = 0; i<_trails.Count; i++)
{
ParticlePlaygroundTrail trail = _trails[i];
// Set shadow casting/receiving
trail.trailRenderer.receiveShadows = receiveShadows;
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6
trail.trailRenderer.castShadows = castShadows;
#else
trail.trailRenderer.shadowCastingMode = shadowCastingMode;
#endif
if (_isDoneThread)
trail.UpdateMesh();
}
// Finally calculate all trails
if (multithreading)
{
if (_isDoneThread)
{
_calculationStartTime = Application.isPlaying? Time.time : Time.realtimeSinceStartup;
_isDoneThread = false;
PlaygroundC.RunAsync(()=>{
lock (_locker)
{
if (_isDoneThread) return;
CalculateTrail();
_isDoneThread = true;
}
});
}
}
else
{
_calculationStartTime = Application.isPlaying? Time.time : Time.realtimeSinceStartup;
CalculateTrail();
}
}
// Prevent build-up of the birth queue while Editor is out of focus
#if UNITY_EDITOR
public void OnApplicationPause (bool pauseStatus)
{
if (!pauseStatus && !UnityEditor.EditorApplication.isPlaying)
{
_birthQueue.Clear();
}
}
#endif
/****************************************************************************
Event Listeners
****************************************************************************/
/// <summary>
/// This function will be called whenever a particle is birthed.
/// </summary>
/// <param name="particle">The birthed particle.</param>
void OnParticleBirthEvent (PlaygroundEventParticle particle)
{
_birthQueue.Enqueue (new TrailParticleInfo(particle.particleId, particle.position, particle.velocity));
}
/// <summary>
/// This function will be called whenever a particle has died.
/// </summary>
/// <param name="particle">The particle which died.</param>
void OnParticleDeathEvent (PlaygroundEventParticle particle)
{
int trailIndex = GetOldestTrailWithParticleId(particle.particleId);
if (trailIndex > -1)
{
if (createLastPointOnParticleDeath)
{
_trails[trailIndex].SetLastPoint(particle.position, particle.velocity, EvaluateWidth(0), time, _calculationStartTime);
}
else
{
_trails[trailIndex].SetParticlePosition(particle.position);
_trails[trailIndex].Die();
}
}
}
/// <summary>
/// This function will be called whenever a particle is colliding.
/// </summary>
/// <param name="particle">The collided particle.</param>
void OnParticleCollisionEvent (PlaygroundEventParticle particle)
{
if (createPointsOnCollision)
{
int trailIndex = GetNewestTrailWithParticleId (particle.particleId);
if (trailIndex < 0)
return;
ParticlePlaygroundTrail trailAtIndex = _trails[trailIndex];
trailAtIndex.AddPoint (playgroundSystem.particleCache[particle.particleId].position, EvaluateWidth(0), time, _calculationStartTime);
}
}
/// <summary>
/// Gets the birth event this Playground Trail is listening to.
/// </summary>
/// <returns>The particle birth event.</returns>
public PlaygroundEventC GetBirthEvent () {return birthEvent;}
/// <summary>
/// Gets the death event this Playground Trail is listening to.
/// </summary>
/// <returns>The particle death event.</returns>
public PlaygroundEventC GetDeathEvent () {return deathEvent;}
/// <summary>
/// Gets the collision event this Playground Trail is listening to.
/// </summary>
/// <returns>The particle collision event.</returns>
public PlaygroundEventC GetCollisionEvent () {return collisionEvent;}
/// <summary>
/// Adds the required particle events to track particles.
/// </summary>
public void AddRequiredParticleEvents ()
{
if (playgroundSystem != null)
{
// Hookup events
birthEvent = GetEventFromType(EVENTTYPEC.Birth);
if (birthEvent == null)
{
birthEvent = PlaygroundC.CreateEvent(playgroundSystem);
birthEvent.broadcastType = EVENTBROADCASTC.EventListeners;
birthEvent.eventType = EVENTTYPEC.Birth;
}
birthEvent.particleEvent += OnParticleBirthEvent;
deathEvent = GetEventFromType(EVENTTYPEC.Death);
if (deathEvent == null)
{
deathEvent = PlaygroundC.CreateEvent(playgroundSystem);
deathEvent.broadcastType = EVENTBROADCASTC.EventListeners;
deathEvent.eventType = EVENTTYPEC.Death;
}
deathEvent.particleEvent += OnParticleDeathEvent;
collisionEvent = GetEventFromType(EVENTTYPEC.Collision);
if (collisionEvent == null)
{
collisionEvent = PlaygroundC.CreateEvent(playgroundSystem);
collisionEvent.broadcastType = EVENTBROADCASTC.EventListeners;
collisionEvent.eventType = EVENTTYPEC.Collision;
}
collisionEvent.particleEvent += OnParticleCollisionEvent;
}
}
/// <summary>
/// Removes the required events to track particles.
/// </summary>
public void RemoveRequiredEvents ()
{
if (playgroundSystem != null)
{
if (birthEvent != null)
{
birthEvent.particleEvent -= OnParticleBirthEvent;
birthEvent = null;
}
if (deathEvent != null)
{
deathEvent.particleEvent -= OnParticleDeathEvent;
deathEvent = null;
}
if (collisionEvent != null)
{
collisionEvent.particleEvent -= OnParticleCollisionEvent;
collisionEvent = null;
}
}
}
/// <summary>
/// Gets the type of event based on the passed in EVETTTYPEC.
/// </summary>
/// <returns>The event of type specified.</returns>
/// <param name="eventType">The event type.</param>
public PlaygroundEventC GetEventFromType (EVENTTYPEC eventType)
{
for (int i = 0; i<playgroundSystem.events.Count; i++)
if (playgroundSystem.events[i].eventType == eventType)
return playgroundSystem.events[i];
return null;
}
/****************************************************************************
Misc functions
****************************************************************************/
/// <summary>
/// Returns a default pair of AnimationCurve Keyframes in X 0 and X 1 at value Y 1.
/// </summary>
/// <returns>The default width keys.</returns>
public Keyframe[] DefaultWidthKeys () {
Keyframe[] keys = new Keyframe[2];
keys[0].time = 0;
keys[1].time = 1f;
keys[0].value = 1f;
keys[1].value = 1f;
return keys;
}
/// <summary>
/// Sets the material of all trails.
/// </summary>
/// <param name="material">The material all trails should get.</param>
public void SetMaterial (Material material) {
for (int i = 0; i<_trails.Count; i++) {
if (_trails[i] != null && _trails[i].trailRenderer != null)
_trails[i].trailRenderer.sharedMaterial = material;
}
_materialCache = material;
}
/// <summary>
/// Evaluates the width at normalized trail time.
/// </summary>
/// <returns>The width at normalized trail time.</returns>
/// <param name="normalizedTime">Normalized time.</param>
public float EvaluateWidth (float normalizedTime) {
return timeWidth.Evaluate(normalizedTime)*widthScale;
}
public Color32 EvaluateColor (float normalizedTime)
{
return lifetimeColor.Evaluate(normalizedTime);
}
public Color32 EvaluateColor (int trailIndex, int trailPointIndex)
{
return lifetimeColor.Evaluate((trailPointIndex*1f) / (_trails[trailIndex].GetBirthIterator()-1));
}
/****************************************************************************
Trail functions
****************************************************************************/
/// <summary>
/// Creates a trail and assigns it to a particle.
/// </summary>
/// <param name="particleInfo">Information about the particle.</param>
public void AddTrail (TrailParticleInfo particleInfo)
{
// Check parent object
if (_parentGameObject == null)
{
_parentGameObject = new GameObject("Playground Trails ("+playgroundSystem.name+")", typeof(PlaygroundTrailParent));
_parentTransform = _parentGameObject.transform;
_parentGameObject.GetComponent<PlaygroundTrailParent>().trailsReference = this;
}
ParticlePlaygroundTrail newTrail = new ParticlePlaygroundTrail(maxPoints);
newTrail.trailGameObject = new GameObject("Playground Trail "+particleInfo.particleId);
newTrail.trailTransform = newTrail.trailGameObject.transform;
newTrail.trailTransform.parent = _parentTransform;
newTrail.trailRenderer = newTrail.trailGameObject.AddComponent<MeshRenderer>();
newTrail.trailMeshFilter = newTrail.trailGameObject.AddComponent<MeshFilter>();
newTrail.trailMesh = new Mesh();
newTrail.trailMesh.MarkDynamic();
newTrail.trailMeshFilter.sharedMesh = newTrail.trailMesh;
newTrail.trailRenderer.sharedMaterial = material;
newTrail.particleId = particleInfo.particleId;
if (createFirstPointOnParticleBirth)
newTrail.SetFirstPoint(particleInfo.position, particleInfo.velocity, EvaluateWidth(0), time, _calculationStartTime);
_trails.Add (newTrail);
}
/// <summary>
/// Gets the oldest trail following the particle id. If the trail is already dead or doesn't contain the particle id -1 will be returned.
/// </summary>
/// <returns>The trail with particle id (-1 if not found).</returns>
/// <param name="particleId">Particle identifier.</param>
public int GetOldestTrailWithParticleId (int particleId)
{
for (int i = 0; i<_trails.Count; i++)
if (_trails[i].particleId == particleId && !_trails[i].IsDead())
return i;
return -1;
}
/// <summary>
/// Gets the newest trail following the particle id. If the trail is already dead or doesn't contain the particle id -1 will be returned.
/// </summary>
/// <returns>The trail with particle id (-1 if not found).</returns>
/// <param name="particleId">Particle identifier.</param>
public int GetNewestTrailWithParticleId (int particleId)
{
for (int i = _trails.Count-1; i>=0; --i)
if (_trails[i].particleId == particleId && !_trails[i].IsDead())
return i;
return -1;
}
/// <summary>
/// Gets the cached parent transform of the trails.
/// </summary>
/// <returns>The parent transform.</returns>
public Transform GetParentTransform ()
{
return _parentTransform;
}
/// <summary>
/// Gets the cached parent game object of the trails.
/// </summary>
/// <returns>The parent game object.</returns>
public GameObject GetParentGameObject ()
{
return _parentGameObject;
}
/// <summary>
/// Stopping the trail will make the trail stop following its assigned particle.
/// </summary>
/// <param name="trailNumber">Trail number.</param>
public void StopTrail (int trailNumber)
{
if (trailNumber < 0)
{
return;
}
_trails[trailNumber].Die();
}
/// <summary>
/// Stops the oldest trail with particle identifier.
/// </summary>
/// <param name="particleId">Particle identifier.</param>
public void StopOldestTrailWithParticleId (int particleId)
{
StopTrail (GetOldestTrailWithParticleId (particleId));
}
/// <summary>
/// Stops the newest trail with particle identifier.
/// </summary>
/// <param name="particleId">Particle identifier.</param>
public void StopNewestTrailWithParticleId (int particleId)
{
StopTrail (GetNewestTrailWithParticleId (particleId));
}
/// <summary>
/// Resets all trails.
/// </summary>
public void ResetTrails () {
DestroyAllTrails();
if (playgroundSystem != null && gameObject.activeInHierarchy)
{
_currentParticleCount = playgroundSystem.particleCount;
_currentParticleMinLifetime = playgroundSystem.lifetimeMin;
_currentParticleMaxLifetime = playgroundSystem.lifetime;
_localSpace = playgroundSystem.shurikenParticleSystem.simulationSpace == ParticleSystemSimulationSpace.Local;
}
_isDoneThread = true;
}
/// <summary>
/// Removes the trail at index.
/// </summary>
/// <param name="index">The trail index.</param>
public void RemoveTrail (int index) {
if (Application.isPlaying)
Destroy(_trails[index].trailGameObject);
else
DestroyImmediate(_trails[index].trailGameObject);
_trails.RemoveAt(index);
}
/// <summary>
/// Destroys all trails and clears out trail list.
/// </summary>
public void DestroyAllTrails () {
foreach (ParticlePlaygroundTrail trail in _trails)
{
if (Application.isPlaying)
Destroy(trail.trailGameObject);
else
DestroyImmediate(trail.trailGameObject);
}
if (_parentGameObject != null)
{
if (Application.isPlaying)
Destroy (_parentGameObject);
else
DestroyImmediate(_parentGameObject);
}
_trails.Clear();
_birthQueue.Clear();
}
/****************************************************************************
Internal
****************************************************************************/
void CalculateTrail ()
{
// Iterate through all trails
for (int i = 0; i<_trails.Count; i++)
{
ParticlePlaygroundTrail trail = _trails[i];
// Skip this trail if it's prepared to be removed
if (trail.CanRemoveTrail())
continue;
if (trail.particleId >= 0 && !trail.IsDead())
{
if (trail.GetBirthIterator()>0)
{
// New point creation
float pointDistance = Vector3.Distance(trail.GetParticlePosition(), trail.GetLastAddedPointPosition());
if (pointDistance>minVertexDistance) {
float pathDeviationAngle = trail.GetPathDeviation();
if (pointDistance>maxVertexDistance || pathDeviationAngle>maxPathDeviation) {
trail.AddPoint(playgroundSystem.particleCache[trail.particleId].position, EvaluateWidth(0), time, _calculationStartTime);
}
}
}
else
{
// First point creation
trail.SetFirstPoint(playgroundSystem.particleCache[trail.particleId].position, playgroundSystem.particleCache[trail.particleId].velocity, EvaluateWidth(0), time, _calculationStartTime);
}
// Set the particle position info
trail.SetParticlePosition(playgroundSystem.particleCache[trail.particleId].position);
}
// Update the trail points
for (int x = 0; x<trail.trailPoints.Count; x++)
{
TrailPoint trailPoint = trail.trailPoints[x];
if (trailPoint.CanRemove())
{
trail.RemovePoint(x);
if (!_localSpace)
continue;
}
float normalizedLifetime = trailPoint.GetNormalizedLifetime();
// Update trail points data
trailPoint.Update (
_calculationStartTime,
EvaluateWidth(normalizedLifetime)
);
// Set end point to follow particle
if (!trail.IsDead() && x==trail.trailPoints.Count-1)
trailPoint.position = trail.GetParticlePosition();
// Rotation of trail points
Vector3 currentPosition = trailPoint.position;
Vector3 nextPosition = x<trail.trailPoints.Count-1? trail.trailPoints[x+1].position : currentPosition + (currentPosition - trail.trailPoints[x-1].position);
Vector3 lookDirection = Vector3.up;
switch (renderMode)
{
case TrailRenderMode.Vertical:
lookDirection = Vector3.forward;
break;
case TrailRenderMode.Billboard:
lookDirection = (_billboardTransformPosition - currentPosition).normalized;
break;
}
// If this is local space then recompute current & next position based on the local matrix
if (_localSpace)
{
currentPosition = _localMatrix.MultiplyPoint3x4(currentPosition);
nextPosition = _localMatrix.MultiplyPoint3x4(nextPosition);
}
Vector3 dir = renderMode != TrailRenderMode.CustomRenderScale? (Vector3.Cross(lookDirection, nextPosition - currentPosition)).normalized : customRenderScale;
Vector3 lPoint = currentPosition + (dir * (trailPoint.width*.5f));
Vector3 rPoint = currentPosition - (dir * (trailPoint.width*.5f));
// Set mesh vertices into the rotated position
trail.meshVerticesCache[x*2] = lPoint;
trail.meshVerticesCache[(x*2)+1] = rPoint;
// Set uv
float uvRatio = uvMode == TrailUvMode.Lifetime? normalizedLifetime : (x*1f) / (trail.GetBirthIterator()-1);
trail.meshUvsCache[x*2] = new Vector2(uvRatio, 0);
trail.meshUvsCache[(x*2)+1] = new Vector2(uvRatio, 1f);
// Update colors
if (colorMode == TrailColorMode.Lifetime)
{
Color32 color = EvaluateColor(normalizedLifetime);
color.a = (byte)(color.a*(pointArrayAlpha.Evaluate((x*1f) / (trail.GetBirthIterator()-1))));
trail.SetColor(x, color);
}
else
trail.SetColor(x, EvaluateColor(i, x));
}
}
}
}
/// <summary>
/// The trail render mode determines how the trail will be rotated.
/// Using billboard will rotate towards the assigned transform position, this is by default the main camera.
/// Horizontal will rotate the points flat on X-Z axis.
/// Vertical will rotate the points flat on X-Y axis.
/// CustomRenderScale is a global world space normal which will multiply the scale on each axis.
/// </summary>
public enum TrailRenderMode
{
/// <summary>
/// Rotate points towards assigned billboard transform.
/// </summary>
Billboard,
/// <summary>
/// Rotate points flat X-Z.
/// </summary>
Horizontal,
/// <summary>
/// Rotate points flat X-Y.
/// </summary>
Vertical,
/// <summary>
/// Creates a custom render rotation/scale.
/// </summary>
CustomRenderScale
}
/// <summary>
/// The trail color mode determines how color should be distributed over a trail.
/// </summary>
public enum TrailColorMode
{
/// <summary>
/// When using TrailColorMode.Lifetime the colors will be set depending on each point's normalized lifetime.
/// </summary>
Lifetime,
/// <summary>
/// When using TrailColorMode.PointArray the colors will be set depending on all the points within the trail, where each point is a normalized value linearly towards the total points.
/// </summary>
PointArray
}
/// <summary>
/// The trail uv mode determines how uv will be distributed over a trail.
/// </summary>
public enum TrailUvMode
{
/// <summary>
/// When using TrailUvMode.Lifetime the uvs will be set depending on each point's normalized lifetime.
/// </summary>
Lifetime,
/// <summary>
/// When using TrailUvMode.PointArray the uvs will be set depending on all the points within the trail, where each point is a normalized value linearly towards the total points.
/// </summary>
PointArray
}
/// <summary>
/// The trail particle info struct contains data about particles to be read by a Playground Trail.
/// </summary>
public struct TrailParticleInfo {
/// <summary>
/// The particle identifier linearly towards the particle system's cached particles.
/// </summary>
public int particleId;
/// <summary>
/// The position of this trail particle.
/// </summary>
public Vector3 position;
/// <summary>
/// The velocity of this trail particle.
/// </summary>
public Vector3 velocity;
/// <summary>
/// Initializes a new instance of the <see cref="ParticlePlayground.TrailParticleInfo"/> struct.
/// </summary>
/// <param name="particleId">Particle identifier.</param>
/// <param name="position">Particle position.</param>
/// <param name="velocity">Particle velocity.</param>
public TrailParticleInfo (int particleId, Vector3 position, Vector3 velocity)
{
this.particleId = particleId;
this.position = position;
this.velocity = velocity;
}
}
}

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 76bc001fa44a547bb86aea579a67ae0d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 0a27dd1d06c3c504583ba9a6fc31f673
folderAsset: yes
timeCreated: 1451984948
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: fb31e28e5e0963e428467e92c4c48753
folderAsset: yes
timeCreated: 1451984948
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: cb29d5dac6f2c2048bfd3819df6838b2
folderAsset: yes
timeCreated: 1451984949
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: e25eb56ec99894d6f8f83d963d324f78
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: b7efd61b3c21e45fc9510abf1dd559bf
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: ea83db8bf09b741a1a203571af766f5f
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 6e01041934bf04f5a88b0272165bf725
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: cb8fe57cd63cd47b78c512b6cb3828f9
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 3dbf42a686ef44cbd8525bda4eb6723d
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: eb30b1d0e444c4556960c7042d29784f
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 17ed52b4e246743928ec1ec2ef8a5699
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: bb3878536081d40a1970f04bcbeb5a1e
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 87036563b73f24d38b515d892cb425ee
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 16c18ec21693744eab6afd5b60915147
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 993cf971063fd48c1843623b82a29438
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: e9116afcaef00450a949d2d983af28cd
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 1824ae714e5244646ad99c05eda63a63
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: c9151beffdd374add86203bb3703c0ce
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 9d653a4f8784a4dc386f5c1cdf37248e
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 32
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 5526d326b5fd746a8835ff5a83332dfb
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 32
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 672fadd0d1245444dba30066ebed196d
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 32
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 866f85a4a947341e79b24a5c788a02ee
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 32
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: 7cbc9b4a9effd4fe9ac8af6a21456512
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 32
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: caf46de47c6d8429e976d93964bf5eac
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 32
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,55 @@
fileFormatVersion: 2
guid: e9e9e5dc11a3347b08009756a9d60739
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 7
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 32
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Some files were not shown because too many files have changed in this diff Show more