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
|
||||
private SerializedProperty m_Scale3D;
|
||||
private SerializedProperty m_AnimatableProperties;
|
||||
private SerializedProperty m_MeshSharing;
|
||||
private SerializedProperty m_GroupId;
|
||||
|
||||
private ReorderableList _ro;
|
||||
static private bool _xyzMode;
|
||||
|
@ -133,6 +135,8 @@ namespace Coffee.UIExtensions
|
|||
m_Maskable = serializedObject.FindProperty("m_Maskable");
|
||||
m_Scale3D = serializedObject.FindProperty("m_Scale3D");
|
||||
m_AnimatableProperties = serializedObject.FindProperty("m_AnimatableProperties");
|
||||
m_MeshSharing = serializedObject.FindProperty("m_MeshSharing");
|
||||
m_GroupId = serializedObject.FindProperty("m_GroupId");
|
||||
|
||||
var sp = serializedObject.FindProperty("m_Particles");
|
||||
_ro = new ReorderableList(sp.serializedObject, sp, true, true, true, true);
|
||||
|
@ -213,7 +217,9 @@ namespace Coffee.UIExtensions
|
|||
EditorGUILayout.PropertyField(m_Maskable);
|
||||
|
||||
// Scale
|
||||
EditorGUI.BeginDisabledGroup(!m_MeshSharing.hasMultipleDifferentValues && m_MeshSharing.intValue == 4);
|
||||
_xyzMode = DrawFloatOrVector3Field(m_Scale3D, _xyzMode);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
// AnimatableProperties
|
||||
var mats = current.particles
|
||||
|
@ -225,6 +231,9 @@ namespace Coffee.UIExtensions
|
|||
// Animated properties
|
||||
AnimatedPropertiesEditor.DrawAnimatableProperties(m_AnimatableProperties, mats);
|
||||
|
||||
// Mesh sharing
|
||||
DrawMeshSharing();
|
||||
|
||||
// Target ParticleSystems.
|
||||
_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)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -20,6 +20,15 @@ namespace Coffee.UIExtensions
|
|||
[RequireComponent(typeof(CanvasRenderer))]
|
||||
public class UIParticle : MaskableGraphic
|
||||
{
|
||||
public enum MeshSharing
|
||||
{
|
||||
None,
|
||||
Auto,
|
||||
Primary,
|
||||
PrimarySimulator,
|
||||
Reprica,
|
||||
}
|
||||
|
||||
[HideInInspector][SerializeField] internal bool m_IsTrail = false;
|
||||
|
||||
[Tooltip("Particle effect scale")]
|
||||
|
@ -34,6 +43,14 @@ namespace Coffee.UIExtensions
|
|||
[SerializeField]
|
||||
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>();
|
||||
|
||||
#if !SERIALIZE_FIELD_MASKABLE
|
||||
|
@ -52,6 +69,48 @@ namespace Coffee.UIExtensions
|
|||
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>
|
||||
/// Particle effect scale.
|
||||
/// </summary>
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Coffee.UIExtensions
|
|||
private static ParticleSystem.Particle[] s_Particles = new ParticleSystem.Particle[2048];
|
||||
private static readonly List<Material> s_Materials = new List<Material>(2);
|
||||
private static MaterialPropertyBlock s_Mpb;
|
||||
private static readonly List<UIParticleRenderer> s_Renderers = new List<UIParticleRenderer>();
|
||||
|
||||
private ParticleSystemRenderer _renderer;
|
||||
private ParticleSystem _particleSystem;
|
||||
|
@ -30,6 +31,7 @@ namespace Coffee.UIExtensions
|
|||
private Vector2Int _prevScreenSize;
|
||||
private bool _delay = false;
|
||||
private bool _prewarm = false;
|
||||
private Material _currentMaterialForRendering;
|
||||
|
||||
public override Texture mainTexture
|
||||
{
|
||||
|
@ -76,6 +78,8 @@ namespace Coffee.UIExtensions
|
|||
/// </summary>
|
||||
public override Material GetModifiedMaterial(Material baseMaterial)
|
||||
{
|
||||
_currentMaterialForRendering = null;
|
||||
|
||||
if (!IsActive()) return baseMaterial;
|
||||
|
||||
var modifiedMaterial = base.GetModifiedMaterial(baseMaterial);
|
||||
|
@ -164,6 +168,7 @@ namespace Coffee.UIExtensions
|
|||
// No particle to render: Clear mesh.
|
||||
if (
|
||||
!enabled || !_particleSystem || !_parent || !canvasRenderer || !canvas || !bakeCamera
|
||||
|| _parent.meshSharing == UIParticle.MeshSharing.Reprica
|
||||
|| !transform.lossyScale.GetScaled(_parent.scale3D).IsVisible() // Scale is not visible.
|
||||
|| (!_particleSystem.IsAlive() && !_particleSystem.isPlaying) // No particle.
|
||||
|| (_isTrail && !_particleSystem.trails.enabled) // Trail, but it is not enabled.
|
||||
|
@ -185,9 +190,9 @@ namespace Coffee.UIExtensions
|
|||
var psPos = _particleSystem.transform.position;
|
||||
|
||||
// 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 (!Application.isPlaying)
|
||||
{
|
||||
|
@ -210,32 +215,31 @@ namespace Coffee.UIExtensions
|
|||
_particleSystem.Stop(false);
|
||||
}
|
||||
}
|
||||
Profiler.EndSample();
|
||||
_prevScale = scale;
|
||||
_prevPsPos = psPos;
|
||||
_delay = false;
|
||||
}
|
||||
Profiler.EndSample();
|
||||
|
||||
// Bake mesh.
|
||||
Profiler.BeginSample("[UIParticleRenderer] Bake Mesh");
|
||||
if (_isTrail && _parent.canSimulate)
|
||||
{
|
||||
if (_isTrail)
|
||||
{
|
||||
_renderer.BakeTrailsMesh(s_CombineInstances[0].mesh, bakeCamera, true);
|
||||
}
|
||||
else if (_renderer.CanBakeMesh())
|
||||
{
|
||||
_renderer.BakeMesh(s_CombineInstances[0].mesh, bakeCamera, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_CombineInstances[0].mesh.Clear();
|
||||
}
|
||||
_renderer.BakeTrailsMesh(s_CombineInstances[0].mesh, bakeCamera, true);
|
||||
}
|
||||
else if (_renderer.CanBakeMesh())
|
||||
{
|
||||
_renderer.BakeMesh(s_CombineInstances[0].mesh, bakeCamera, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_CombineInstances[0].mesh.Clear();
|
||||
}
|
||||
Profiler.EndSample();
|
||||
|
||||
// Combine mesh to transform. ([ParticleSystem local ->] world -> renderer local)
|
||||
Profiler.BeginSample("[UIParticleRenderer] Combine Mesh");
|
||||
if (_parent.canSimulate)
|
||||
{
|
||||
s_CombineInstances[0].transform = canvasRenderer.transform.worldToLocalMatrix * GetWorldMatrix(psPos, scale);
|
||||
workerMesh.CombineMeshes(s_CombineInstances, true, true);
|
||||
|
@ -252,15 +256,49 @@ namespace Coffee.UIExtensions
|
|||
}
|
||||
Profiler.EndSample();
|
||||
|
||||
|
||||
// Get grouped renderers.
|
||||
s_Renderers.Clear();
|
||||
if (_parent.useMeshSharing)
|
||||
{
|
||||
UIParticleUpdater.GetGroupedRenderers(_parent.groupId, _index, s_Renderers);
|
||||
}
|
||||
|
||||
// Set mesh to the CanvasRenderer.
|
||||
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);
|
||||
Profiler.EndSample();
|
||||
|
||||
// Update animatable material properties.
|
||||
Profiler.BeginSample("[UIParticleRenderer] Update Animatable Material Properties");
|
||||
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();
|
||||
|
||||
s_Renderers.Clear();
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
|
@ -275,6 +313,7 @@ namespace Coffee.UIExtensions
|
|||
hideFlags = HideFlags.HideAndDontSave,
|
||||
};
|
||||
}
|
||||
_currentMaterialForRendering = null;
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
|
@ -283,6 +322,7 @@ namespace Coffee.UIExtensions
|
|||
|
||||
ModifiedMaterial.Remove(_modifiedMaterial);
|
||||
_modifiedMaterial = null;
|
||||
_currentMaterialForRendering = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace Coffee.UIExtensions
|
||||
{
|
||||
internal static class UIParticleUpdater
|
||||
{
|
||||
static readonly List<UIParticle> s_ActiveParticles = new List<UIParticle>();
|
||||
static readonly HashSet<int> s_UpdatedGroupIds = new HashSet<int>();
|
||||
private static int frameCount = 0;
|
||||
|
||||
public static int uiParticleCount
|
||||
|
@ -46,21 +45,61 @@ namespace Coffee.UIExtensions
|
|||
if (frameCount == Time.frameCount) return;
|
||||
frameCount = Time.frameCount;
|
||||
|
||||
Profiler.BeginSample("[UIParticle] Refresh");
|
||||
// Simulate -> Primary
|
||||
for (var i = 0; i < s_ActiveParticles.Count; 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();
|
||||
}
|
||||
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