推进度!

This commit is contained in:
SoulliesOfficial
2025-11-25 21:49:03 -05:00
parent f0c06f0337
commit ad4948207e
1068 changed files with 418853 additions and 1047 deletions

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace UI_Spline_Renderer
{
public struct CopiedNativeSpline : ISpline, IDisposable
{
[ReadOnly]
NativeArray<BezierKnot> m_Knots;
[ReadOnly]
NativeArray<BezierCurve> m_Curves;
[ReadOnly]
NativeArray<DistanceToInterpolation> m_SegmentLengthsLookupTable;
bool m_Closed;
float m_Length;
const int k_SegmentResolution = 30;
public NativeArray<BezierKnot> Knots => m_Knots;
public NativeArray<BezierCurve> Curves => m_Curves;
public bool Closed => m_Closed;
public int Count => m_Knots.Length;
public readonly float GetLength() => m_Length;
public BezierKnot this[int index] => m_Knots[index];
public IEnumerator<BezierKnot> GetEnumerator() => m_Knots.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public CopiedNativeSpline(NativeArray<BezierKnot> knots, bool closed, float4x4 transform, Allocator allocator = Allocator.Temp)
{
int kc = knots.Length;
m_Knots = knots;
m_Curves = new NativeArray<BezierCurve>(kc, allocator);
m_SegmentLengthsLookupTable = new NativeArray<DistanceToInterpolation>(kc * k_SegmentResolution, allocator);
m_Closed = closed;
m_Length = 0f;
NativeArray<DistanceToInterpolation> distanceToTimes = new NativeArray<DistanceToInterpolation>(k_SegmentResolution, Allocator.Temp);
if (knots.Length > 0)
{
BezierKnot cur = knots[0].Transform(transform);
for (int i = 0; i < kc; ++i)
{
BezierKnot next = knots[(i + 1) % kc].Transform(transform);
m_Knots[i] = cur;
m_Curves[i] = new BezierCurve(cur, next);
InternalUtility.CalculateCurveLengths(m_Curves[i], distanceToTimes);
if (m_Closed || i < kc - 1)
m_Length += distanceToTimes[k_SegmentResolution - 1].Distance;
for (int index = 0; index < k_SegmentResolution; index++)
{
m_SegmentLengthsLookupTable[i * k_SegmentResolution + index] = distanceToTimes[index];
}
cur = next;
}
}
}
/// <summary>
/// Get a <see cref="BezierCurve"/> from a knot index.
/// </summary>
/// <param name="index">The knot index that serves as the first control point for this curve.</param>
/// <returns>
/// A <see cref="BezierCurve"/> formed by the knot at index and the next knot.
/// </returns>
public BezierCurve GetCurve(int index) => m_Curves[index];
/// <summary>
/// Get the length of a <see cref="BezierCurve"/>.
/// </summary>
/// <param name="curveIndex">The 0 based index of the curve to find length for.</param>
/// <returns>The length of the bezier curve at index.</returns>
public float GetCurveLength(int curveIndex)
{
return m_SegmentLengthsLookupTable[curveIndex * k_SegmentResolution + k_SegmentResolution - 1].Distance;
}
public float3 GetCurveUpVector(int index, float t)
{
return this.CalculateUpVector(index, t);
}
/// <summary>
/// Release allocated resources.
/// </summary>
public void Dispose()
{
m_Knots.Dispose();
m_Curves.Dispose();
m_SegmentLengthsLookupTable.Dispose();
}
// Wrapper around NativeSlice<T> because the native type does not implement IReadOnlyList<T>.
struct Slice<T> : IReadOnlyList<T> where T : struct
{
NativeSlice<T> m_Slice;
public Slice(NativeArray<T> array, int start, int count) { m_Slice = new NativeSlice<T>(array, start, count); }
public IEnumerator<T> GetEnumerator() => m_Slice.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int Count => m_Slice.Length;
public T this[int index] => m_Slice[index];
}
/// <summary>
/// Return the normalized interpolation (t) corresponding to a distance on a <see cref="BezierCurve"/>.
/// </summary>
/// <param name="curveIndex"> The zero-based index of the curve.</param>
/// <param name="curveDistance">The curve-relative distance to convert to an interpolation ratio (also referred to as 't').</param>
/// <returns> The normalized interpolation ratio associated to distance on the designated curve.</returns>
public float GetCurveInterpolation(int curveIndex, float curveDistance)
{
if(curveIndex <0 || curveIndex >= m_SegmentLengthsLookupTable.Length || curveDistance <= 0)
return 0f;
var curveLength = GetCurveLength(curveIndex);
if(curveDistance >= curveLength)
return 1f;
var startIndex = curveIndex * k_SegmentResolution;
var slice = new Slice<DistanceToInterpolation>(m_SegmentLengthsLookupTable, startIndex, k_SegmentResolution);
return CurveUtility.GetDistanceToInterpolation(slice, curveDistance);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ea2e3adba3aa4b25a044aa103d6fc756
timeCreated: 1713945997

View File

@@ -0,0 +1,92 @@
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
namespace UI_Spline_Renderer
{
internal struct NativeColorGradient : IDisposable
{
// y is time.
[ReadOnly]
public NativeArray<float2> alphaKeyFrames;
// w is time.
[ReadOnly]
public NativeArray<float4> colorKeyFrames;
public Color Evaluate(float t)
{
var nextAlphaIdx = -1;
for (int i = 0; i < alphaKeyFrames.Length; i++)
{
if(t > alphaKeyFrames[i].y) continue;
nextAlphaIdx = i;
break;
}
var nextColorKeyIdx = -1;
for (int i = 0; i < colorKeyFrames.Length; i++)
{
if (t > colorKeyFrames[i].w) continue;
nextColorKeyIdx = i;
break;
}
float alpha;
if (nextAlphaIdx == -1)
{
alpha = alphaKeyFrames[^1].x;
}
else if (nextAlphaIdx == 0)
{
alpha = alphaKeyFrames[0].x;
}
else
{
var preAlpha = alphaKeyFrames[nextAlphaIdx - 1];
var nextAlpha = alphaKeyFrames[nextAlphaIdx];
var remappedT = t.Remap(0, 1, preAlpha.y, nextAlpha.y);
alpha = math.lerp(preAlpha, nextAlpha, remappedT).x;
}
Color color;
if(nextColorKeyIdx == -1)
{
color = toColor(colorKeyFrames[^1]);
}
else if(nextColorKeyIdx == 0)
{
color = toColor(colorKeyFrames[0]);
}
else
{
var preColor = toColor(colorKeyFrames[nextColorKeyIdx - 1]);
var nextKey = toColor(colorKeyFrames[nextColorKeyIdx]);
var remappedT = (t - preColor.a) / (nextKey.a - preColor.a);
color = Color.Lerp(preColor, nextKey, remappedT);
}
color.a = alpha;
return color;
}
Color toColor(float4 f)
{
return new Color(f.x, f.y, f.z, f.w);
}
public void Dispose()
{
alphaKeyFrames.Dispose();
colorKeyFrames.Dispose();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: baf1300b266e4ca887cad7e70d1a70cb
timeCreated: 1690408892

View File

@@ -0,0 +1,351 @@
using System;
using System.Security.Cryptography;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;
namespace UI_Spline_Renderer
{
internal readonly struct NativeCurve : IDisposable
{
private const int TRUE = 1;
private const int FALSE = 1;
public readonly NativeArray<Keyframe> Keys;
private readonly int owner;
public NativeCurve(AnimationCurve c, Allocator alloc)
{
Keys = new NativeArray<Keyframe>(c.keys, alloc);
owner = TRUE;
}
public NativeCurve(int size, Allocator alloc)
{
Keys = new NativeArray<Keyframe>(size, alloc);
owner = TRUE;
}
public NativeCurve(Keyframe[] keyframes, Allocator alloc)
{
Keys = new NativeArray<Keyframe>(keyframes, alloc);
owner = TRUE;
}
public NativeCurve(NativeArray<Keyframe> keyframes, Allocator alloc)
{
Keys = new NativeArray<Keyframe>(keyframes, alloc);
owner = TRUE;
}
public NativeCurve(NativeArray<Keyframe> keyframes)
{
Keys = keyframes;
owner = FALSE;
}
public NativeCurve(NativeCurve other)
{
Keys = other.Keys;
owner = FALSE;
}
public NativeCurve(NativeCurve other, Allocator alloc)
{
Keys = new NativeArray<Keyframe>(other.Keys, alloc);
owner = TRUE;
}
public void Dispose()
{
if (owner != 0)
{
Keys.Dispose();
}
}
public float Evaluate(float time)
{
return CurveSampling.ThreadSafe.Evaluate(Keys, time);
}
public int Length => Keys.Length;
public float Duration => Keys[Length - 1].time - Keys[0].time;
}
public static class CurveSampling
{
const float DefaultWeight = 0;
public static class ThreadSafe
{
public static float Evaluate(NativeArray<Keyframe> keys, float curveT)
{
return EvaluateWithinRange(keys, curveT, 0, keys.Length - 1);
}
public static float EvaluateWithHint(NativeArray<Keyframe> keys, float curveT, ref int hintIndex)
{
int startIndex = 0;
int endIndex = keys.Length - 1;
if (endIndex <= hintIndex)
return keys[hintIndex].value;
// wrap time
curveT = math.clamp(curveT, keys[hintIndex].time, keys[endIndex].time);
FindIndexForSampling(keys, curveT, startIndex, endIndex, hintIndex, out int lhsIndex, out int rhsIndex);
Keyframe lhs = keys[hintIndex];
Keyframe rhs = keys[rhsIndex];
return InterpolateKeyframe(lhs, rhs, curveT);
}
public static float EvaluateWithinRange(NativeArray<Keyframe> keys, float curveT, int startIndex,
int endIndex)
{
if (endIndex <= startIndex)
return keys[startIndex].value;
// wrap time
curveT = math.clamp(curveT, keys[startIndex].time, keys[endIndex].time);
FindIndexForSampling(keys, curveT, startIndex, endIndex, -1, out int lhsIndex, out int rhsIndex);
Keyframe lhs = keys[lhsIndex];
Keyframe rhs = keys[rhsIndex];
return InterpolateKeyframe(lhs, rhs, curveT);
}
static void FindIndexForSampling(NativeArray<Keyframe> keys, float curveT, int start, int end,
int hint,
out int lhs, out int rhs)
{
if (hint != -1)
{
hint = math.clamp(hint, start, end);
// We can not use the cache time or time end since that is in unwrapped time space!
float time = keys[hint].time;
if (curveT > time)
{
const int kMaxLookahead = 3;
for (int i = 0; i < kMaxLookahead; i++)
{
int index = hint + i;
if (index + 1 < end && keys[index + 1].time > curveT)
{
lhs = index;
rhs = math.min(lhs + 1, end);
return;
}
}
}
}
// Fall back to using binary search
// upper bound (first value larger than curveT)
int __len = end - start;
int __half;
int __middle;
int __first = start;
while (__len > 0)
{
__half = __len >> 1;
__middle = __first + __half;
var mid = keys[__middle];
if (curveT < mid.time)
__len = __half;
else
{
__first = __middle;
++__first;
__len = __len - __half - 1;
}
}
// If not within range, we pick the last element twice
lhs = __first - 1;
rhs = math.min(end, __first);
}
public static float InterpolateKeyframe(Keyframe lhs, Keyframe rhs, float curveT)
{
float output;
if ((lhs.weightedMode & WeightedMode.Out) != 0 || (rhs.weightedMode & WeightedMode.In) != 0)
output = BezierInterpolate(curveT, lhs, rhs);
else
output = HermiteInterpolate(curveT, lhs, rhs);
HandleSteppedCurve(lhs, rhs, ref output);
return output;
}
static float HermiteInterpolate(float curveT, Keyframe lhs, Keyframe rhs)
{
float dx = rhs.time - lhs.time;
float m1;
float m2;
float t;
if (dx != 0.0F)
{
t = (curveT - lhs.time) / dx;
m1 = lhs.outTangent * dx;
m2 = rhs.inTangent * dx;
}
else
{
t = 0.0F;
m1 = 0;
m2 = 0;
}
return HermiteInterpolate(t, lhs.value, m1, m2, rhs.value);
}
static float HermiteInterpolate(float t, float p0, float m0, float m1, float p1)
{
float t2 = t * t;
float t3 = t2 * t;
float a = 2.0F * t3 - 3.0F * t2 + 1.0F;
float b = t3 - 2.0F * t2 + t;
float c = t3 - t2;
float d = -2.0F * t3 + 3.0F * t2;
return a * p0 + b * m0 + c * m1 + d * p1;
}
static float BezierInterpolate(float curveT, Keyframe lhs, Keyframe rhs)
{
float lhsOutWeight = (lhs.weightedMode & WeightedMode.Out) != 0 ? lhs.outWeight : DefaultWeight;
float rhsInWeight = (rhs.weightedMode & WeightedMode.In) != 0 ? rhs.inWeight : DefaultWeight;
float dx = rhs.time - lhs.time;
if (dx == 0.0F)
return lhs.value;
return BezierInterpolate((curveT - lhs.time) / dx, lhs.value, lhs.outTangent * dx, lhsOutWeight,
rhs.value, rhs.inTangent * dx, rhsInWeight);
}
static float FAST_CBRT_POSITIVE(float x)
{
return math.exp(math.log(x) / 3.0f);
}
static float FAST_CBRT(float x)
{
return (((x) < 0) ? -math.exp(math.log(-(x)) / 3.0f) : math.exp(math.log(x) / 3.0f));
}
static float BezierExtractU(float t, float w1, float w2)
{
float a = 3.0F * w1 - 3.0F * w2 + 1.0F;
float b = -6.0F * w1 + 3.0F * w2;
float c = 3.0F * w1;
float d = -t;
if (math.abs(a) > 1e-3f)
{
float p = -b / (3.0F * a);
float p2 = p * p;
float p3 = p2 * p;
float q = p3 + (b * c - 3.0F * a * d) / (6.0F * a * a);
float q2 = q * q;
float r = c / (3.0F * a);
float rmp2 = r - p2;
float s = q2 + rmp2 * rmp2 * rmp2;
if (s < 0.0F)
{
float ssi = math.sqrt(-s);
float r_1 = math.sqrt(-s + q2);
float phi = math.atan2(ssi, q);
float r_3 = FAST_CBRT_POSITIVE(r_1);
float phi_3 = phi / 3.0F;
// Extract cubic roots.
float u1 = 2.0F * r_3 * math.cos(phi_3) + p;
float u2 = 2.0F * r_3 * math.cos(phi_3 + 2.0F * (float)math.PI / 3.0f) + p;
float u3 = 2.0F * r_3 * math.cos(phi_3 - 2.0F * (float)math.PI / 3.0f) + p;
if (u1 >= 0.0F && u1 <= 1.0F)
return u1;
else if (u2 >= 0.0F && u2 <= 1.0F)
return u2;
else if (u3 >= 0.0F && u3 <= 1.0F)
return u3;
// Aiming at solving numerical imprecision when u is outside [0,1].
return (t < 0.5F) ? 0.0F : 1.0F;
}
else
{
float ss = math.sqrt(s);
float u = FAST_CBRT(q + ss) + FAST_CBRT(q - ss) + p;
if (u >= 0.0F && u <= 1.0F)
return u;
// Aiming at solving numerical imprecision when u is outside [0,1].
return (t < 0.5F) ? 0.0F : 1.0F;
}
}
if (math.abs(b) > 1e-3f)
{
float s = c * c - 4.0F * b * d;
float ss = math.sqrt(s);
float u1 = (-c - ss) / (2.0F * b);
float u2 = (-c + ss) / (2.0F * b);
if (u1 >= 0.0F && u1 <= 1.0F)
return u1;
else if (u2 >= 0.0F && u2 <= 1.0F)
return u2;
// Aiming at solving numerical imprecision when u is outside [0,1].
return (t < 0.5F) ? 0.0F : 1.0F;
}
if (math.abs(c) > 1e-3f)
{
return (-d / c);
}
return 0.0F;
}
static float BezierInterpolate(float t, float v1, float m1, float w1, float v2, float m2, float w2)
{
float u = BezierExtractU(t, w1, 1.0F - w2);
return BezierInterpolate(u, v1, w1 * m1 + v1, v2 - w2 * m2, v2);
}
static float BezierInterpolate(float t, float p0, float p1, float p2, float p3)
{
float t2 = t * t;
float t3 = t2 * t;
float omt = 1.0F - t;
float omt2 = omt * omt;
float omt3 = omt2 * omt;
return omt3 * p0 + 3.0F * t * omt2 * p1 + 3.0F * t2 * omt * p2 + t3 * p3;
}
static void HandleSteppedCurve(Keyframe lhs, Keyframe rhs, ref float value)
{
if (float.IsInfinity(lhs.outTangent) || float.IsInfinity(rhs.inTangent))
value = lhs.value;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7a04bbc35d484a798423531879e71d9c
timeCreated: 1696727209

View File

@@ -0,0 +1,571 @@
#if ENABLE_SPLINES
#if ENABLE_COLLECTIONS
#if ENABLE_MATHEMATICS
#if ENABLE_BURST
using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
using UnityEngine.Splines;
namespace UI_Spline_Renderer
{
[BurstCompile]
internal struct SplineExtrudeJob : IJob
{
[ReadOnly] public NativeSpline spline;
[ReadOnly] public NativeCurve widthCurve;
[ReadOnly] public float width;
[ReadOnly] public bool keepZeroZ;
[ReadOnly] public bool keepBillboard;
[ReadOnly] public float2 clipRange;
[ReadOnly] public float2 uvMultiplier;
[ReadOnly] public float2 uvOffset;
[ReadOnly] public UVMode uvMode;
[ReadOnly] public Color color;
[ReadOnly] internal NativeColorGradient colorGradient;
[ReadOnly] public int resolution;
[ReadOnly] public bool smooth;
[ReadOnly] public bool roundEnds;
public NativeList<UIVertex> vertices;
public NativeList<int3> triangles;
public int addedEdgeCount;
int sampleCount => (int)(resolution * length * 0.03f);
float v;
float length;
public void Execute()
{
if (spline.Count < 2) return;
var edgePoints = new NativeList<EdgePoint>(Allocator.Temp);
if(smooth)
{
SmoothSample(ref edgePoints);
Extrude(in edgePoints);
}
else
{
UniformEvaluate(ref edgePoints);
Extrude(in edgePoints);
}
if (roundEnds && ((spline.Closed && (clipRange.x > math.EPSILON || clipRange.y < 1)) || !spline.Closed))
{
MakeRoundEdges(in edgePoints);
}
}
void SmoothSample(ref NativeList<EdgePoint> edgePoints)
{
var samples = new NativeList<EdgePoint>(Allocator.Temp);
// 전체 스플라인 샘플링
length = spline.GetLength();
for (int i = 0; i < spline.Count; i++)
{
var t = spline.CurveToSplineT(i);
var unitT = (GetWidthAt(t) / length) * 0.5f;
var knot = spline[i];
add_smooth_sample_point(in spline, samples, t, unitT, math.length(knot.TangentIn), math.length(knot.TangentOut));
}
var sampleUnitT = 1f / sampleCount;
for (int i = 0; i < spline.Count; i++)
{
var t = spline.CurveToSplineT(i);
var t_1 = spline.CurveToSplineT(i - 1);
var t1 = spline.CurveToSplineT(i + 1);
var leftT = math.lerp(t, t_1, 0.5f);
var rightT = math.lerp(t, t1, 0.5f);
var leftCount = (int)(math.abs(t - leftT) / sampleUnitT);
var rightCount = (int)(math.abs(rightT - t) / sampleUnitT);
if (spline.Closed)
{
if(i == 0)
{
t_1 = spline.CurveToSplineT(spline.Count - 1);
leftT = math.lerp(1, t_1, 0.5f);
leftCount = (int)(math.abs(1 - leftT) / sampleUnitT);
}
}
else
{
if (i == 0) leftCount = 0;
if (i == spline.Count - 1) rightCount = 0;
}
for (int j = 1; j < leftCount+1; j++)
{
var tt = t - sampleUnitT*j;
if (i == 0 && spline.Closed) tt += 1;
add_smooth_sample_point(in spline, samples, tt, sampleUnitT, 1, 1);
}
for (int j = 1; j < rightCount+1; j++)
{
var tt = t + sampleUnitT*j;
add_smooth_sample_point(in spline, samples, tt, sampleUnitT, 1, 1);
}
}
// SortByT(ref samples);
samples.Sort();
// 꼭짓점 스무딩
for (int i = 0; i < samples.Length; i++)
{
var sample = samples[i];
if(sample.angle < 10) continue;
// 열린 스플라인의 시작점과 끝점에선 스무딩을 안함.
if(!spline.Closed && (i == 0 || i == samples.Length - 1)) continue;
var preSampleIdx = previous_index(i, samples.Length);
var preSample = samples[preSampleIdx];
var nextSampleIdx = next_index(i, samples.Length);
var nextSample = samples[nextSampleIdx];
var mp0 = sample.pos;
var mp1 = math.lerp(preSample.pos, nextSample.pos, 0.5f);
var tanLength = (sample.tanInLength + sample.tanOutLength);
var curvature = tanLength < math.EPSILON ? 0 : tanLength / GetWidthAt(sample.t);
curvature = 1 - math.clamp(curvature, 0, 1);
var lerp = InternalUtility.Remap(sample.angle, 0, 180, 0, curvature);
var middlePos = math.lerp(mp0, mp1, lerp);
sample.pos = middlePos;
samples[i] = sample;
}
var knots = new NativeArray<BezierKnot>(samples.Length, Allocator.Temp);
for (int i = 0; i < samples.Length; i++)
{
var sample = samples[i];
var preSamplePos = samples[previous_index(i, samples.Length)].pos;
var nextSamplePos = samples[next_index(i, samples.Length)].pos;
var knot = SplineUtility.GetAutoSmoothKnot(sample.pos, preSamplePos, nextSamplePos, sample.up);
knots[i] = knot;
}
var nSpline = new CopiedNativeSpline(knots, spline.Closed, float4x4.identity);
for (int i = 0; i < nSpline.Count; i++)
{
var knot = nSpline[i];
var t = nSpline.CurveToSplineT(i);
// nSpline.Evaluate(t, out var pos, out var tan, out var up);
var tan = nSpline.EvaluateTangent(t);
var up = keepBillboard ? new float3(0,0,-1) : nSpline.EvaluateUpVector(t);
var sample = samples[i];
sample.t = t;
sample.pos = knot.Position;
sample.tan = tan;
sample.up = up;
samples[i] = sample;
}
var tempEdgePoints = new NativeList<EdgePoint>(Allocator.Temp);
// 각 세그먼트의 EdgePoint 계산
for (int i = 0; i < samples.Length; i++)
{
var isClosingSegment = i == samples.Length - 1 && spline.Closed;
var sample0 = samples[i];
var sample1 = isClosingSegment ? samples[0] : samples[i + 1];
// 열린 스플라인의 마지막 세그먼트에선 포인트를 추가하기만 함.
// (length - 1)은 마지막 ep라서 열린 스플라인에서 마지막 세그먼트가 아님
if (i == samples.Length - 2 && !spline.Closed)
{
tempEdgePoints.Add(sample0);
tempEdgePoints.Add(sample1);
break;
}
var inTan = sample0.tan;
var outTan = sample1.tan;
var angle = InternalUtility.Angle(inTan, outTan);
var edgePointCount = (int)math.round((angle / 30f) * 5);
// 실제 Extrude에 사용할 EdgePoints 추가
tempEdgePoints.Add(sample0);
for (int j = 0; j < edgePointCount; j++)
{
var t = (float)(j + 1) / (edgePointCount + 1);
t = t.Remap(0, 1, sample0.t, isClosingSegment ? 1 : sample1.t);
var ep = GetEdgePoint(nSpline, t);
tempEdgePoints.Add(ep);
}
if(i == samples.Length - 1 && spline.Closed) tempEdgePoints.Add(sample1);
}
var clipStartAdded = false;
var clipEndAdded = false;
var shouldClipping = clipRange.x > math.EPSILON || clipRange.y < 1;
for (int i = 0; i < tempEdgePoints.Length; i++)
{
var point = tempEdgePoints[i];
if (spline.Closed && shouldClipping && point.t == 0 && clipRange.y < 1)
{
continue;
}
if(shouldClipping && (point.t < clipRange.x || point.t > clipRange.y)) continue;
if (point.t == clipRange.x)
{
edgePoints.Add(point);
clipStartAdded = true;
continue;
}
if (point.t == clipRange.y)
{
edgePoints.Add(point);
clipEndAdded = true;
continue;
}
if (clipRange.x < point.t && point.t < clipRange.y)
{
if(!clipStartAdded)
{
var ep = GetEdgePoint(nSpline, clipRange.x);
edgePoints.Add(ep);
clipStartAdded = true;
}
else
{
edgePoints.Add(point);
}
}
}
if (!clipEndAdded)
{
var ep = GetEdgePoint(nSpline, clipRange.y);
edgePoints.Add(ep);
}
}
static void SortByT(ref NativeList<EdgePoint> samples)
{
for (int i = 0; i < samples.Length - 1; i++)
{
for (int j = i + 1; j < samples.Length; j++)
{
if (samples[i].t > samples[j].t)
{
// 두 객체의 위치를 교환
(samples[i], samples[j]) = (samples[j], samples[i]);
}
}
}
}
void add_smooth_sample_point(in NativeSpline s, NativeList<EdgePoint> samples, float t, float unitT, float tanInLength, float tanOutLength)
{
var preT = InternalUtility.Repeat(t - unitT, 1);
var preTTan = s.EvaluateTangent(preT);
var nextT = InternalUtility.Repeat(t + unitT, 1);
var nextTTan = s.EvaluateTangent(nextT);
var angle = InternalUtility.Angle(preTTan, nextTTan);
var ep = GetEdgePoint(s, t, angle, tanInLength, tanOutLength);
samples.Add(ep);
}
int previous_index(int i, int maxLength)
{
if (i == 0)
{
if (spline.Closed) return maxLength - 1;
return 0;
}
return i - 1;
}
int next_index(int i, int maxLength)
{
if (i == maxLength - 1)
{
if (spline.Closed) return 0;
return i;
}
return i + 1;
}
bool is_used(float t, float unitT, in NativeList<EdgePoint> list)
{
for (int i = 0; i < list.Length; i++)
{
var diff = t.CircularDistance(list[i].t, list.Length - 1);
if (diff < unitT) return true;
}
return false;
}
void UniformEvaluate(ref NativeList<EdgePoint> edgePoints)
{
length = spline.GetLength();
var clippedLength = length * (clipRange.y - clipRange.x);
var edgeCount = math.max((int)math.ceil(clippedLength * resolution * 0.05f), 1) + 2;
for (int i = 0; i < edgeCount; i++)
{
var t = (float)i / (edgeCount - 1);
t = t.Remap(0, 1, clipRange.x, clipRange.y);
var ep = GetEdgePoint(spline, t);
edgePoints.Add(ep);
}
}
EdgePoint GetEdgePoint(in NativeSpline s, float t, float angle = 0, float tanInLength = 0, float tanOutLength = 0)
{
if (keepBillboard)
{
var pos = s.EvaluatePosition(t);
var tan = s.EvaluateTangent(t);
return new EdgePoint(t, angle, pos, tan, new float3(0,0,-1), tanInLength, tanOutLength);
}
else
{
s.Evaluate(t, out var pos, out var tan, out var up);
return new EdgePoint(t, angle, pos, tan, up, tanInLength, tanOutLength);
}
}
EdgePoint GetEdgePoint(in CopiedNativeSpline s, float t)
{
if (keepBillboard)
{
var pos = s.EvaluatePosition(t);
var tan = s.EvaluateTangent(t);
return new EdgePoint(t, 0, pos, tan, new float3(0,0,-1));
}
else
{
s.Evaluate(t, out var pos, out var tan, out var up);
return new EdgePoint(t, 0, pos, tan, up);
}
}
void Extrude(in NativeList<EdgePoint> edgePoints)
{
for (int i = 0; i < edgePoints.Length; i++)
{
var ep = edgePoints[i];
var t = ep.t;
var pos = ep.pos;
var tan = ep.tan;
var up = ep.up;
// resolve (0,0,0) tangent
if (tan is { x: 0, y: 0 })
{
var prev = i == 0 ? pos : edgePoints[^2].pos;
var next = i == edgePoints.Length - 1 ? pos : edgePoints[i + 1].pos;
tan = next - prev;
}
InternalUtility.ExtrudeEdge(
GetWidthAt(t), GetVAt(t, i), GetColorAt(t), ref pos, tan, up,
keepBillboard, keepZeroZ, uvMultiplier, uvOffset, out var v0, out var v1);
AddVert(in v0, in v1);
if (i > 0)
{
AddQuadUsingLastVertices();
}
}
}
void AddVert(in UIVertex left, in UIVertex right)
{
vertices.Add(left);
vertices.Add(right);
}
void AddQuadUsingLastVertices()
{
var vi = vertices.Length;
triangles.Add(new int3
(
vi - 2,
vi - 3,
vi - 4
));
triangles.Add(new int3
(
vi - 2,
vi - 1,
vi - 3
));
}
float GetWidthAt(float t)
{
return width * widthCurve.Evaluate(t);
}
Color GetColorAt(float t)
{
return color * colorGradient.Evaluate(t);
}
float GetVAt(float t, int i)
{
switch (uvMode)
{
case UVMode.Tile:
return length / width * t;
case UVMode.RepeatPerSegment:
return i;
case UVMode.Stretch:
return t;
default:
throw new ArgumentOutOfRangeException();
}
}
void MakeRoundEdges(in NativeList<EdgePoint> edgePoints)
{
var sl = vertices[0];
var sr = vertices[1];
var el = vertices[^2];
var er = vertices[^1];
{
// at start
var start = edgePoints[0];
var t = start.t;
var pos = start.pos;
var tan = start.tan;
var up = start.up;
var V = GetVAt(t, 0);
var uv = new float2(0, V) * uvMultiplier - uvOffset;
var clr = GetColorAt(t);
MakeRoundEdge(in pos, sl.position, sr.position, false, up, uv, clr);
}
{
// at end
var end = edgePoints[^1];
var t = end.t;
var pos = end.pos;
var tan = end.tan;
var up = end.up;
var V = GetVAt(t, edgePoints.Length - 1);
var uv = new float2(0, V) * uvMultiplier - uvOffset;
var clr = GetColorAt(t);
MakeRoundEdge(in pos, el.position, er.position, true, up, uv, clr);
}
}
void MakeRoundEdge(in float3 center, in float3 left, in float3 right, bool invert,
in float3 up, in float2 uv, in Color clr)
{
var vertexCount = resolution + 7;
var marginAngle = 180f / vertexCount;
var radius = math.length(center - left);
var axis = keepBillboard ? math.back() : up;
var arm = invert ? left - center : right - center;
var centerVertex = new UIVertex();
centerVertex.position = center;
centerVertex.uv0 = new Vector4(0.5f, 0);
centerVertex.color = clr;
vertices.Add(centerVertex);
var startIndex = vertices.Length - 1;
var toRadians =
#if ENABLE_MATHMATICS__1_3_1_OR_NEWER
math.TORADIANS;
#else
Mathf.Deg2Rad;
#endif
for (int i = 0; i <= vertexCount; i++)
{
var vector = math.rotate(quaternion.AxisAngle(axis, marginAngle * i * toRadians), arm);
vector = math.normalizesafe(vector);
var ratio = (float)i / vertexCount;
var vert = new UIVertex
{
position = center + vector * radius,
uv0 = new Vector4(0, ratio),
color = clr
};
vertices.Add(vert);
if(i > 0) triangles.Add(new int3(startIndex, startIndex + i, startIndex + i + 1));
}
}
}
[BurstCompile]
public struct EdgePoint : IComparable<EdgePoint>
{
public float t;
public readonly float angle;
public float3 pos;
public float3 tan;
public float3 up;
public readonly float tanInLength;
public readonly float tanOutLength;
public bool smoothed;
public EdgePoint(float t, float angle, float3 pos, float3 tan, float3 up, float tanInLength = 0, float tanOutLength = 0)
{
this.t = t;
this.pos = pos;
this.tan = tan;
this.up = up;
this.angle = angle;
this.tanInLength = tanInLength;
this.tanOutLength = tanOutLength;
smoothed = false;
}
public int CompareTo(EdgePoint other)
{
return t.CompareTo(other.t);
}
}
}
#endif
#endif
#endif
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c38d6e1e178241fca1d16a519603f5f2
timeCreated: 1690397815