You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

217 lines
6.3 KiB
C#

/******************************************************************************/
/*
Project - MudBun
Publisher - Long Bunny Labs
http://LongBunnyLabs.com
Author - Ming-Lun "Allen" Chou
http://AllenChou.net
*/
/******************************************************************************/
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
#if MUDBUN_BURST
using Unity.Burst;
using Unity.Mathematics;
using AOT;
#endif
namespace MudBun
{
#if MUDBUN_BURST
[BurstCompile]
#endif
[ExecuteInEditMode]
public class MudParticleSystem : MudSolid
{
public ParticleSystem Particles;
[SerializeField] private float m_radiusMultiplier = 1.0f;
public float RadiusMultiplier { get => m_radiusMultiplier; set { m_radiusMultiplier = value; MarkDirty(); } }
[SerializeField] private float m_selfBlend = 0.5f;
public float SelfBlend { get => m_selfBlend; set { m_selfBlend = value; MarkDirty(); } }
private static readonly int InitNumParticles = 16;
private int m_lastReadFrame = -1;
// read particle data
private ParticleSystem.Particle [] m_aParticle = new ParticleSystem.Particle[InitNumParticles];
private int m_numParticles = 0;
private Vector3 [] m_aPosWs = new Vector3[InitNumParticles];
private Vector3 [] m_aPosRs = new Vector3[InitNumParticles];
private float [] m_aRadius = new float[InitNumParticles];
private float [] m_aSelfBlendMult = new float[InitNumParticles];
public override Aabb RawBoundsRs
{
get
{
if (!ReadParticles())
return Aabb.Empty;
Aabb bounds = Aabb.Empty;
for (int i = 0, n = Particles.particleCount; i < n; ++i)
{
Vector3 posCs = m_aPosRs[i];
Vector3 r = (m_aRadius[i] + m_selfBlend) * Vector3.one;
bounds.Include(new Aabb(posCs - r, posCs + r));
}
return bounds;
}
}
private bool ReadParticles()
{
if (Particles == null)
return false;
if (!Particles.isPlaying)
return m_numParticles > 0;
if (m_lastReadFrame >= Time.renderedFrameCount)
return m_aParticle.Length >= Particles.particleCount;
if (m_aParticle.Length < Particles.particleCount)
{
int newLen = m_aParticle.Length;
while (newLen < Particles.particleCount)
newLen *= 2;
m_aParticle = new ParticleSystem.Particle[newLen];
m_aPosWs = new Vector3[newLen];
m_aPosRs = new Vector3[newLen];
m_aRadius = new float[newLen];
m_aSelfBlendMult = new float[newLen];
}
float selfBlendSizeFactor = Mathf.Clamp(5.0f / Mathf.Max(MathUtil.Epsilon, Particles.main.startSizeMultiplier), 0.1f, 100.0f);
Particles.GetParticles(m_aParticle, Particles.particleCount);
for (int i = 0, n = Particles.particleCount; i < n; ++i)
{
m_aPosWs[i] = Particles.gameObject.transform.TransformPoint(m_aParticle[i].position);
m_aRadius[i] = m_aParticle[i].GetCurrentSize(Particles) * m_radiusMultiplier;
m_aSelfBlendMult[i] = Mathf.Clamp01(m_aParticle[i].GetCurrentSize(Particles) * selfBlendSizeFactor);
}
switch (Particles.main.simulationSpace)
{
case ParticleSystemSimulationSpace.Local:
break;
case ParticleSystemSimulationSpace.World:
for (int i = 0, n = Particles.particleCount; i < n; ++i)
m_aPosWs[i] = transform.InverseTransformPoint(m_aPosWs[i]);
break;
case ParticleSystemSimulationSpace.Custom:
if (Particles.main.customSimulationSpace != null)
for (int i = 0, n = Particles.particleCount; i < n; ++i)
m_aPosWs[i] = Particles.main.customSimulationSpace.InverseTransformPoint(m_aPosWs[i]);
break;
}
for (int i = 0, n = Particles.particleCount; i < n; ++i)
m_aPosRs[i] = PointRs(m_aPosWs[i]);
m_lastReadFrame = Time.renderedFrameCount;
m_numParticles = Particles.particleCount;
return true;
}
private void LateUpdate()
{
if (!ReadParticles())
return;
MarkDirty();
}
public override void SanitizeParameters()
{
base.SanitizeParameters();
Validate.NonNegative(ref m_radiusMultiplier);
Validate.NonNegative(ref m_selfBlend);
}
private int m_iStart = -1;
public override int FillComputeData(NativeArray<SdfBrush> aBrush, int iStart, List<Transform> aBone)
{
m_iStart = -1;
if (!ReadParticles())
return 0;
m_iStart = iStart;
SdfBrush brush = SdfBrush.New();
if (aBone != null)
{
brush.BoneIndex = aBone.Count;
aBone.Add(gameObject.transform);
}
for (int i = 0, n = m_numParticles; i < n; ++i)
{
if (i == 0)
{
brush.Type = (int) SdfBrush.TypeEnum.ParticleSystem;
brush.Data2.x = n;
}
else
{
brush.Type = (int) SdfBrush.TypeEnum.Nop;
}
Vector3 posCs = m_aPosRs[i];
float r = m_aRadius[i];
brush.Data0 = new Vector4(posCs.x, posCs.y, posCs.z, r);
// fade out self blend as particles die off to avoid pops
brush.Data1.x = m_selfBlend * Mathf.Clamp01(10.0f * (m_aSelfBlendMult[i] - 0.1f));
aBrush[iStart++] = brush;
}
return iStart - m_iStart;
}
public override void FillBrushData(ref SdfBrush brush, int iBrush)
{
if (m_iStart < 0)
return;
// only need to fill in the first one
if (iBrush == m_iStart)
base.FillBrushData(ref brush, iBrush);
brush.Position = m_aPosWs[brush.Index - m_iStart];
}
#if MUDBUN_BURST
[BurstCompile]
[MonoPInvokeCallback(typeof(Sdf.SdfBrushEvalFunc))]
[RegisterSdfBrushEvalFunc(SdfBrush.TypeEnum.ParticleSystem)]
public static unsafe float EvaluateSdf(float resDummy, ref float3 p, in float3 pRel, SdfBrush* aBrush, int iBrush)
{
float res = float.MaxValue;
int numParticles = (int) aBrush[iBrush].Data2.x;
for (int i = 0; i < numParticles; ++i)
{
float3 pos = new float4(aBrush[iBrush + i].Data0).xyz;
float r = aBrush[iBrush + i].Data0.w;
float selfBlend = aBrush[iBrush + i].Data1.x;
res = Sdf.UniCubic(res, Sdf.Sphere(p - pos, r), selfBlend);
}
return res;
}
#endif
}
}