feat: mesh sharing group
Particle simulation results are shared within the same group. A large number of the same effects can be displayed with a small load.pull/225/head
parent
d8e96e69a6
commit
9afeebf672
|
@ -58,6 +58,8 @@ namespace Coffee.UIExtensions
|
||||||
#endif
|
#endif
|
||||||
private SerializedProperty m_Scale3D;
|
private SerializedProperty m_Scale3D;
|
||||||
private SerializedProperty m_AnimatableProperties;
|
private SerializedProperty m_AnimatableProperties;
|
||||||
|
private SerializedProperty m_MeshSharing;
|
||||||
|
private SerializedProperty m_GroupId;
|
||||||
|
|
||||||
private ReorderableList _ro;
|
private ReorderableList _ro;
|
||||||
static private bool _xyzMode;
|
static private bool _xyzMode;
|
||||||
|
@ -133,6 +135,8 @@ namespace Coffee.UIExtensions
|
||||||
m_Maskable = serializedObject.FindProperty("m_Maskable");
|
m_Maskable = serializedObject.FindProperty("m_Maskable");
|
||||||
m_Scale3D = serializedObject.FindProperty("m_Scale3D");
|
m_Scale3D = serializedObject.FindProperty("m_Scale3D");
|
||||||
m_AnimatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
|
m_AnimatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
|
||||||
|
m_MeshSharing = serializedObject.FindProperty("m_MeshSharing");
|
||||||
|
m_GroupId = serializedObject.FindProperty("m_GroupId");
|
||||||
|
|
||||||
var sp = serializedObject.FindProperty("m_Particles");
|
var sp = serializedObject.FindProperty("m_Particles");
|
||||||
_ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true);
|
_ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true);
|
||||||
|
@ -213,7 +217,9 @@ namespace Coffee.UIExtensions
|
||||||
EditorGUILayout.PropertyField(m_Maskable);
|
EditorGUILayout.PropertyField(m_Maskable);
|
||||||
|
|
||||||
// Scale
|
// Scale
|
||||||
|
EditorGUI.BeginDisabledGroup(!m_MeshSharing.hasMultipleDifferentValues && m_MeshSharing.intValue == 4);
|
||||||
_xyzMode = DrawFloatOrVector3Field(m_Scale3D, _xyzMode);
|
_xyzMode = DrawFloatOrVector3Field(m_Scale3D, _xyzMode);
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
// AnimatableProperties
|
// AnimatableProperties
|
||||||
var mats = current.particles
|
var mats = current.particles
|
||||||
|
@ -225,6 +231,9 @@ namespace Coffee.UIExtensions
|
||||||
// Animated properties
|
// Animated properties
|
||||||
AnimatedPropertiesEditor.DrawAnimatableProperties(m_AnimatableProperties, mats);
|
AnimatedPropertiesEditor.DrawAnimatableProperties(m_AnimatableProperties, mats);
|
||||||
|
|
||||||
|
// Mesh sharing
|
||||||
|
DrawMeshSharing();
|
||||||
|
|
||||||
// Target ParticleSystems.
|
// Target ParticleSystems.
|
||||||
_ro.DoLayoutList();
|
_ro.DoLayoutList();
|
||||||
|
|
||||||
|
@ -272,6 +281,22 @@ namespace Coffee.UIExtensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawMeshSharing()
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(m_MeshSharing);
|
||||||
|
EditorGUI.BeginDisabledGroup(m_MeshSharing.intValue == 0);
|
||||||
|
EditorGUI.indentLevel++;
|
||||||
|
EditorGUILayout.PropertyField(m_GroupId);
|
||||||
|
if (m_MeshSharing.intValue == 1 || m_MeshSharing.intValue == 4)
|
||||||
|
{
|
||||||
|
EditorGUI.BeginDisabledGroup(true);
|
||||||
|
EditorGUILayout.ObjectField("Primary", UIParticleUpdater.GetPrimary(m_GroupId.intValue), typeof(UIParticle), false);
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
}
|
||||||
|
EditorGUI.indentLevel--;
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
}
|
||||||
|
|
||||||
private static void WindowFunction(UnityEngine.Object target, SceneView sceneView)
|
private static void WindowFunction(UnityEngine.Object target, SceneView sceneView)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -20,6 +20,15 @@ namespace Coffee.UIExtensions
|
||||||
[RequireComponent(typeof(CanvasRenderer))]
|
[RequireComponent(typeof(CanvasRenderer))]
|
||||||
public class UIParticle : MaskableGraphic
|
public class UIParticle : MaskableGraphic
|
||||||
{
|
{
|
||||||
|
public enum MeshSharing
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Auto,
|
||||||
|
Primary,
|
||||||
|
PrimarySimulator,
|
||||||
|
Reprica,
|
||||||
|
}
|
||||||
|
|
||||||
[HideInInspector][SerializeField] internal bool m_IsTrail = false;
|
[HideInInspector][SerializeField] internal bool m_IsTrail = false;
|
||||||
|
|
||||||
[Tooltip("Particle effect scale")]
|
[Tooltip("Particle effect scale")]
|
||||||
|
@ -34,6 +43,14 @@ namespace Coffee.UIExtensions
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
private List<ParticleSystem> m_Particles = new List<ParticleSystem>();
|
private List<ParticleSystem> m_Particles = new List<ParticleSystem>();
|
||||||
|
|
||||||
|
[Tooltip("Mesh sharing.None: disable mesh sharing.\nAuto: automatically select Primary/Reprica.\nPrimary: provides particle simulation results to the same group.\nPrimary Simulator: Primary, but do not render the particle (simulation only).\nReprica: render simulation results provided by the primary.")]
|
||||||
|
[SerializeField]
|
||||||
|
private MeshSharing m_MeshSharing = MeshSharing.None;
|
||||||
|
|
||||||
|
[Tooltip("Mesh sharing group ID. If non-zero is specified, particle simulation results are shared within the group.")]
|
||||||
|
[SerializeField]
|
||||||
|
private int m_GroupId = 0;
|
||||||
|
|
||||||
private List<UIParticleRenderer> m_Renderers = new List<UIParticleRenderer>();
|
private List<UIParticleRenderer> m_Renderers = new List<UIParticleRenderer>();
|
||||||
|
|
||||||
#if !SERIALIZE_FIELD_MASKABLE
|
#if !SERIALIZE_FIELD_MASKABLE
|
||||||
|
@ -52,6 +69,48 @@ namespace Coffee.UIExtensions
|
||||||
set { }
|
set { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mesh sharing.None: disable mesh sharing.
|
||||||
|
/// Auto: automatically select Primary/Reprica.
|
||||||
|
/// Primary: provides particle simulation results to the same group.
|
||||||
|
/// Primary Simulator: Primary, but do not render the particle (simulation only).
|
||||||
|
/// Reprica: render simulation results provided by the primary.
|
||||||
|
/// </summary>
|
||||||
|
public MeshSharing meshSharing
|
||||||
|
{
|
||||||
|
get { return m_MeshSharing; }
|
||||||
|
set { m_MeshSharing = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mesh sharing group ID. If non-zero is specified, particle simulation results are shared within the group.
|
||||||
|
/// </summary>
|
||||||
|
public int groupId
|
||||||
|
{
|
||||||
|
get { return m_GroupId; }
|
||||||
|
set { m_GroupId = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool useMeshSharing
|
||||||
|
{
|
||||||
|
get { return m_MeshSharing != MeshSharing.None; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool isPrimary
|
||||||
|
{
|
||||||
|
get { return m_MeshSharing == MeshSharing.Primary || m_MeshSharing == MeshSharing.PrimarySimulator; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool canSimulate
|
||||||
|
{
|
||||||
|
get { return m_MeshSharing == MeshSharing.None || m_MeshSharing == MeshSharing.Auto || m_MeshSharing == MeshSharing.Primary || m_MeshSharing == MeshSharing.PrimarySimulator; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool canRender
|
||||||
|
{
|
||||||
|
get { return m_MeshSharing == MeshSharing.None || m_MeshSharing == MeshSharing.Auto || m_MeshSharing == MeshSharing.Primary || m_MeshSharing == MeshSharing.Reprica; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Particle effect scale.
|
/// Particle effect scale.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -17,6 +17,7 @@ namespace Coffee.UIExtensions
|
||||||
private static ParticleSystem.Particle[] s_Particles = new ParticleSystem.Particle[2048];
|
private static ParticleSystem.Particle[] s_Particles = new ParticleSystem.Particle[2048];
|
||||||
private static readonly List<Material> s_Materials = new List<Material>(2);
|
private static readonly List<Material> s_Materials = new List<Material>(2);
|
||||||
private static MaterialPropertyBlock s_Mpb;
|
private static MaterialPropertyBlock s_Mpb;
|
||||||
|
private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>();
|
||||||
|
|
||||||
private ParticleSystemRenderer _renderer;
|
private ParticleSystemRenderer _renderer;
|
||||||
private ParticleSystem _particleSystem;
|
private ParticleSystem _particleSystem;
|
||||||
|
@ -30,6 +31,7 @@ namespace Coffee.UIExtensions
|
||||||
private Vector2Int _prevScreenSize;
|
private Vector2Int _prevScreenSize;
|
||||||
private bool _delay = false;
|
private bool _delay = false;
|
||||||
private bool _prewarm = false;
|
private bool _prewarm = false;
|
||||||
|
private Material _currentMaterialForRendering;
|
||||||
|
|
||||||
public override Texture mainTexture
|
public override Texture mainTexture
|
||||||
{
|
{
|
||||||
|
@ -76,6 +78,8 @@ namespace Coffee.UIExtensions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override Material GetModifiedMaterial(Material baseMaterial)
|
public override Material GetModifiedMaterial(Material baseMaterial)
|
||||||
{
|
{
|
||||||
|
_currentMaterialForRendering = null;
|
||||||
|
|
||||||
if (!IsActive()) return baseMaterial;
|
if (!IsActive()) return baseMaterial;
|
||||||
|
|
||||||
var modifiedMaterial = base.GetModifiedMaterial(baseMaterial);
|
var modifiedMaterial = base.GetModifiedMaterial(baseMaterial);
|
||||||
|
@ -164,6 +168,7 @@ namespace Coffee.UIExtensions
|
||||||
// No particle to render: Clear mesh.
|
// No particle to render: Clear mesh.
|
||||||
if (
|
if (
|
||||||
!enabled || !_particleSystem || !_parent || !canvasRenderer || !canvas || !bakeCamera
|
!enabled || !_particleSystem || !_parent || !canvasRenderer || !canvas || !bakeCamera
|
||||||
|
|| _parent.meshSharing == UIParticle.MeshSharing.Reprica
|
||||||
|| !transform.lossyScale.GetScaled(_parent.scale3D).IsVisible() // Scale is not visible.
|
|| !transform.lossyScale.GetScaled(_parent.scale3D).IsVisible() // Scale is not visible.
|
||||||
|| (!_particleSystem.IsAlive() && !_particleSystem.isPlaying) // No particle.
|
|| (!_particleSystem.IsAlive() && !_particleSystem.isPlaying) // No particle.
|
||||||
|| (_isTrail && !_particleSystem.trails.enabled) // Trail, but it is not enabled.
|
|| (_isTrail && !_particleSystem.trails.enabled) // Trail, but it is not enabled.
|
||||||
|
@ -185,9 +190,9 @@ namespace Coffee.UIExtensions
|
||||||
var psPos = _particleSystem.transform.position;
|
var psPos = _particleSystem.transform.position;
|
||||||
|
|
||||||
// Simulate particles.
|
// Simulate particles.
|
||||||
if (!_isTrail)
|
Profiler.BeginSample("[UIParticle] Bake Mesh > Simulate Particles");
|
||||||
|
if (!_isTrail && _parent.canSimulate)
|
||||||
{
|
{
|
||||||
Profiler.BeginSample("[UIParticle] Bake Mesh > Simulate Particles");
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
if (!Application.isPlaying)
|
if (!Application.isPlaying)
|
||||||
{
|
{
|
||||||
|
@ -210,32 +215,31 @@ namespace Coffee.UIExtensions
|
||||||
_particleSystem.Stop(false);
|
_particleSystem.Stop(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Profiler.EndSample();
|
|
||||||
_prevScale = scale;
|
_prevScale = scale;
|
||||||
_prevPsPos = psPos;
|
_prevPsPos = psPos;
|
||||||
_delay = false;
|
_delay = false;
|
||||||
}
|
}
|
||||||
|
Profiler.EndSample();
|
||||||
|
|
||||||
// Bake mesh.
|
// Bake mesh.
|
||||||
Profiler.BeginSample("[UIParticleRenderer] Bake Mesh");
|
Profiler.BeginSample("[UIParticleRenderer] Bake Mesh");
|
||||||
|
if (_isTrail && _parent.canSimulate)
|
||||||
{
|
{
|
||||||
if (_isTrail)
|
_renderer.BakeTrailsMesh(s_CombineInstances[0].mesh, bakeCamera, true);
|
||||||
{
|
}
|
||||||
_renderer.BakeTrailsMesh(s_CombineInstances[0].mesh, bakeCamera, true);
|
else if (_renderer.CanBakeMesh())
|
||||||
}
|
{
|
||||||
else if (_renderer.CanBakeMesh())
|
_renderer.BakeMesh(s_CombineInstances[0].mesh, bakeCamera, true);
|
||||||
{
|
}
|
||||||
_renderer.BakeMesh(s_CombineInstances[0].mesh, bakeCamera, true);
|
else
|
||||||
}
|
{
|
||||||
else
|
s_CombineInstances[0].mesh.Clear();
|
||||||
{
|
|
||||||
s_CombineInstances[0].mesh.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
|
|
||||||
// Combine mesh to transform. ([ParticleSystem local ->] world -> renderer local)
|
// Combine mesh to transform. ([ParticleSystem local ->] world -> renderer local)
|
||||||
Profiler.BeginSample("[UIParticleRenderer] Combine Mesh");
|
Profiler.BeginSample("[UIParticleRenderer] Combine Mesh");
|
||||||
|
if (_parent.canSimulate)
|
||||||
{
|
{
|
||||||
s_CombineInstances[0].transform = canvasRenderer.transform.worldToLocalMatrix * GetWorldMatrix(psPos, scale);
|
s_CombineInstances[0].transform = canvasRenderer.transform.worldToLocalMatrix * GetWorldMatrix(psPos, scale);
|
||||||
workerMesh.CombineMeshes(s_CombineInstances, true, true);
|
workerMesh.CombineMeshes(s_CombineInstances, true, true);
|
||||||
|
@ -252,15 +256,49 @@ namespace Coffee.UIExtensions
|
||||||
}
|
}
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
|
|
||||||
|
|
||||||
|
// Get grouped renderers.
|
||||||
|
s_Renderers.Clear();
|
||||||
|
if (_parent.useMeshSharing)
|
||||||
|
{
|
||||||
|
UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, s_Renderers);
|
||||||
|
}
|
||||||
|
|
||||||
// Set mesh to the CanvasRenderer.
|
// Set mesh to the CanvasRenderer.
|
||||||
Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
|
Profiler.BeginSample("[UIParticleRenderer] Set Mesh");
|
||||||
|
for (int i = 0; i < s_Renderers.Count; i++)
|
||||||
|
{
|
||||||
|
if (s_Renderers[i] == this) continue;
|
||||||
|
s_Renderers[i].canvasRenderer.SetMesh(workerMesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_parent.canRender)
|
||||||
|
{
|
||||||
|
workerMesh.Clear();
|
||||||
|
}
|
||||||
canvasRenderer.SetMesh(workerMesh);
|
canvasRenderer.SetMesh(workerMesh);
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
|
|
||||||
// Update animatable material properties.
|
// Update animatable material properties.
|
||||||
Profiler.BeginSample("[UIParticleRenderer] Update Animatable Material Properties");
|
Profiler.BeginSample("[UIParticleRenderer] Update Animatable Material Properties");
|
||||||
UpdateMaterialProperties();
|
UpdateMaterialProperties();
|
||||||
|
if (!_parent.useMeshSharing)
|
||||||
|
{
|
||||||
|
if (!_currentMaterialForRendering)
|
||||||
|
{
|
||||||
|
_currentMaterialForRendering = materialForRendering;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < s_Renderers.Count; i++)
|
||||||
|
{
|
||||||
|
if (s_Renderers[i] == this) continue;
|
||||||
|
|
||||||
|
s_Renderers[i].canvasRenderer.materialCount = 1;
|
||||||
|
s_Renderers[i].canvasRenderer.SetMaterial(_currentMaterialForRendering, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
Profiler.EndSample();
|
Profiler.EndSample();
|
||||||
|
|
||||||
|
s_Renderers.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnEnable()
|
protected override void OnEnable()
|
||||||
|
@ -275,6 +313,7 @@ namespace Coffee.UIExtensions
|
||||||
hideFlags = HideFlags.HideAndDontSave,
|
hideFlags = HideFlags.HideAndDontSave,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
_currentMaterialForRendering = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisable()
|
protected override void OnDisable()
|
||||||
|
@ -283,6 +322,7 @@ namespace Coffee.UIExtensions
|
||||||
|
|
||||||
ModifiedMaterial.Remove(_modifiedMaterial);
|
ModifiedMaterial.Remove(_modifiedMaterial);
|
||||||
_modifiedMaterial = null;
|
_modifiedMaterial = null;
|
||||||
|
_currentMaterialForRendering = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Profiling;
|
|
||||||
|
|
||||||
namespace Coffee.UIExtensions
|
namespace Coffee.UIExtensions
|
||||||
{
|
{
|
||||||
internal static class UIParticleUpdater
|
internal static class UIParticleUpdater
|
||||||
{
|
{
|
||||||
static readonly List<UIParticle> s_ActiveParticles = new List<UIParticle>();
|
static readonly List<UIParticle> s_ActiveParticles = new List<UIParticle>();
|
||||||
|
static readonly HashSet<int> s_UpdatedGroupIds = new HashSet<int>();
|
||||||
private static int frameCount = 0;
|
private static int frameCount = 0;
|
||||||
|
|
||||||
public static int uiParticleCount
|
public static int uiParticleCount
|
||||||
|
@ -46,21 +45,61 @@ namespace Coffee.UIExtensions
|
||||||
if (frameCount == Time.frameCount) return;
|
if (frameCount == Time.frameCount) return;
|
||||||
frameCount = Time.frameCount;
|
frameCount = Time.frameCount;
|
||||||
|
|
||||||
Profiler.BeginSample("[UIParticle] Refresh");
|
// Simulate -> Primary
|
||||||
for (var i = 0; i < s_ActiveParticles.Count; i++)
|
for (var i = 0; i < s_ActiveParticles.Count; i++)
|
||||||
{
|
{
|
||||||
var uip = s_ActiveParticles[i];
|
var uip = s_ActiveParticles[i];
|
||||||
try
|
if (!uip.isPrimary || s_UpdatedGroupIds.Contains(uip.groupId)) continue;
|
||||||
|
|
||||||
|
s_UpdatedGroupIds.Add(uip.groupId);
|
||||||
|
uip.UpdateTransformScale();
|
||||||
|
uip.UpdateRenderers();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate -> Others
|
||||||
|
for (var i = 0; i < s_ActiveParticles.Count; i++)
|
||||||
|
{
|
||||||
|
var uip = s_ActiveParticles[i];
|
||||||
|
uip.UpdateTransformScale();
|
||||||
|
|
||||||
|
if (!uip.useMeshSharing)
|
||||||
{
|
{
|
||||||
uip.UpdateTransformScale();
|
|
||||||
uip.UpdateRenderers();
|
uip.UpdateRenderers();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
else if (!s_UpdatedGroupIds.Contains(uip.groupId))
|
||||||
{
|
{
|
||||||
Debug.LogException(e);
|
s_UpdatedGroupIds.Add(uip.groupId);
|
||||||
|
uip.UpdateRenderers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Profiler.EndSample();
|
|
||||||
|
s_UpdatedGroupIds.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void GetGroupedRenderers(int groupId, int index, List<UIParticleRenderer> results)
|
||||||
|
{
|
||||||
|
results.Clear();
|
||||||
|
for (var i = 0; i < s_ActiveParticles.Count; i++)
|
||||||
|
{
|
||||||
|
var uip = s_ActiveParticles[i];
|
||||||
|
if (uip.useMeshSharing && uip.groupId == groupId)
|
||||||
|
{
|
||||||
|
results.Add(uip.GetRenderer(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static UIParticle GetPrimary(int groupId)
|
||||||
|
{
|
||||||
|
UIParticle primary = null;
|
||||||
|
for (var i = 0; i < s_ActiveParticles.Count; i++)
|
||||||
|
{
|
||||||
|
var uip = s_ActiveParticles[i];
|
||||||
|
if (!uip.useMeshSharing || uip.groupId != groupId) continue;
|
||||||
|
if (uip.isPrimary) return uip;
|
||||||
|
if (!primary && uip.canSimulate) primary = uip;
|
||||||
|
}
|
||||||
|
return primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue