import Coffee.Internal
parent
025efcbc5d
commit
a96b6915ef
|
@ -0,0 +1 @@
|
||||||
|
s/Coffee.Internal/Coffee.UIParticleInternal/g
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 53aa3f36032944b3fb1455e774c52396
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8cf8018dee45a4c42a19eec890eaa5b1
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,134 @@
|
||||||
|
#if UNITY_2021_3_0 || UNITY_2021_3_1 || UNITY_2021_3_2 || UNITY_2021_3_3 || UNITY_2021_3_4 || UNITY_2021_3_5 || UNITY_2021_3_6 || UNITY_2021_3_7 || UNITY_2021_3_8 || UNITY_2021_3_9
|
||||||
|
#elif UNITY_2021_3_10 || UNITY_2021_3_11 || UNITY_2021_3_12 || UNITY_2021_3_13 || UNITY_2021_3_14 || UNITY_2021_3_15 || UNITY_2021_3_16 || UNITY_2021_3_17 || UNITY_2021_3_18 || UNITY_2021_3_19
|
||||||
|
#elif UNITY_2021_3_20 || UNITY_2021_3_21 || UNITY_2021_3_22 || UNITY_2021_3_23 || UNITY_2021_3_24 || UNITY_2021_3_25 || UNITY_2021_3_26 || UNITY_2021_3_27 || UNITY_2021_3_28 || UNITY_2021_3_29
|
||||||
|
#elif UNITY_2021_3_30 || UNITY_2021_3_31 || UNITY_2021_3_32 || UNITY_2021_3_33
|
||||||
|
#elif UNITY_2022_2_0 || UNITY_2022_2_1 || UNITY_2022_2_2 || UNITY_2022_2_3 || UNITY_2022_2_4 || UNITY_2022_2_5 || UNITY_2022_2_6 || UNITY_2022_2_7 || UNITY_2022_2_8 || UNITY_2022_2_9
|
||||||
|
#elif UNITY_2022_2_10 || UNITY_2022_2_11 || UNITY_2022_2_12 || UNITY_2022_2_13 || UNITY_2022_2_14
|
||||||
|
#elif UNITY_2021_3 || UNITY_2022_2 || UNITY_2022_3 || UNITY_2023_2_OR_NEWER
|
||||||
|
#define CANVAS_SUPPORT_ALWAYS_GAMMA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
#if UNITY_MODULE_VR
|
||||||
|
using UnityEngine.XR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
internal static class CanvasExtensions
|
||||||
|
{
|
||||||
|
public static bool ShouldGammaToLinearInShader(this Canvas canvas)
|
||||||
|
{
|
||||||
|
return QualitySettings.activeColorSpace == ColorSpace.Linear &&
|
||||||
|
#if CANVAS_SUPPORT_ALWAYS_GAMMA
|
||||||
|
canvas.vertexColorAlwaysGammaSpace;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ShouldGammaToLinearInMesh(this Canvas canvas)
|
||||||
|
{
|
||||||
|
return QualitySettings.activeColorSpace == ColorSpace.Linear &&
|
||||||
|
#if CANVAS_SUPPORT_ALWAYS_GAMMA
|
||||||
|
!canvas.vertexColorAlwaysGammaSpace;
|
||||||
|
#else
|
||||||
|
true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsStereoCanvas(this Canvas canvas)
|
||||||
|
{
|
||||||
|
#if UNITY_MODULE_VR
|
||||||
|
if (FrameCache.TryGet<bool>(canvas, nameof(IsStereoCanvas), out var stereo)) return stereo;
|
||||||
|
|
||||||
|
stereo =
|
||||||
|
canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay && canvas.worldCamera != null
|
||||||
|
&& XRSettings.enabled && !string.IsNullOrEmpty(XRSettings.loadedDeviceName);
|
||||||
|
FrameCache.Set(canvas, nameof(IsStereoCanvas), stereo);
|
||||||
|
return stereo;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the view-projection matrix for a Canvas.
|
||||||
|
/// </summary>
|
||||||
|
public static void GetViewProjectionMatrix(this Canvas canvas, out Matrix4x4 vpMatrix)
|
||||||
|
{
|
||||||
|
canvas.GetViewProjectionMatrix(Camera.MonoOrStereoscopicEye.Mono, out vpMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the view-projection matrix for a Canvas.
|
||||||
|
/// </summary>
|
||||||
|
public static void GetViewProjectionMatrix(this Canvas canvas, Camera.MonoOrStereoscopicEye eye,
|
||||||
|
out Matrix4x4 vpMatrix)
|
||||||
|
{
|
||||||
|
if (FrameCache.TryGet(canvas, nameof(GetViewProjectionMatrix), out vpMatrix)) return;
|
||||||
|
|
||||||
|
canvas.GetViewProjectionMatrix(eye, out var viewMatrix, out var projectionMatrix);
|
||||||
|
vpMatrix = viewMatrix * projectionMatrix;
|
||||||
|
FrameCache.Set(canvas, nameof(GetViewProjectionMatrix), vpMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the view and projection matrices for a Canvas.
|
||||||
|
/// </summary>
|
||||||
|
public static void GetViewProjectionMatrix(this Canvas canvas, out Matrix4x4 vMatrix, out Matrix4x4 pMatrix)
|
||||||
|
{
|
||||||
|
canvas.GetViewProjectionMatrix(Camera.MonoOrStereoscopicEye.Mono, out vMatrix, out pMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the view and projection matrices for a Canvas.
|
||||||
|
/// </summary>
|
||||||
|
public static void GetViewProjectionMatrix(this Canvas canvas, Camera.MonoOrStereoscopicEye eye,
|
||||||
|
out Matrix4x4 vMatrix, out Matrix4x4 pMatrix)
|
||||||
|
{
|
||||||
|
if (FrameCache.TryGet(canvas, "GetViewMatrix", (int)eye, out vMatrix) &&
|
||||||
|
FrameCache.TryGet(canvas, "GetProjectionMatrix", (int)eye, out pMatrix))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get view and projection matrices.
|
||||||
|
Profiler.BeginSample("(COF)[CanvasExt] GetViewProjectionMatrix");
|
||||||
|
var rootCanvas = canvas.rootCanvas;
|
||||||
|
var cam = rootCanvas.worldCamera;
|
||||||
|
if (rootCanvas && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay && cam)
|
||||||
|
{
|
||||||
|
if (eye == Camera.MonoOrStereoscopicEye.Mono)
|
||||||
|
{
|
||||||
|
vMatrix = cam.worldToCameraMatrix;
|
||||||
|
pMatrix = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pMatrix = cam.GetStereoProjectionMatrix((Camera.StereoscopicEye)eye);
|
||||||
|
vMatrix = cam.GetStereoViewMatrix((Camera.StereoscopicEye)eye);
|
||||||
|
pMatrix = GL.GetGPUProjectionMatrix(pMatrix, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var pos = rootCanvas.transform.position;
|
||||||
|
vMatrix = Matrix4x4.TRS(
|
||||||
|
new Vector3(-pos.x, -pos.y, -1000),
|
||||||
|
Quaternion.identity,
|
||||||
|
new Vector3(1, 1, -1f));
|
||||||
|
pMatrix = Matrix4x4.TRS(
|
||||||
|
new Vector3(0, 0, -1),
|
||||||
|
Quaternion.identity,
|
||||||
|
new Vector3(1 / pos.x, 1 / pos.y, -2 / 10000f));
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameCache.Set(canvas, "GetViewMatrix", (int)eye, vMatrix);
|
||||||
|
FrameCache.Set(canvas, "GetProjectionMatrix", (int)eye, pMatrix);
|
||||||
|
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: b0beae5bb1cb142b9ab90dc0d371f026
|
guid: 9dd767b8c0f95478386e7d5079cd44df
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
|
@ -0,0 +1,77 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
internal static class Color32Extensions
|
||||||
|
{
|
||||||
|
private static readonly List<Color32> s_Colors = new List<Color32>();
|
||||||
|
private static byte[] s_LinearToGammaLut;
|
||||||
|
private static byte[] s_GammaToLinearLut;
|
||||||
|
|
||||||
|
public static byte LinearToGamma(this byte self)
|
||||||
|
{
|
||||||
|
if (s_LinearToGammaLut == null)
|
||||||
|
{
|
||||||
|
s_LinearToGammaLut = new byte[256];
|
||||||
|
for (var i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
s_LinearToGammaLut[i] = (byte)(Mathf.LinearToGammaSpace(i / 255f) * 255f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_LinearToGammaLut[self];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte GammaToLinear(this byte self)
|
||||||
|
{
|
||||||
|
if (s_GammaToLinearLut == null)
|
||||||
|
{
|
||||||
|
s_GammaToLinearLut = new byte[256];
|
||||||
|
for (var i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
s_GammaToLinearLut[i] = (byte)(Mathf.GammaToLinearSpace(i / 255f) * 255f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_GammaToLinearLut[self];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LinearToGamma(this Mesh self)
|
||||||
|
{
|
||||||
|
Profiler.BeginSample("(COF)[ColorExt] LinearToGamma (Mesh)");
|
||||||
|
self.GetColors(s_Colors);
|
||||||
|
var count = s_Colors.Count;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var c = s_Colors[i];
|
||||||
|
c.r = c.r.LinearToGamma();
|
||||||
|
c.g = c.g.LinearToGamma();
|
||||||
|
c.b = c.b.LinearToGamma();
|
||||||
|
s_Colors[i] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.SetColors(s_Colors);
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void GammaToLinear(this Mesh self)
|
||||||
|
{
|
||||||
|
Profiler.BeginSample("(COF)[ColorExt] GammaToLinear (Mesh)");
|
||||||
|
self.GetColors(s_Colors);
|
||||||
|
var count = s_Colors.Count;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var c = s_Colors[i];
|
||||||
|
c.r = c.r.GammaToLinear();
|
||||||
|
c.g = c.g.GammaToLinear();
|
||||||
|
c.b = c.b.GammaToLinear();
|
||||||
|
s_Colors[i] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.SetColors(s_Colors);
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: d188d31b140094ebc84a9caafbc7ac71
|
guid: 0ef431b9df32c410ea5fa46be81def6b
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
|
@ -0,0 +1,198 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for Component class.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ComponentExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get components in children of a specific type in the hierarchy of a GameObject.
|
||||||
|
/// </summary>
|
||||||
|
public static T[] GetComponentsInChildren<T>(this Component self, int depth)
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
var results = ListPool<T>.Rent();
|
||||||
|
self.GetComponentsInChildren_Internal(results, depth);
|
||||||
|
var array = results.ToArray();
|
||||||
|
ListPool<T>.Return(ref results);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get components in children of a specific type in the hierarchy of a GameObject.
|
||||||
|
/// </summary>
|
||||||
|
public static void GetComponentsInChildren<T>(this Component self, List<T> results, int depth)
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
results.Clear();
|
||||||
|
self.GetComponentsInChildren_Internal(results, depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetComponentsInChildren_Internal<T>(this Component self, List<T> results, int depth)
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
if (!self || results == null || depth < 0) return;
|
||||||
|
|
||||||
|
var tr = self.transform;
|
||||||
|
if (tr.TryGetComponent<T>(out var t))
|
||||||
|
{
|
||||||
|
results.Add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depth - 1 < 0) return;
|
||||||
|
var childCount = tr.childCount;
|
||||||
|
for (var i = 0; i < childCount; i++)
|
||||||
|
{
|
||||||
|
tr.GetChild(i).GetComponentsInChildren_Internal(results, depth - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or add a component of a specific type to a GameObject.
|
||||||
|
/// </summary>
|
||||||
|
public static T GetOrAddComponent<T>(this Component self) where T : Component
|
||||||
|
{
|
||||||
|
if (!self) return null;
|
||||||
|
return self.TryGetComponent<T>(out var component)
|
||||||
|
? component
|
||||||
|
: self.gameObject.AddComponent<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the root component of a specific type in the hierarchy of a GameObject.
|
||||||
|
/// </summary>
|
||||||
|
public static T GetRootComponent<T>(this Component self) where T : Component
|
||||||
|
{
|
||||||
|
T component = null;
|
||||||
|
var transform = self.transform;
|
||||||
|
while (transform)
|
||||||
|
{
|
||||||
|
if (transform.TryGetComponent<T>(out var c))
|
||||||
|
{
|
||||||
|
component = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform = transform.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a component of a specific type in the parent hierarchy of a GameObject.
|
||||||
|
/// </summary>
|
||||||
|
public static T GetComponentInParent<T>(this Component self, bool includeSelf, Transform stopAfter,
|
||||||
|
Predicate<T> valid)
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
var tr = includeSelf ? self.transform : self.transform.parent;
|
||||||
|
while (tr)
|
||||||
|
{
|
||||||
|
if (tr.TryGetComponent<T>(out var c) && valid(c)) return c;
|
||||||
|
if (tr == stopAfter) return null;
|
||||||
|
tr = tr.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a component of a specific type to the children of a GameObject.
|
||||||
|
/// </summary>
|
||||||
|
public static void AddComponentOnChildren<T>(this Component self, HideFlags hideFlags, bool includeSelf)
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
if (self == null) return;
|
||||||
|
|
||||||
|
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Self");
|
||||||
|
if (includeSelf && !self.TryGetComponent<T>(out _))
|
||||||
|
{
|
||||||
|
var c = self.gameObject.AddComponent<T>();
|
||||||
|
c.hideFlags = hideFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiler.EndSample();
|
||||||
|
|
||||||
|
Profiler.BeginSample("(COF)[ComponentExt] AddComponentOnChildren > Child");
|
||||||
|
var childCount = self.transform.childCount;
|
||||||
|
for (var i = 0; i < childCount; i++)
|
||||||
|
{
|
||||||
|
var child = self.transform.GetChild(i);
|
||||||
|
if (child.TryGetComponent<T>(out _)) continue;
|
||||||
|
|
||||||
|
var c = child.gameObject.AddComponent<T>();
|
||||||
|
c.hideFlags = hideFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !UNITY_2021_2_OR_NEWER && !UNITY_2020_3_45 && !UNITY_2020_3_46 && !UNITY_2020_3_47 && !UNITY_2020_3_48
|
||||||
|
public static T GetComponentInParent<T>(this Component self, bool includeInactive) where T : Component
|
||||||
|
{
|
||||||
|
if (!self) return null;
|
||||||
|
if (!includeInactive) return self.GetComponentInParent<T>();
|
||||||
|
|
||||||
|
var current = self.transform;
|
||||||
|
while (current)
|
||||||
|
{
|
||||||
|
if (current.TryGetComponent<T>(out var c)) return c;
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// Verify whether it can be converted to the specified component.
|
||||||
|
/// </summary>
|
||||||
|
internal static bool CanConvertTo<T>(this Object context) where T : MonoBehaviour
|
||||||
|
{
|
||||||
|
return context && context.GetType() != typeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert to the specified component.
|
||||||
|
/// </summary>
|
||||||
|
internal static void ConvertTo<T>(this Object context) where T : MonoBehaviour
|
||||||
|
{
|
||||||
|
var target = context as MonoBehaviour;
|
||||||
|
if (target == null) return;
|
||||||
|
|
||||||
|
var so = new SerializedObject(target);
|
||||||
|
so.Update();
|
||||||
|
|
||||||
|
var oldEnable = target.enabled;
|
||||||
|
target.enabled = false;
|
||||||
|
|
||||||
|
// Find MonoScript of the specified component.
|
||||||
|
foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
|
||||||
|
{
|
||||||
|
if (script.GetClass() != typeof(T))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set 'm_Script' to convert.
|
||||||
|
so.FindProperty("m_Script").objectReferenceValue = script;
|
||||||
|
so.ApplyModifiedProperties();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (so.targetObject is MonoBehaviour mb)
|
||||||
|
{
|
||||||
|
mb.enabled = oldEnable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8455ee485a5ee4cacbdf558f66af65fb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,125 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for Graphic class.
|
||||||
|
/// </summary>
|
||||||
|
internal static class GraphicExtensions
|
||||||
|
{
|
||||||
|
private static readonly Vector3[] s_WorldCorners = new Vector3[4];
|
||||||
|
private static readonly Bounds s_ScreenBounds = new Bounds(new Vector3(0.5f, 0.5f, 0.5f), new Vector3(1, 1, 1));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a Graphic component is currently in the screen view.
|
||||||
|
/// </summary>
|
||||||
|
public static void GetMaterialsForRendering(this Graphic self, List<Material> result)
|
||||||
|
{
|
||||||
|
result.Clear();
|
||||||
|
if (!self) return;
|
||||||
|
|
||||||
|
var cr = self.canvasRenderer;
|
||||||
|
var count = cr.materialCount;
|
||||||
|
var popCount = cr.popMaterialCount;
|
||||||
|
|
||||||
|
if (result.Capacity < count + popCount)
|
||||||
|
{
|
||||||
|
result.Capacity = count + popCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
result.Add(cr.GetMaterial(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < popCount; i++)
|
||||||
|
{
|
||||||
|
result.Add(cr.GetPopMaterial(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a Graphic component is currently in the screen view.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsInScreen(this Graphic self)
|
||||||
|
{
|
||||||
|
if (!self || !self.canvas) return false;
|
||||||
|
|
||||||
|
if (FrameCache.TryGet(self, nameof(IsInScreen), out bool result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiler.BeginSample("(COF)[GraphicExt] IsInScreen");
|
||||||
|
var cam = self.canvas.renderMode != RenderMode.ScreenSpaceOverlay
|
||||||
|
? self.canvas.worldCamera
|
||||||
|
: null;
|
||||||
|
var min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
|
||||||
|
var max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
|
||||||
|
self.rectTransform.GetWorldCorners(s_WorldCorners);
|
||||||
|
var screenSize = GetScreenSize();
|
||||||
|
for (var i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (cam)
|
||||||
|
{
|
||||||
|
s_WorldCorners[i] = cam.WorldToViewportPoint(s_WorldCorners[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_WorldCorners[i] = RectTransformUtility.WorldToScreenPoint(null, s_WorldCorners[i]);
|
||||||
|
s_WorldCorners[i].x /= screenSize.x;
|
||||||
|
s_WorldCorners[i].y /= screenSize.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_WorldCorners[i].z = 0;
|
||||||
|
min = Vector3.Min(s_WorldCorners[i], min);
|
||||||
|
max = Vector3.Max(s_WorldCorners[i], max);
|
||||||
|
}
|
||||||
|
|
||||||
|
var bounds = new Bounds(min, Vector3.zero);
|
||||||
|
bounds.Encapsulate(max);
|
||||||
|
result = bounds.Intersects(s_ScreenBounds);
|
||||||
|
FrameCache.Set(self, nameof(IsInScreen), result);
|
||||||
|
Profiler.EndSample();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the actual main texture of a Graphic component.
|
||||||
|
/// </summary>
|
||||||
|
public static Texture GetActualMainTexture(this Graphic self)
|
||||||
|
{
|
||||||
|
var image = self as Image;
|
||||||
|
if (image == null) return self.mainTexture;
|
||||||
|
|
||||||
|
var sprite = image.overrideSprite;
|
||||||
|
return sprite ? sprite.GetActualTexture() : self.mainTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector2Int GetScreenSize()
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (!Application.isPlaying && !Camera.current)
|
||||||
|
{
|
||||||
|
var res = UnityStats.screenRes.Split('x');
|
||||||
|
return new Vector2Int(int.Parse(res[0]), int.Parse(res[1]));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return new Vector2Int(Screen.width, Screen.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float GetParentGroupAlpha(this Graphic self)
|
||||||
|
{
|
||||||
|
var alpha = self.canvasRenderer.GetAlpha();
|
||||||
|
if (Mathf.Approximately(alpha, 0)) return 1;
|
||||||
|
|
||||||
|
var inheritedAlpha = self.canvasRenderer.GetInheritedAlpha();
|
||||||
|
return Mathf.Clamp01(inheritedAlpha / alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3803b037cd2ed45459dd660072f223dd
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,48 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
internal static class Misc
|
||||||
|
{
|
||||||
|
public static void Destroy(Object obj)
|
||||||
|
{
|
||||||
|
if (!obj) return;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (!Application.isPlaying)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(obj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Object.Destroy(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DestroyImmediate(Object obj)
|
||||||
|
{
|
||||||
|
if (!obj) return;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (Application.isEditor)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(obj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Object.Destroy(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("UNITY_EDITOR")]
|
||||||
|
public static void SetDirty(Object obj)
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (!obj) return;
|
||||||
|
EditorUtility.SetDirty(obj);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 39ed6a6b0a72e482488bd298b2ae762e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.U2D;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using System.Reflection;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for Sprite class.
|
||||||
|
/// </summary>
|
||||||
|
internal static class SpriteExtensions
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
private static readonly Type s_SpriteEditorExtensionType =
|
||||||
|
Type.GetType("UnityEditor.Experimental.U2D.SpriteEditorExtension, UnityEditor")
|
||||||
|
?? Type.GetType("UnityEditor.U2D.SpriteEditorExtension, UnityEditor");
|
||||||
|
|
||||||
|
private static readonly MethodInfo s_GetActiveAtlasTextureMethod = s_SpriteEditorExtensionType
|
||||||
|
.GetMethod("GetActiveAtlasTexture", BindingFlags.Static | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
private static readonly MethodInfo s_GetActiveAtlasMethod = s_SpriteEditorExtensionType
|
||||||
|
.GetMethod("GetActiveAtlas", BindingFlags.Static | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the actual texture of a sprite in play mode or edit mode.
|
||||||
|
/// </summary>
|
||||||
|
public static Texture2D GetActualTexture(this Sprite self)
|
||||||
|
{
|
||||||
|
if (!self) return null;
|
||||||
|
|
||||||
|
if (Application.isPlaying) return self.texture;
|
||||||
|
|
||||||
|
var ret = s_GetActiveAtlasTextureMethod.Invoke(null, new object[] { self }) as Texture2D;
|
||||||
|
return ret ? ret : self.texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the active sprite atlas of a sprite in play mode or edit mode.
|
||||||
|
/// </summary>
|
||||||
|
public static SpriteAtlas GetActiveAtlas(this Sprite self)
|
||||||
|
{
|
||||||
|
if (!self) return null;
|
||||||
|
|
||||||
|
return s_GetActiveAtlasMethod.Invoke(null, new object[] { self }) as SpriteAtlas;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/// <summary>
|
||||||
|
/// Get the actual texture of a sprite in play mode.
|
||||||
|
/// </summary>
|
||||||
|
internal static Texture2D GetActualTexture(this Sprite self)
|
||||||
|
{
|
||||||
|
return self ? self.texture : null;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a7a2e11131111447cb7fc0394a14da65
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,46 @@
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
internal static class Vector3Extensions
|
||||||
|
{
|
||||||
|
public static Vector3 Inverse(this Vector3 self)
|
||||||
|
{
|
||||||
|
self.x = Mathf.Approximately(self.x, 0) ? 1 : 1 / self.x;
|
||||||
|
self.y = Mathf.Approximately(self.y, 0) ? 1 : 1 / self.y;
|
||||||
|
self.z = Mathf.Approximately(self.z, 0) ? 1 : 1 / self.z;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector3 GetScaled(this Vector3 self, Vector3 other1)
|
||||||
|
{
|
||||||
|
self.Scale(other1);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2)
|
||||||
|
{
|
||||||
|
self.Scale(other1);
|
||||||
|
self.Scale(other2);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector3 GetScaled(this Vector3 self, Vector3 other1, Vector3 other2, Vector3 other3)
|
||||||
|
{
|
||||||
|
self.Scale(other1);
|
||||||
|
self.Scale(other2);
|
||||||
|
self.Scale(other3);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsVisible(this Vector3 self)
|
||||||
|
{
|
||||||
|
return 0 < Mathf.Abs(self.x * self.y * self.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsVisible2D(this Vector3 self)
|
||||||
|
{
|
||||||
|
return 0 < Mathf.Abs(self.x * self.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6a7b5fb989e4b48c8bc7ecce834060f5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 398e06c9985ad4291a95f0749c2927fb
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,219 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Build;
|
||||||
|
using UnityEditor.Build.Reporting;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
public abstract class PreloadedProjectSettings : ScriptableObject
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
{
|
||||||
|
private class PreprocessBuildWithReport : IPreprocessBuildWithReport
|
||||||
|
{
|
||||||
|
int IOrderedCallback.callbackOrder => 0;
|
||||||
|
|
||||||
|
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
[InitializeOnEnterPlayMode]
|
||||||
|
private static void Initialize()
|
||||||
|
{
|
||||||
|
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
|
||||||
|
foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>)))
|
||||||
|
{
|
||||||
|
var defaultSettings = GetDefaultSettings(t);
|
||||||
|
if (!defaultSettings)
|
||||||
|
{
|
||||||
|
// When create a new instance, automatically set it as default settings.
|
||||||
|
defaultSettings = t.GetProperty("instance", flags)
|
||||||
|
?.GetValue(null, null) as PreloadedProjectSettings;
|
||||||
|
}
|
||||||
|
else if (GetPreloadedSettings(t).Length != 1)
|
||||||
|
{
|
||||||
|
SetDefaultSettings(defaultSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorApplication.QueuePlayerLoopUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static string GetDefaultName(Type type, bool nicify)
|
||||||
|
{
|
||||||
|
var typeName = type.Name.Replace("ProjectSettings", "");
|
||||||
|
return nicify
|
||||||
|
? ObjectNames.NicifyVariableName(typeName)
|
||||||
|
: typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object[] GetPreloadedSettings(Type type)
|
||||||
|
{
|
||||||
|
return PlayerSettings.GetPreloadedAssets()
|
||||||
|
.Where(x => x && x.GetType() == type)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static PreloadedProjectSettings GetDefaultSettings(Type type)
|
||||||
|
{
|
||||||
|
return GetPreloadedSettings(type).FirstOrDefault() as PreloadedProjectSettings
|
||||||
|
?? AssetDatabase.FindAssets($"t:{nameof(PreloadedProjectSettings)}")
|
||||||
|
.Select(AssetDatabase.GUIDToAssetPath)
|
||||||
|
.Select(AssetDatabase.LoadAssetAtPath<PreloadedProjectSettings>)
|
||||||
|
.FirstOrDefault(x => x && x.GetType() == type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void SetDefaultSettings(PreloadedProjectSettings asset)
|
||||||
|
{
|
||||||
|
if (!asset) return;
|
||||||
|
var type = asset.GetType();
|
||||||
|
if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(asset)))
|
||||||
|
{
|
||||||
|
if (!AssetDatabase.IsValidFolder("Assets/ProjectSettings"))
|
||||||
|
{
|
||||||
|
AssetDatabase.CreateFolder("Assets", "ProjectSettings");
|
||||||
|
}
|
||||||
|
|
||||||
|
var assetPath = $"Assets/ProjectSettings/{GetDefaultName(type, false)}.asset";
|
||||||
|
assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath);
|
||||||
|
AssetDatabase.CreateAsset(asset, assetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var preloadedAssets = PlayerSettings.GetPreloadedAssets();
|
||||||
|
var projectSettings = GetPreloadedSettings(type);
|
||||||
|
PlayerSettings.SetPreloadedAssets(preloadedAssets
|
||||||
|
.Where(x => x)
|
||||||
|
.Except(projectSettings.Except(new[] { asset }))
|
||||||
|
.Append(asset)
|
||||||
|
.Distinct()
|
||||||
|
.ToArray());
|
||||||
|
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public abstract class PreloadedProjectSettings<T> : PreloadedProjectSettings
|
||||||
|
where T : PreloadedProjectSettings<T>
|
||||||
|
{
|
||||||
|
private static T s_Instance;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
private string _jsonText;
|
||||||
|
|
||||||
|
public static T instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (s_Instance) return s_Instance;
|
||||||
|
|
||||||
|
s_Instance = GetDefaultSettings(typeof(T)) as T;
|
||||||
|
if (s_Instance) return s_Instance;
|
||||||
|
|
||||||
|
s_Instance = CreateInstance<T>();
|
||||||
|
if (!s_Instance)
|
||||||
|
{
|
||||||
|
s_Instance = null;
|
||||||
|
return s_Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDefaultSettings(s_Instance);
|
||||||
|
return s_Instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case PlayModeStateChange.ExitingEditMode:
|
||||||
|
_jsonText = EditorJsonUtility.ToJson(this);
|
||||||
|
break;
|
||||||
|
case PlayModeStateChange.ExitingPlayMode:
|
||||||
|
if (_jsonText != null)
|
||||||
|
{
|
||||||
|
EditorJsonUtility.FromJsonOverwrite(_jsonText, this);
|
||||||
|
_jsonText = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
public static T instance => s_Instance ? s_Instance : s_Instance = CreateInstance<T>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This function is called when the object becomes enabled and active.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnEnable()
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
var isDefaultSettings = !s_Instance || s_Instance == this || GetDefaultSettings(typeof(T)) == this;
|
||||||
|
if (!isDefaultSettings)
|
||||||
|
{
|
||||||
|
DestroyImmediate(this, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (s_Instance) return;
|
||||||
|
s_Instance = this as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This function is called when the behaviour becomes disabled.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnDisable()
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||||
|
#endif
|
||||||
|
if (s_Instance != this) return;
|
||||||
|
|
||||||
|
s_Instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
protected sealed class PreloadedProjectSettingsProvider : SettingsProvider
|
||||||
|
{
|
||||||
|
private Editor _editor;
|
||||||
|
private PreloadedProjectSettings<T> _target;
|
||||||
|
|
||||||
|
public PreloadedProjectSettingsProvider(string path) : base(path, SettingsScope.Project)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGUI(string searchContext)
|
||||||
|
{
|
||||||
|
if (!_target)
|
||||||
|
{
|
||||||
|
if (_editor)
|
||||||
|
{
|
||||||
|
DestroyImmediate(_editor);
|
||||||
|
_editor = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_target = instance;
|
||||||
|
_editor = Editor.CreateEditor(_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
_editor.OnInspectorGUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 790ea008741dc411497c8794745319eb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1b2877595f27c4a70a426991d515434f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,88 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for a fast action.
|
||||||
|
/// </summary>
|
||||||
|
internal class FastActionBase<T>
|
||||||
|
{
|
||||||
|
private static readonly ObjectPool<LinkedListNode<T>> s_NodePool =
|
||||||
|
new ObjectPool<LinkedListNode<T>>(() => new LinkedListNode<T>(default), _ => true, x => x.Value = default);
|
||||||
|
|
||||||
|
private readonly LinkedList<T> _delegates = new LinkedList<T>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a delegate to the action.
|
||||||
|
/// </summary>
|
||||||
|
public void Add(T rhs)
|
||||||
|
{
|
||||||
|
if (rhs == null) return;
|
||||||
|
Profiler.BeginSample("(COF)[FastAction] Add Action");
|
||||||
|
var node = s_NodePool.Rent();
|
||||||
|
node.Value = rhs;
|
||||||
|
_delegates.AddLast(node);
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a delegate from the action.
|
||||||
|
/// </summary>
|
||||||
|
public void Remove(T rhs)
|
||||||
|
{
|
||||||
|
if (rhs == null) return;
|
||||||
|
Profiler.BeginSample("(COF)[FastAction] Remove Action");
|
||||||
|
var node = _delegates.Find(rhs);
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
_delegates.Remove(node);
|
||||||
|
s_NodePool.Return(ref node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the action with a callback function.
|
||||||
|
/// </summary>
|
||||||
|
protected void Invoke(Action<T> callback)
|
||||||
|
{
|
||||||
|
var node = _delegates.First;
|
||||||
|
while (node != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
callback(node.Value);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
node = node.Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_delegates.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A fast action without parameters.
|
||||||
|
/// </summary>
|
||||||
|
internal class FastAction : FastActionBase<Action>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Invoke all the registered delegates.
|
||||||
|
/// </summary>
|
||||||
|
public void Invoke()
|
||||||
|
{
|
||||||
|
Invoke(action => action.Invoke());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a7c8c268a827b4787a8e050f1fe95ad5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,101 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
internal static class FrameCache
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<Type, IFrameCache> s_Caches = new Dictionary<Type, IFrameCache>();
|
||||||
|
|
||||||
|
static FrameCache()
|
||||||
|
{
|
||||||
|
s_Caches.Clear();
|
||||||
|
UIExtraCallbacks.onLateAfterCanvasRebuild += ClearAllCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
|
private static void Clear()
|
||||||
|
{
|
||||||
|
s_Caches.Clear();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to retrieve a value from the frame cache with a specified key.
|
||||||
|
/// </summary>
|
||||||
|
public static bool TryGet<T>(object key1, string key2, out T result)
|
||||||
|
{
|
||||||
|
return GetFrameCache<T>().TryGet((key1.GetHashCode(), key2.GetHashCode()), out result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to retrieve a value from the frame cache with a specified key.
|
||||||
|
/// </summary>
|
||||||
|
public static bool TryGet<T>(object key1, string key2, int key3, out T result)
|
||||||
|
{
|
||||||
|
return GetFrameCache<T>().TryGet((key1.GetHashCode(), key2.GetHashCode() + key3), out result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a value in the frame cache with a specified key.
|
||||||
|
/// </summary>
|
||||||
|
public static void Set<T>(object key1, string key2, T result)
|
||||||
|
{
|
||||||
|
GetFrameCache<T>().Set((key1.GetHashCode(), key2.GetHashCode()), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a value in the frame cache with a specified key.
|
||||||
|
/// </summary>
|
||||||
|
public static void Set<T>(object key1, string key2, int key3, T result)
|
||||||
|
{
|
||||||
|
GetFrameCache<T>().Set((key1.GetHashCode(), key2.GetHashCode() + key3), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ClearAllCache()
|
||||||
|
{
|
||||||
|
foreach (var cache in s_Caches.Values)
|
||||||
|
{
|
||||||
|
cache.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FrameCacheContainer<T> GetFrameCache<T>()
|
||||||
|
{
|
||||||
|
var t = typeof(T);
|
||||||
|
if (s_Caches.TryGetValue(t, out var frameCache)) return frameCache as FrameCacheContainer<T>;
|
||||||
|
|
||||||
|
frameCache = new FrameCacheContainer<T>();
|
||||||
|
s_Caches.Add(t, frameCache);
|
||||||
|
|
||||||
|
return (FrameCacheContainer<T>)frameCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface IFrameCache
|
||||||
|
{
|
||||||
|
void Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FrameCacheContainer<T> : IFrameCache
|
||||||
|
{
|
||||||
|
private readonly Dictionary<(int, int), T> _caches = new Dictionary<(int, int), T>();
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_caches.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGet((int, int) key, out T result)
|
||||||
|
{
|
||||||
|
return _caches.TryGetValue(key, out result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set((int, int) key, T result)
|
||||||
|
{
|
||||||
|
_caches[key] = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5f129e3b07ffb4d3bbb4cc5f6bd94087
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,254 @@
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
#if ENABLE_COFFEE_LOGGER
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
#else
|
||||||
|
using Conditional = System.Diagnostics.ConditionalAttribute;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
internal static class Logging
|
||||||
|
{
|
||||||
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
|
private const string k_DisableSymbol = "DISABLE_COFFEE_LOGGER";
|
||||||
|
|
||||||
|
[Conditional(k_DisableSymbol)]
|
||||||
|
#endif
|
||||||
|
private static void Log_Internal(LogType type, object tag, object message, Object context)
|
||||||
|
{
|
||||||
|
#if ENABLE_COFFEE_LOGGER
|
||||||
|
AppendTag(s_Sb, tag);
|
||||||
|
s_Sb.Append(message);
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case LogType.Error:
|
||||||
|
case LogType.Assert:
|
||||||
|
case LogType.Exception:
|
||||||
|
Debug.LogError(s_Sb, context);
|
||||||
|
break;
|
||||||
|
case LogType.Warning:
|
||||||
|
Debug.LogWarning(s_Sb, context);
|
||||||
|
break;
|
||||||
|
case LogType.Log:
|
||||||
|
Debug.Log(s_Sb, context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_Sb.Length = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
|
[Conditional(k_DisableSymbol)]
|
||||||
|
#endif
|
||||||
|
public static void LogIf(bool enable, object tag, object message, Object context = null)
|
||||||
|
{
|
||||||
|
if (!enable) return;
|
||||||
|
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
|
[Conditional(k_DisableSymbol)]
|
||||||
|
#endif
|
||||||
|
public static void Log(object tag, object message, Object context = null)
|
||||||
|
{
|
||||||
|
Log_Internal(LogType.Log, tag, message, context ? context : tag as Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
|
[Conditional(k_DisableSymbol)]
|
||||||
|
#endif
|
||||||
|
public static void LogWarning(object tag, object message, Object context = null)
|
||||||
|
{
|
||||||
|
Log_Internal(LogType.Warning, tag, message, context ? context : tag as Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogError(object tag, object message, Object context = null)
|
||||||
|
{
|
||||||
|
#if ENABLE_COFFEE_LOGGER
|
||||||
|
Log_Internal(LogType.Error, tag, message, context ? context : tag as Object);
|
||||||
|
#else
|
||||||
|
Debug.LogError($"{tag}: {message}", context);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
|
[Conditional(k_DisableSymbol)]
|
||||||
|
#endif
|
||||||
|
public static void LogMulticast(Type type, string fieldName, object instance = null, string message = null)
|
||||||
|
{
|
||||||
|
#if ENABLE_COFFEE_LOGGER
|
||||||
|
AppendTag(s_Sb, instance ?? type);
|
||||||
|
|
||||||
|
var handler = type
|
||||||
|
.GetField(fieldName,
|
||||||
|
BindingFlags.Static | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic)
|
||||||
|
?.GetValue(instance);
|
||||||
|
|
||||||
|
var list = ((MulticastDelegate)handler)?.GetInvocationList() ?? Array.Empty<Delegate>();
|
||||||
|
s_Sb.Append("<color=orange>");
|
||||||
|
s_Sb.Append(type.Name);
|
||||||
|
s_Sb.Append(".");
|
||||||
|
s_Sb.Append(fieldName);
|
||||||
|
s_Sb.Append(" has ");
|
||||||
|
s_Sb.Append(list.Length);
|
||||||
|
s_Sb.Append(" callbacks");
|
||||||
|
if (message != null)
|
||||||
|
{
|
||||||
|
s_Sb.Append(" (");
|
||||||
|
s_Sb.Append(message);
|
||||||
|
s_Sb.Append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
s_Sb.Append(":</color>");
|
||||||
|
|
||||||
|
for (var i = 0; i < list.Length; i++)
|
||||||
|
{
|
||||||
|
s_Sb.Append("\n - ");
|
||||||
|
s_Sb.Append(list[i].Method.DeclaringType?.Name);
|
||||||
|
s_Sb.Append(".");
|
||||||
|
s_Sb.Append(list[i].Method.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log(s_Sb);
|
||||||
|
s_Sb.Length = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
|
[Conditional(k_DisableSymbol)]
|
||||||
|
#endif
|
||||||
|
private static void AppendTag(StringBuilder sb, object tag)
|
||||||
|
{
|
||||||
|
#if ENABLE_COFFEE_LOGGER
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sb.Append("f");
|
||||||
|
sb.Append(Time.frameCount);
|
||||||
|
sb.Append(":<color=#");
|
||||||
|
AppendReadableCode(sb, tag);
|
||||||
|
sb.Append("><b>[");
|
||||||
|
|
||||||
|
switch (tag)
|
||||||
|
{
|
||||||
|
case string name:
|
||||||
|
sb.Append(name);
|
||||||
|
break;
|
||||||
|
case Type type:
|
||||||
|
AppendType(sb, type);
|
||||||
|
break;
|
||||||
|
case Object uObject:
|
||||||
|
AppendType(sb, tag.GetType());
|
||||||
|
sb.Append(" #");
|
||||||
|
sb.Append(uObject.name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
AppendType(sb, tag.GetType());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append("]</b></color> ");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
sb.Append("f");
|
||||||
|
sb.Append(Time.frameCount);
|
||||||
|
sb.Append(":<b>[");
|
||||||
|
sb.Append(tag);
|
||||||
|
sb.Append("]</b> ");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
|
[Conditional(k_DisableSymbol)]
|
||||||
|
#endif
|
||||||
|
private static void AppendType(StringBuilder sb, Type type)
|
||||||
|
{
|
||||||
|
#if ENABLE_COFFEE_LOGGER
|
||||||
|
if (s_TypeNameCache.TryGetValue(type, out var name))
|
||||||
|
{
|
||||||
|
sb.Append(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// New type found
|
||||||
|
var start = sb.Length;
|
||||||
|
if (0 < start && sb[start - 1] == '<' && (type.Name == "Material" || type.Name == "Color"))
|
||||||
|
{
|
||||||
|
sb.Append('@');
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append(type.Name);
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
sb.Length -= 2;
|
||||||
|
sb.Append("<");
|
||||||
|
foreach (var gType in type.GetGenericArguments())
|
||||||
|
{
|
||||||
|
AppendType(sb, gType);
|
||||||
|
sb.Append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Length -= 2;
|
||||||
|
sb.Append(">");
|
||||||
|
}
|
||||||
|
|
||||||
|
s_TypeNameCache.Add(type, sb.ToString(start, sb.Length - start));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_COFFEE_LOGGER
|
||||||
|
[Conditional(k_DisableSymbol)]
|
||||||
|
#endif
|
||||||
|
private static void AppendReadableCode(StringBuilder sb, object tag)
|
||||||
|
{
|
||||||
|
#if ENABLE_COFFEE_LOGGER
|
||||||
|
int hash;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (tag)
|
||||||
|
{
|
||||||
|
case string text:
|
||||||
|
hash = text.GetHashCode();
|
||||||
|
break;
|
||||||
|
case Type type:
|
||||||
|
type = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
|
||||||
|
hash = type.FullName?.GetHashCode() ?? 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hash = tag.GetType().FullName?.GetHashCode() ?? 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
sb.Append("FFFFFF");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = hash & (s_Codes.Length - 1);
|
||||||
|
if (s_Codes[hash] == null)
|
||||||
|
{
|
||||||
|
var hue = hash / (float)s_Codes.Length;
|
||||||
|
var modifier = 1f - Mathf.Clamp01(Mathf.Abs(hue - 0.65f) / 0.2f);
|
||||||
|
var saturation = 0.7f + modifier * -0.2f;
|
||||||
|
var value = 0.8f + modifier * 0.3f;
|
||||||
|
s_Codes[hash] = ColorUtility.ToHtmlStringRGB(Color.HSVToRGB(hue, saturation, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append(s_Codes[hash]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ENABLE_COFFEE_LOGGER
|
||||||
|
private static readonly StringBuilder s_Sb = new StringBuilder();
|
||||||
|
private static readonly string[] s_Codes = new string[64];
|
||||||
|
private static readonly Dictionary<Type, string> s_TypeNameCache = new Dictionary<Type, string>();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8255313895da84e7cbdc876be3795334
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,92 @@
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality to manage materials.
|
||||||
|
/// </summary>
|
||||||
|
internal static class MaterialRepository
|
||||||
|
{
|
||||||
|
private static readonly ObjectRepository<Material> s_Repository = new ObjectRepository<Material>();
|
||||||
|
|
||||||
|
public static int count => s_Repository.count;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
|
private static void Clear()
|
||||||
|
{
|
||||||
|
s_Repository.Clear();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a cached material based on the hash.
|
||||||
|
/// </summary>
|
||||||
|
public static bool Valid(Hash128 hash, Material material)
|
||||||
|
{
|
||||||
|
Profiler.BeginSample("(COF)[MaterialRegistry] Valid");
|
||||||
|
var ret = s_Repository.Valid(hash, material);
|
||||||
|
Profiler.EndSample();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or retrieves a cached material based on the hash.
|
||||||
|
/// </summary>
|
||||||
|
public static void Get(Hash128 hash, ref Material material, Func<Material> onCreate)
|
||||||
|
{
|
||||||
|
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
||||||
|
s_Repository.Get(hash, ref material, onCreate);
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or retrieves a cached material based on the hash.
|
||||||
|
/// </summary>
|
||||||
|
public static void Get(Hash128 hash, ref Material material, string shaderName)
|
||||||
|
{
|
||||||
|
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
||||||
|
s_Repository.Get(hash, ref material, x => new Material(Shader.Find(x))
|
||||||
|
{
|
||||||
|
hideFlags = HideFlags.DontSave | HideFlags.NotEditable
|
||||||
|
}, shaderName);
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or retrieves a cached material based on the hash.
|
||||||
|
/// </summary>
|
||||||
|
public static void Get(Hash128 hash, ref Material material, string shaderName, string[] keywords)
|
||||||
|
{
|
||||||
|
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
||||||
|
s_Repository.Get(hash, ref material, x => new Material(Shader.Find(x.shaderName))
|
||||||
|
{
|
||||||
|
hideFlags = HideFlags.DontSave | HideFlags.NotEditable,
|
||||||
|
shaderKeywords = x.keywords
|
||||||
|
}, (shaderName, keywords));
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or retrieves a cached material based on the hash.
|
||||||
|
/// </summary>
|
||||||
|
public static void Get<T>(Hash128 hash, ref Material material, Func<T, Material> onCreate, T source)
|
||||||
|
{
|
||||||
|
Profiler.BeginSample("(COF)[MaterialRepository] Get");
|
||||||
|
s_Repository.Get(hash, ref material, onCreate, source);
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a soft mask material from the cache.
|
||||||
|
/// </summary>
|
||||||
|
public static void Release(ref Material material)
|
||||||
|
{
|
||||||
|
Profiler.BeginSample("(COF)[MaterialRepository] Release");
|
||||||
|
s_Repository.Release(ref material);
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 702912f2ee2ec49bb8003a64151ae4f7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,85 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Object pool.
|
||||||
|
/// </summary>
|
||||||
|
internal class ObjectPool<T>
|
||||||
|
{
|
||||||
|
private readonly Func<T> _onCreate; // Delegate for creating instances
|
||||||
|
private readonly Action<T> _onReturn; // Delegate for returning instances to the pool
|
||||||
|
private readonly Predicate<T> _onValid; // Delegate for checking if instances are valid
|
||||||
|
private readonly Stack<T> _pool = new Stack<T>(32); // Object pool
|
||||||
|
private int _count; // Total count of created instances
|
||||||
|
|
||||||
|
public ObjectPool(Func<T> onCreate, Predicate<T> onValid, Action<T> onReturn)
|
||||||
|
{
|
||||||
|
_onCreate = onCreate;
|
||||||
|
_onValid = onValid;
|
||||||
|
_onReturn = onReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rent an instance from the pool.
|
||||||
|
/// When you no longer need it, return it with <see cref="Return" />.
|
||||||
|
/// </summary>
|
||||||
|
public T Rent()
|
||||||
|
{
|
||||||
|
while (0 < _pool.Count)
|
||||||
|
{
|
||||||
|
var instance = _pool.Pop();
|
||||||
|
if (_onValid(instance))
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no instances in the pool, create a new one.
|
||||||
|
Logging.Log(this, $"A new instance is created (pooled: {_pool.Count}, created: {++_count}).");
|
||||||
|
return _onCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return an instance to the pool and assign null.
|
||||||
|
/// Be sure to return the instance obtained with <see cref="Rent" /> with this method.
|
||||||
|
/// </summary>
|
||||||
|
public void Return(ref T instance)
|
||||||
|
{
|
||||||
|
if (instance == null || _pool.Contains(instance)) return; // Ignore if already pooled or null.
|
||||||
|
|
||||||
|
_onReturn(instance); // Return the instance to the pool.
|
||||||
|
_pool.Push(instance);
|
||||||
|
Logging.Log(this, $"An instance is released (pooled: {_pool.Count}, created: {_count}).");
|
||||||
|
instance = default; // Set the reference to null.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Object pool for <see cref="List{T}" />.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ListPool<T>
|
||||||
|
{
|
||||||
|
private static readonly ObjectPool<List<T>> s_ListPool =
|
||||||
|
new ObjectPool<List<T>>(() => new List<T>(), _ => true, x => x.Clear());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rent an instance from the pool.
|
||||||
|
/// When you no longer need it, return it with <see cref="Return" />.
|
||||||
|
/// </summary>
|
||||||
|
public static List<T> Rent()
|
||||||
|
{
|
||||||
|
return s_ListPool.Rent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return an instance to the pool and assign null.
|
||||||
|
/// Be sure to return the instance obtained with <see cref="Rent" /> with this method.
|
||||||
|
/// </summary>
|
||||||
|
public static void Return(ref List<T> toRelease)
|
||||||
|
{
|
||||||
|
s_ListPool.Return(ref toRelease);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 632cb1ba34e6a4e80b55a32bb63ca369
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,209 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Profiling;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
internal class ObjectRepository<T> where T : Object
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Hash128, Entry> _cache = new Dictionary<Hash128, Entry>(8);
|
||||||
|
private readonly Dictionary<int, Hash128> _objectKey = new Dictionary<int, Hash128>(8);
|
||||||
|
private readonly string _name;
|
||||||
|
private readonly Action<T> _onRelease;
|
||||||
|
private readonly Stack<Entry> _pool = new Stack<Entry>(8);
|
||||||
|
|
||||||
|
public ObjectRepository(Action<T> onRelease = null)
|
||||||
|
{
|
||||||
|
_name = $"{typeof(T).Name}Repository";
|
||||||
|
if (onRelease == null)
|
||||||
|
{
|
||||||
|
_onRelease = x =>
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (!Application.isPlaying)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(x, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Object.Destroy(x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_onRelease = onRelease;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
_pool.Push(new Entry());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int count => _cache.Count;
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
foreach (var kv in _cache)
|
||||||
|
{
|
||||||
|
var entry = kv.Value;
|
||||||
|
if (entry == null) continue;
|
||||||
|
|
||||||
|
entry.Release(_onRelease);
|
||||||
|
_pool.Push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache.Clear();
|
||||||
|
_objectKey.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Valid(Hash128 hash, T obj)
|
||||||
|
{
|
||||||
|
return _cache.TryGetValue(hash, out var entry) && entry.storedObject == obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or retrieves a cached object based on the hash.
|
||||||
|
/// </summary>
|
||||||
|
public void Get(Hash128 hash, ref T obj, Func<T> onCreate)
|
||||||
|
{
|
||||||
|
if (GetFromCache(hash, ref obj)) return;
|
||||||
|
Add(hash, ref obj, onCreate());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or retrieves a cached object based on the hash.
|
||||||
|
/// </summary>
|
||||||
|
public void Get<TS>(Hash128 hash, ref T obj, Func<TS, T> onCreate, TS source)
|
||||||
|
{
|
||||||
|
if (GetFromCache(hash, ref obj)) return;
|
||||||
|
Add(hash, ref obj, onCreate(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool GetFromCache(Hash128 hash, ref T obj)
|
||||||
|
{
|
||||||
|
// Find existing entry.
|
||||||
|
Profiler.BeginSample("(COF)[ObjectRepository] GetFromCache");
|
||||||
|
if (_cache.TryGetValue(hash, out var entry))
|
||||||
|
{
|
||||||
|
if (!entry.storedObject)
|
||||||
|
{
|
||||||
|
Release(ref entry.storedObject);
|
||||||
|
Profiler.EndSample();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.storedObject != obj)
|
||||||
|
{
|
||||||
|
// if the object is different, release the old one.
|
||||||
|
Release(ref obj);
|
||||||
|
++entry.reference;
|
||||||
|
obj = entry.storedObject;
|
||||||
|
Logging.Log(_name, $"Get(total#{count}): {entry}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiler.EndSample();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiler.EndSample();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Add(Hash128 hash, ref T obj, T newObject)
|
||||||
|
{
|
||||||
|
if (!newObject)
|
||||||
|
{
|
||||||
|
Release(ref obj);
|
||||||
|
obj = newObject;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and add a new entry.
|
||||||
|
Profiler.BeginSample("(COF)[ObjectRepository] Add");
|
||||||
|
var newEntry = 0 < _pool.Count ? _pool.Pop() : new Entry();
|
||||||
|
newEntry.storedObject = newObject;
|
||||||
|
newEntry.hash = hash;
|
||||||
|
newEntry.reference = 1;
|
||||||
|
_cache[hash] = newEntry;
|
||||||
|
_objectKey[newObject.GetInstanceID()] = hash;
|
||||||
|
Logging.Log(_name, $"<color=#03c700>Add</color>(total#{count}): {newEntry}");
|
||||||
|
Release(ref obj);
|
||||||
|
obj = newObject;
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Release a object.
|
||||||
|
/// </summary>
|
||||||
|
public void Release(ref T obj)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(obj, null)) return;
|
||||||
|
|
||||||
|
// Find and release the entry.
|
||||||
|
Profiler.BeginSample("(COF)[ObjectRepository] Release");
|
||||||
|
var id = obj.GetInstanceID();
|
||||||
|
if (_objectKey.TryGetValue(id, out var hash)
|
||||||
|
&& _cache.TryGetValue(hash, out var entry))
|
||||||
|
{
|
||||||
|
entry.reference--;
|
||||||
|
if (entry.reference <= 0 || !entry.storedObject)
|
||||||
|
{
|
||||||
|
Remove(entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Log(_name, $"Release(total#{_cache.Count}): {entry}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Log(_name, $"Release(total#{_cache.Count}): <color=red>Already released: {obj}</color>");
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = null;
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Remove(Entry entry)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(entry, null)) return;
|
||||||
|
|
||||||
|
Profiler.BeginSample("(COF)[ObjectRepository] Remove");
|
||||||
|
_cache.Remove(entry.hash);
|
||||||
|
_objectKey.Remove(entry.storedObject.GetInstanceID());
|
||||||
|
_pool.Push(entry);
|
||||||
|
entry.reference = 0;
|
||||||
|
Logging.Log(_name, $"<color=#f29e03>Remove</color>(total#{_cache.Count}): {entry}");
|
||||||
|
entry.Release(_onRelease);
|
||||||
|
Profiler.EndSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Entry
|
||||||
|
{
|
||||||
|
public Hash128 hash;
|
||||||
|
public int reference;
|
||||||
|
public T storedObject;
|
||||||
|
|
||||||
|
public void Release(Action<T> onRelease)
|
||||||
|
{
|
||||||
|
reference = 0;
|
||||||
|
if (storedObject)
|
||||||
|
{
|
||||||
|
onRelease?.Invoke(storedObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
storedObject = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"h{(uint)hash.GetHashCode()} (refs#{reference}), {storedObject}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a713d67bdb31e45e296e5f18460717e2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,93 @@
|
||||||
|
using System;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides additional callbacks related to canvas and UI system.
|
||||||
|
/// </summary>
|
||||||
|
internal static class UIExtraCallbacks
|
||||||
|
{
|
||||||
|
private static bool s_IsInitializedAfterCanvasRebuild;
|
||||||
|
private static readonly FastAction s_AfterCanvasRebuildAction = new FastAction();
|
||||||
|
private static readonly FastAction s_LateAfterCanvasRebuildAction = new FastAction();
|
||||||
|
private static readonly FastAction s_BeforeCanvasRebuildAction = new FastAction();
|
||||||
|
|
||||||
|
static UIExtraCallbacks()
|
||||||
|
{
|
||||||
|
Canvas.willRenderCanvases += OnBeforeCanvasRebuild;
|
||||||
|
Logging.LogMulticast(typeof(Canvas), "willRenderCanvases", message: "ctor");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that occurs after canvas rebuilds.
|
||||||
|
/// </summary>
|
||||||
|
public static event Action onLateAfterCanvasRebuild
|
||||||
|
{
|
||||||
|
add => s_LateAfterCanvasRebuildAction.Add(value);
|
||||||
|
remove => s_LateAfterCanvasRebuildAction.Remove(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that occurs before canvas rebuilds.
|
||||||
|
/// </summary>
|
||||||
|
public static event Action onBeforeCanvasRebuild
|
||||||
|
{
|
||||||
|
add => s_BeforeCanvasRebuildAction.Add(value);
|
||||||
|
remove => s_BeforeCanvasRebuildAction.Remove(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that occurs after canvas rebuilds.
|
||||||
|
/// </summary>
|
||||||
|
public static event Action onAfterCanvasRebuild
|
||||||
|
{
|
||||||
|
add => s_AfterCanvasRebuildAction.Add(value);
|
||||||
|
remove => s_AfterCanvasRebuildAction.Remove(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the UIExtraCallbacks to ensure proper event handling.
|
||||||
|
/// </summary>
|
||||||
|
private static void InitializeAfterCanvasRebuild()
|
||||||
|
{
|
||||||
|
if (s_IsInitializedAfterCanvasRebuild) return;
|
||||||
|
s_IsInitializedAfterCanvasRebuild = true;
|
||||||
|
|
||||||
|
CanvasUpdateRegistry.IsRebuildingLayout();
|
||||||
|
Canvas.willRenderCanvases += OnAfterCanvasRebuild;
|
||||||
|
Logging.LogMulticast(typeof(Canvas), "willRenderCanvases",
|
||||||
|
message: "InitializeAfterCanvasRebuild");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
#endif
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||||
|
private static void InitializeOnLoad()
|
||||||
|
{
|
||||||
|
Canvas.willRenderCanvases -= OnAfterCanvasRebuild;
|
||||||
|
s_IsInitializedAfterCanvasRebuild = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Callback method called before canvas rebuilds.
|
||||||
|
/// </summary>
|
||||||
|
private static void OnBeforeCanvasRebuild()
|
||||||
|
{
|
||||||
|
s_BeforeCanvasRebuildAction.Invoke();
|
||||||
|
InitializeAfterCanvasRebuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Callback method called after canvas rebuilds.
|
||||||
|
/// </summary>
|
||||||
|
private static void OnAfterCanvasRebuild()
|
||||||
|
{
|
||||||
|
s_AfterCanvasRebuildAction.Invoke();
|
||||||
|
s_LateAfterCanvasRebuildAction.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9ea318e6e3e6c46aa97c72e28230bdc9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 66c42f0f30de84ca4bd8305a1188af85
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,175 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace Coffee.UIParticleInternal
|
||||||
|
{
|
||||||
|
internal static class ParticleSystemExtensions
|
||||||
|
{
|
||||||
|
private static ParticleSystem.Particle[] s_TmpParticles = new ParticleSystem.Particle[2048];
|
||||||
|
|
||||||
|
public static ParticleSystem.Particle[] GetParticleArray(int size)
|
||||||
|
{
|
||||||
|
if (s_TmpParticles.Length < size)
|
||||||
|
{
|
||||||
|
while (s_TmpParticles.Length < size)
|
||||||
|
{
|
||||||
|
size = Mathf.NextPowerOfTwo(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
s_TmpParticles = new ParticleSystem.Particle[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_TmpParticles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ValidateShape(this ParticleSystem self)
|
||||||
|
{
|
||||||
|
var shape = self.shape;
|
||||||
|
if (shape.enabled && shape.alignToDirection)
|
||||||
|
{
|
||||||
|
if (Mathf.Approximately(shape.scale.x * shape.scale.y * shape.scale.z, 0))
|
||||||
|
{
|
||||||
|
if (Mathf.Approximately(shape.scale.x, 0))
|
||||||
|
{
|
||||||
|
shape.scale.Set(0.0001f, shape.scale.y, shape.scale.z);
|
||||||
|
}
|
||||||
|
else if (Mathf.Approximately(shape.scale.y, 0))
|
||||||
|
{
|
||||||
|
shape.scale.Set(shape.scale.x, 0.0001f, shape.scale.z);
|
||||||
|
}
|
||||||
|
else if (Mathf.Approximately(shape.scale.z, 0))
|
||||||
|
{
|
||||||
|
shape.scale.Set(shape.scale.x, shape.scale.y, 0.0001f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanBakeMesh(this ParticleSystemRenderer self)
|
||||||
|
{
|
||||||
|
// #69: Editor crashes when mesh is set to null when `ParticleSystem.RenderMode = Mesh`
|
||||||
|
if (self.renderMode == ParticleSystemRenderMode.Mesh && self.mesh == null) return false;
|
||||||
|
|
||||||
|
// #61: When `ParticleSystem.RenderMode = None`, an error occurs
|
||||||
|
if (self.renderMode == ParticleSystemRenderMode.None) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ParticleSystemSimulationSpace GetActualSimulationSpace(this ParticleSystem self)
|
||||||
|
{
|
||||||
|
var main = self.main;
|
||||||
|
var space = main.simulationSpace;
|
||||||
|
if (space == ParticleSystemSimulationSpace.Custom && !main.customSimulationSpace)
|
||||||
|
{
|
||||||
|
space = ParticleSystemSimulationSpace.Local;
|
||||||
|
}
|
||||||
|
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsLocalSpace(this ParticleSystem self)
|
||||||
|
{
|
||||||
|
return GetActualSimulationSpace(self) == ParticleSystemSimulationSpace.Local;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsWorldSpace(this ParticleSystem self)
|
||||||
|
{
|
||||||
|
return GetActualSimulationSpace(self) == ParticleSystemSimulationSpace.World;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SortForRendering(this List<ParticleSystem> self, Transform transform, bool sortByMaterial)
|
||||||
|
{
|
||||||
|
self.Sort((a, b) =>
|
||||||
|
{
|
||||||
|
var aRenderer = a.GetComponent<ParticleSystemRenderer>();
|
||||||
|
var bRenderer = b.GetComponent<ParticleSystemRenderer>();
|
||||||
|
|
||||||
|
// Render queue: ascending
|
||||||
|
var aMat = aRenderer.sharedMaterial ? aRenderer.sharedMaterial : aRenderer.trailMaterial;
|
||||||
|
var bMat = bRenderer.sharedMaterial ? bRenderer.sharedMaterial : bRenderer.trailMaterial;
|
||||||
|
if (!aMat && !bMat) return 0;
|
||||||
|
if (!aMat) return -1;
|
||||||
|
if (!bMat) return 1;
|
||||||
|
|
||||||
|
if (sortByMaterial)
|
||||||
|
{
|
||||||
|
return aMat.GetInstanceID() - bMat.GetInstanceID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aMat.renderQueue != bMat.renderQueue)
|
||||||
|
{
|
||||||
|
return aMat.renderQueue - bMat.renderQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorting layer: ascending
|
||||||
|
if (aRenderer.sortingLayerID != bRenderer.sortingLayerID)
|
||||||
|
{
|
||||||
|
return SortingLayer.GetLayerValueFromID(aRenderer.sortingLayerID) -
|
||||||
|
SortingLayer.GetLayerValueFromID(bRenderer.sortingLayerID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorting order: ascending
|
||||||
|
if (aRenderer.sortingOrder != bRenderer.sortingOrder)
|
||||||
|
{
|
||||||
|
return aRenderer.sortingOrder - bRenderer.sortingOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Z position & sortingFudge: descending
|
||||||
|
var aTransform = a.transform;
|
||||||
|
var bTransform = b.transform;
|
||||||
|
var aPos = transform.InverseTransformPoint(aTransform.position).z + aRenderer.sortingFudge;
|
||||||
|
var bPos = transform.InverseTransformPoint(bTransform.position).z + bRenderer.sortingFudge;
|
||||||
|
if (!Mathf.Approximately(aPos, bPos))
|
||||||
|
{
|
||||||
|
return (int)Mathf.Sign(bPos - aPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)Mathf.Sign(GetIndex(self, a) - GetIndex(self, b));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetIndex(IList<ParticleSystem> list, Object ps)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
if (list[i].GetInstanceID() == ps.GetInstanceID())
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Texture2D GetTextureForSprite(this ParticleSystem self)
|
||||||
|
{
|
||||||
|
if (!self) return null;
|
||||||
|
|
||||||
|
// Get sprite's texture.
|
||||||
|
var tsaModule = self.textureSheetAnimation;
|
||||||
|
if (!tsaModule.enabled || tsaModule.mode != ParticleSystemAnimationMode.Sprites) return null;
|
||||||
|
|
||||||
|
for (var i = 0; i < tsaModule.spriteCount; i++)
|
||||||
|
{
|
||||||
|
var sprite = tsaModule.GetSprite(i);
|
||||||
|
if (!sprite) continue;
|
||||||
|
|
||||||
|
return sprite.GetActualTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Exec(this List<ParticleSystem> self, Action<ParticleSystem> action)
|
||||||
|
{
|
||||||
|
foreach (var p in self)
|
||||||
|
{
|
||||||
|
if (!p) continue;
|
||||||
|
action.Invoke(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e51604bfb810e44519e2710fd1b8af90
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Loading…
Reference in New Issue