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.

377 lines
11 KiB
C#

/******************************************************************************/
/*
Project - MudBun
Publisher - Long Bunny Labs
http://LongBunnyLabs.com
Author - Ming-Lun "Allen" Chou
http://AllenChou.net
*/
/******************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
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 MudCurveFull : MudSolid
{
/*
[Header("Noise")]
[SerializeField] private bool m_enableNoise = false;
[SerializeField] private float m_noiseOffset = 0.0f;
[SerializeField] private Vector2 m_noiseBaseOctaveSize = 0.5f * Vector2.one;
[SerializeField] [Range(0.0f, 1.0f)] private float m_noiseThreshold = 0.5f;
[SerializeField] [Range(1, 3)] private int m_noiseNumOctaves = 2;
[SerializeField] private float m_noiseOctaveOffsetFactor = 0.5f;
public bool EnableNoise { get => m_enableNoise; set { m_enableNoise = value; MarkDirty(); } }
public float NoiseOffset { get => m_noiseOffset; set { m_noiseOffset = value; MarkDirty(); } }
public Vector2 NoiseBaseOctaveSize { get => m_noiseBaseOctaveSize; set { m_noiseBaseOctaveSize = value; MarkDirty(); } }
public float NoiseThreshold { get => m_noiseThreshold; set { m_noiseThreshold = value; MarkDirty(); } }
public int NoiseNumOctaves { get => m_noiseNumOctaves; set { m_noiseNumOctaves = value; MarkDirty(); } }
public float NoiseOctaveOffsetFactor { get => m_noiseOctaveOffsetFactor; set { m_noiseOctaveOffsetFactor = value; MarkDirty(); } }
*/
[Serializable]
public class Point
{
public Transform Transform;
public float Radius;
public Point(Transform transform = null, float radius = 0.2f)
{
Transform = transform;
Radius = radius;
}
public Point(GameObject go, float radius = 0.2f)
{
Transform = go?.transform;
Radius = radius;
}
}
[Header("Shape")]
[SerializeField] [Range(1, 16)] private int m_precision = 8;
public int Precision { get => m_precision; set { m_precision = value; MarkDirty(); } }
public Transform HeadControlPoint;
public Transform TailControlPoint;
[SerializeField] private List<Point> m_points = new List<Point>();
public IList<Point> Points
{
get => m_points;
set
{
m_points.Clear();
foreach (var p in value)
m_points.Add(p);
MarkDirty();
}
}
public MudCurveFull()
{
m_points.Add(new Point());
}
public override Aabb RawBoundsRs
{
get
{
Aabb bounds = Aabb.Empty;
foreach (var p in m_points)
{
if (p == null || p.Transform == null)
continue;
Vector3 posRs = PointRs(p.Transform.position);
Vector3 r = 1.5f * p.Radius * Vector3.one;
bounds.Include(new Aabb(posRs - r, posRs + r));
}
return bounds;
}
}
public override void SanitizeParameters()
{
base.SanitizeParameters();
//Validate.NonNegative(ref m_noiseBaseOctaveSize);
if (m_points != null)
{
foreach(var p in m_points)
{
if (p == null || p.Transform == null)
continue;
Validate.NonNegative(ref p.Radius);
}
}
}
private void Update()
{
foreach (var p in m_points)
{
if (p == null || p.Transform == null)
continue;
if (!p.Transform.hasChanged)
continue;
MarkDirty();
p.Transform.hasChanged = false;
}
if (HeadControlPoint != null && HeadControlPoint.hasChanged)
{
MarkDirty();
HeadControlPoint.hasChanged = false;
}
if (TailControlPoint != null && TailControlPoint.hasChanged)
{
MarkDirty();
TailControlPoint.hasChanged = false;
}
}
public override int FillComputeData(NativeArray<SdfBrush> aBrush, int iStart, List<Transform> aBone)
{
if (m_points == null || m_points.Count == 0)
return 0;
if (m_points.Any(p => p == null || p.Transform == null))
return 0;
SdfBrush brush = SdfBrush.New();
brush.Type = (int) SdfBrush.TypeEnum.CurveFull;
if (m_points.Count == 1)
{
return 0;
}
int iBrush = iStart;
brush.Data0.x = m_points.Count + 2;
brush.Data0.y = Precision;
brush.Data0.z = 0.0f;//m_enableNoise ? 1.0f : 0.0f;
if (aBone != null)
{
brush.BoneIndex = aBone.Count;
foreach (var p in m_points)
aBone.Add(p.Transform);
}
aBrush[iBrush++] = brush;
brush.Type = (int) SdfBrush.TypeEnum.Nop;
/*
if (m_enableNoise)
{
brush.Data0 = new Vector4(m_noiseBaseOctaveSize.x, m_noiseBaseOctaveSize.y, m_noiseBaseOctaveSize.y, m_noiseThreshold);
brush.Data1 = new Vector4(m_noiseOffset, 0.0f, 0.0f, m_noiseNumOctaves);
brush.Data2 = new Vector4(m_noiseOctaveOffsetFactor, 0.0f, 0.0f, 0.0f);
aBrush[iBrush++] = brush;
}
*/
int iPreHead = iBrush;
var head = m_points[0];
var postHead = m_points[1];
Vector3 preHeadPosRs =
HeadControlPoint == null
? 2.0f * head.Transform.position - postHead.Transform.position
: HeadControlPoint.position;
preHeadPosRs = PointRs(preHeadPosRs);
brush.Data0 = new Vector4(preHeadPosRs.x, preHeadPosRs.y, preHeadPosRs.z, head.Radius);
aBrush[iBrush++] = brush;
for (int i = 0; i < m_points.Count; ++i)
{
var p = m_points[i];
Vector3 pointPosRs = PointRs(p.Transform.position);
brush.Data0 = new Vector4(pointPosRs.x, pointPosRs.y, pointPosRs.z, p.Radius);
aBrush[iBrush++] = brush;
}
int iPostTail = iBrush;
var tail = m_points[m_points.Count - 1];
var preTail = m_points[m_points.Count - 2];
Vector3 postTailPosRs =
TailControlPoint == null
? 2.0f * tail.Transform.position - preTail.Transform.position
: TailControlPoint.position;
postTailPosRs = PointRs(postTailPosRs);
brush.Data0 = new Vector4(postTailPosRs.x, postTailPosRs.y, postTailPosRs.z, tail.Radius);
aBrush[iBrush++] = brush;
if (HeadControlPoint == null)
{
Vector3 headPosRs = PointRs(head.Transform.position);
Vector3 postHeadPosRs = PointRs(postHead.Transform.position);
Vector3 headControlPosRs =
2.0f * headPosRs
- VectorUtil.CatmullRom
(
preHeadPosRs,
headPosRs,
postHeadPosRs,
aBrush[iPreHead + 3].Data0,
0.75f
);
var b = aBrush[iPreHead];
b.Data0 = new Vector4(headControlPosRs.x, headControlPosRs.y, headControlPosRs.z, head.Radius);
aBrush[iPreHead] = b;
}
if (TailControlPoint == null)
{
Vector3 tailPosRs = PointRs(tail.Transform.position);
Vector3 preTailPosRs = PointRs(preTail.Transform.position);
Vector3 tailControlPosRs =
2.0f * tailPosRs
- VectorUtil.CatmullRom
(
postTailPosRs,
tailPosRs,
preTailPosRs,
aBrush[iPostTail - 3].Data0,
0.75f
);
var b = aBrush[iPostTail];
b.Data0 = new Vector4(tailControlPosRs.x, tailControlPosRs.y, tailControlPosRs.z, tail.Radius);
aBrush[iPostTail] = b;
}
return iBrush - iStart;
}
#if MUDBUN_BURST
[BurstCompile]
[MonoPInvokeCallback(typeof(Sdf.SdfBrushEvalFunc))]
[RegisterSdfBrushEvalFunc(SdfBrush.TypeEnum.CurveFull)]
public static unsafe float EvaluateSdf(float resDummy, ref float3 p, in float3 pRel, SdfBrush* aBrush, int iBrush)
{
float res = float.MaxValue;
var b = aBrush[iBrush];
int numPoints = (int) b.Data0.x;
if (numPoints > 1)
{
int precision = (int) b.Data0.y;
float dt = 1.0f / precision;
bool useNoise = false;//(b.Data0.z > 0.0f);
int iA = iBrush + (useNoise ? 2 : 1);
float globalLen = 0.0f;
int iClosest = -1;
float tClosest = 0.0f;
float segResClosest = 0.0f;
float rClosest = 0.0f;
float3 pClosest = 0.0f;
float closestLen = 0.0f;
for (int i = 1, n = numPoints - 2; i < n; ++i, ++iA)
{
float3 pA = new float4(aBrush[iA + 0].Data0).xyz;
float3 pB = new float4(aBrush[iA + 1].Data0).xyz;
float3 pC = new float4(aBrush[iA + 2].Data0).xyz;
float3 pD = new float4(aBrush[iA + 3].Data0).xyz;
float3 prevPos = pB;
float r = aBrush[iA + 1].Data0.w;
float dr = (aBrush[iA + 2].Data0.w - r) * dt;
float localLen = 0.0f;
for (float t = dt; t < 1.0001f; t += dt)
{
float3 currPos;
MathUtil.CatmullRom(pA, pB, pC, pD, math.min(1.0f, t), out currPos);
float segLen = math.length(currPos - prevPos);
float d = Sdf.RoundCone(p, prevPos, currPos, r, r + dr);
if (d < res)
{
float2 segRes;
Sdf.Segment(p, prevPos, currPos, out segRes);
res = d;
iClosest = i;
tClosest = t;
rClosest = r + dr * segRes.y;
pClosest = math.lerp(prevPos, currPos, segRes.y);
closestLen = globalLen + localLen + segLen * segRes.y;
segResClosest = segRes.y;
}
prevPos = currPos;
r += dr;
localLen += segLen;
}
globalLen += localLen;
}
}
return res;
}
#endif
public override void DrawSelectionGizmosRs()
{
base.DrawSelectionGizmosRs();
if (m_points == null)
return;
if (m_points.Any(p => p == null || p.Transform == null))
return;
GizmosUtil.DrawInvisibleCatmullRom
(
m_points.Select(p => PointRs(p.Transform.position)).ToArray(),
m_points.Select(p => p.Radius).ToArray(),
HeadControlPoint != null ? PointRs(HeadControlPoint.position) : VectorUtil.Invalid,
TailControlPoint != null ? PointRs(TailControlPoint.position) : VectorUtil.Invalid
);
}
public override void DrawOutlineGizmosRs()
{
base.DrawOutlineGizmosRs();
if (m_points == null)
return;
if (m_points.Any(p => p == null || p.Transform == null))
return;
GizmosUtil.DrawWireCatmullRom
(
m_points.Select(p => PointRs(p.Transform.position)).ToArray(),
m_points.Select(p => p.Radius).ToArray(),
HeadControlPoint != null ? PointRs(HeadControlPoint.position) : VectorUtil.Invalid,
TailControlPoint != null ? PointRs(TailControlPoint.position) : VectorUtil.Invalid
);
}
}
}