first commit

This commit is contained in:
lethanhsonvsp
2025-11-17 15:16:36 +07:00
commit a40d0921eb
17012 changed files with 2652386 additions and 0 deletions

View File

@@ -0,0 +1,159 @@
using System;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Splines;
using Unity.Mathematics;
using UnityEngine.Serialization;
using Interpolators = UnityEngine.Splines.Interpolators;
using Quaternion = UnityEngine.Quaternion;
namespace Unity.Splines.Examples
{
public class AnimateCarAlongSpline : MonoBehaviour
{
[SerializeField]
SplineContainer m_SplineContainer;
[Obsolete("Use Container instead.", false)]
public SplineContainer splineContainer => Container;
public SplineContainer Container => m_SplineContainer;
[SerializeField]
Car m_CarToAnimate;
[HideInInspector]
[Obsolete("No longer used.", false)]
public float m_DefaultSpeed;
[HideInInspector]
[Obsolete("No longer used.", false)]
public Vector3 m_DefaultTilt;
[HideInInspector]
[Obsolete("Use MaxSpeed instead.", false)]
public float m_MaxSpeed = 40f;
[FormerlySerializedAs("m_MaxSpeed")]
[Min(0f)]
public float MaxSpeed = 40f;
[SerializeField]
SplineData<float> m_Speed = new SplineData<float>();
[Obsolete("Use Speed instead.", false)]
public SplineData<float> speed => Speed;
public SplineData<float> Speed => m_Speed;
[SerializeField]
SplineData<float3> m_Tilt = new SplineData<float3>();
[Obsolete("Use Tilt instead.", false)]
public SplineData<float3> tilt => Tilt;
public SplineData<float3> Tilt => m_Tilt;
[SerializeField]
DriftSplineData m_Drift;
[Obsolete("Use DriftData instead.", false)]
public DriftSplineData driftData => DriftData;
public DriftSplineData DriftData
{
get
{
if (m_Drift == null)
m_Drift = GetComponent<DriftSplineData>();
return m_Drift;
}
}
float m_CurrentOffset;
float m_CurrentSpeed;
float m_SplineLength;
Spline m_Spline;
public void Initialize()
{
//Trying to initialize either the spline container or the car
if (m_SplineContainer == null && !TryGetComponent<SplineContainer>(out m_SplineContainer))
if (m_CarToAnimate == null)
TryGetComponent<Car>(out m_CarToAnimate);
}
void Start()
{
Assert.IsNotNull(m_SplineContainer);
m_Spline = m_SplineContainer.Spline;
m_SplineLength = m_Spline.GetLength();
m_CurrentOffset = 0f;
}
void OnValidate()
{
if (m_Speed != null)
{
for (int index = 0; index < m_Speed.Count; index++)
{
var data = m_Speed[index];
//We don't want to have a value that is negative or null as it might block the simulation
if (data.Value <= 0)
{
data.Value = Mathf.Max(0f, m_Speed.DefaultValue);
m_Speed[index] = data;
}
}
}
if (m_Tilt != null)
{
for (int index = 0; index < m_Tilt.Count; index++)
{
var data = m_Tilt[index];
//We don't want to have a up vector of magnitude 0
if (math.length(data.Value) == 0)
{
data.Value = m_Tilt.DefaultValue;
m_Tilt[index] = data;
}
}
}
if (DriftData != null)
DriftData.Container = Container;
}
void Update()
{
if (m_SplineContainer == null || m_CarToAnimate == null)
return;
m_CurrentOffset = (m_CurrentOffset + m_CurrentSpeed * Time.deltaTime / m_SplineLength) % 1f;
if (m_Speed.Count > 0)
m_CurrentSpeed = m_Speed.Evaluate(m_Spline, m_CurrentOffset, PathIndexUnit.Normalized, new Interpolators.LerpFloat());
else
m_CurrentSpeed = m_Speed.DefaultValue;
var posOnSplineLocal = SplineUtility.EvaluatePosition(m_Spline, m_CurrentOffset);
var direction = SplineUtility.EvaluateTangent(m_Spline, m_CurrentOffset);
var upSplineDirection = SplineUtility.EvaluateUpVector(m_Spline, m_CurrentOffset);
var right = math.normalize(math.cross(upSplineDirection, direction));
var driftOffset = 0f;
if (DriftData != null)
{
driftOffset = DriftData.Drift.Count == 0 ?
DriftData.Drift.DefaultValue :
DriftData.Drift.Evaluate(m_Spline, m_CurrentOffset, PathIndexUnit.Normalized, new Interpolators.LerpFloat());
}
m_CarToAnimate.transform.position = m_SplineContainer.transform.TransformPoint(posOnSplineLocal + driftOffset * right);
var up =
(m_Tilt.Count == 0) ?
m_Tilt.DefaultValue :
m_Tilt.Evaluate(m_Spline, m_CurrentOffset, PathIndexUnit.Normalized, new Interpolators.LerpFloat3());
var rot = Quaternion.LookRotation(direction, upSplineDirection);
m_CarToAnimate.transform.rotation = Quaternion.LookRotation(direction, rot * up);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9eac6ceedc2724f27893c14cb52f1fc7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using System;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace samples.Runtime
{
/// <summary>
/// Animate extruding a section of a spline.
/// </summary>
[RequireComponent(typeof(SplineExtrude))]
class AnimateSplineExtrude : MonoBehaviour
{
SplineExtrude m_Extrude;
[SerializeField, Range(.0001f, 2f)]
float m_Speed = .25f;
float m_Span;
[SerializeField]
bool m_RebuildExtrudeOnUpdate = true;
void Start()
{
m_Extrude = GetComponent<SplineExtrude>();
m_Span = (m_Extrude.Range.y - m_Extrude.Range.x) * .5f;
}
void Update()
{
bool allClosed = true;
foreach (var spline in m_Extrude.Splines)
if (!spline.Closed)
{
allClosed = false;
break;
}
float t = allClosed
? Time.time * m_Speed
: Mathf.Lerp(-m_Span, 1 + m_Span, math.frac(Time.time * m_Speed));
m_Extrude.Range = new float2(t - m_Span, t + m_Span);
if (m_RebuildExtrudeOnUpdate)
m_Extrude.Rebuild();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8632a8538dad4702a8e48b1a10c92ef4
timeCreated: 1637694540

View File

@@ -0,0 +1,38 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Splines;
using Unity.Mathematics;
namespace Unity.Splines.Examples
{
public class CameraPathExample : MonoBehaviour
{
public SplineContainer container;
[SerializeField]
float speed = 0.01f;
SplinePath cameraTrack;
void Start()
{
cameraTrack = new SplinePath(new[]
{
new SplineSlice<Spline>(container.Splines[0], new SplineRange(0, 6),
container.transform.localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[1], new SplineRange(0, 6),
container.transform.localToWorldMatrix)
});
}
void Update()
{
cameraTrack.Evaluate(math.frac(speed * Time.time), out var pos, out var right, out var up);
Vector3 forward = Vector3.Cross(right, up);
transform.position = pos;
transform.LookAt((Vector3) pos + forward);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e7d0d2e4d7f0c0a40ab38f9ba272a7e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Unity.Splines.Examples
{
public class Car : MonoBehaviour {}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e3af2c81f946c984ead177f24f06763b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,35 @@
using System;
using UnityEngine;
using UnityEngine.Splines;
namespace Unity.Splines.Examples
{
public class DriftSplineData : MonoBehaviour
{
[HideInInspector]
[Obsolete("No longer used.", false)]
public float m_Default;
[SerializeField]
SplineData<float> m_Drift = new SplineData<float>();
[Obsolete("Use Drift instead.", false)]
public SplineData<float> drift => Drift;
public SplineData<float> Drift => m_Drift;
SplineContainer m_SplineContainer;
[Obsolete("Use Container instead.", false)]
public SplineContainer container => Container;
public SplineContainer Container
{
get
{
if (m_SplineContainer == null)
m_SplineContainer = GetComponent<SplineContainer>();
return m_SplineContainer;
}
set => m_SplineContainer = value;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cd08d575c1c88a646835fb7c516cfa72
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Splines;
namespace Unity.Splines.Examples
{
public class HighwayExample : MonoBehaviour
{
public SplineContainer container;
[SerializeField]
float speed = 0.1f;
SplinePath[] paths = new SplinePath[4];
float t = 0f;
IEnumerator CarPathCoroutine()
{
for(int n = 0; ; ++n)
{
t = 0f;
var path = paths[n % 4];
while (t <= 1f)
{
var pos = path.EvaluatePosition(t);
var direction = path.EvaluateTangent(t);
transform.position = pos;
transform.LookAt(pos + direction);
t += speed * Time.deltaTime;
yield return null;
}
}
}
void Start()
{
var localToWorldMatrix = container.transform.localToWorldMatrix;
paths[0] = new SplinePath(new[]
{
new SplineSlice<Spline>(container.Splines[0], new SplineRange(0, 3), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[3], new SplineRange(0, 3), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[1], new SplineRange(1, 2), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[2], new SplineRange(2, 3), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[0], new SplineRange(3, 3), localToWorldMatrix)
});
paths[1] = new SplinePath(new[]
{
new SplineSlice<Spline>(container.Splines[0], new SplineRange(0, 2), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[2], new SplineRange(0, 5), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[0], new SplineRange(3, 3), localToWorldMatrix)
});
paths[2] = new SplinePath(new[]
{
new SplineSlice<Spline>(container.Splines[0], new SplineRange(0, 2), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[2], new SplineRange(0, 3), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[1], new SplineRange(2, -3), localToWorldMatrix),
});
paths[3] = new SplinePath(new[]
{
new SplineSlice<Spline>(container.Splines[0], new SplineRange(0, 3), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[3], new SplineRange(0, 3), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[1], new SplineRange(1, 2), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[2], new SplineRange(2, -3), localToWorldMatrix),
new SplineSlice<Spline>(container.Splines[0], new SplineRange(1, -2), localToWorldMatrix),
});
StartCoroutine(CarPathCoroutine());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37f15b3d02f03eb49af32bd9e33df72e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,112 @@
using System;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Splines;
#endif
// An example showing how to use embedded SplineData on the Spline class. Additionally showcases how to respond to
// changes in the Inspector.
namespace UnityEngine.Splines.Examples
{
#if UNITY_EDITOR
[CustomEditor(typeof(ItemPickup))]
class ItemPickupEditor : Editor
{
void OnEnable()
{
EditorSplineUtility.AfterSplineWasModified += OnAfterSplineModified;
EditorSplineUtility.RegisterSplineDataChanged<Object>(OnAfterSplineDataWasModified);
Undo.undoRedoPerformed += InstantiatePrefabs;
}
void OnDisable()
{
EditorSplineUtility.AfterSplineWasModified -= OnAfterSplineModified;
EditorSplineUtility.UnregisterSplineDataChanged<Object>(OnAfterSplineDataWasModified);
Undo.undoRedoPerformed -= InstantiatePrefabs;
}
void OnAfterSplineModified(Spline _) => InstantiatePrefabs();
void InstantiatePrefabs()
{
if (target is ItemPickup pickup)
pickup.Instantiate();
}
void OnAfterSplineDataWasModified(SplineData<Object> splineData)
{
InstantiatePrefabs();
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
base.OnInspectorGUI();
if (EditorGUI.EndChangeCheck())
InstantiatePrefabs();
}
}
#endif
[RequireComponent(typeof(SplineContainer))]
public class ItemPickup : MonoBehaviour
{
public const string PickupDataKey = "pickups";
// EmbeddedSplineData is a convenience class that wraps all the fields necessary to access SplineData embedded
// in a Spline class and provides a custom property drawer.
// If `Container` and `SplineIndex` are omitted, it is assumed that the referenced Spline is on the first
// SplineContainer component found on the same GameObject, at index 0.
// I.e., gameObject.Component<SplineContainer>().Splines[0].
// In this example we specify that only the key should be shown in the Inspector.
[SerializeField, EmbeddedSplineDataFields(EmbeddedSplineDataField.Key | EmbeddedSplineDataField.SplineIndex)]
EmbeddedSplineData m_Pickups = new EmbeddedSplineData()
{
Key = PickupDataKey,
Type = EmbeddedSplineDataType.Object
};
public void Instantiate()
{
m_Pickups.Container = GetComponent<SplineContainer>();
for (int i = transform.childCount - 1; i > -1; --i)
DestroyImmediate(transform.GetChild(i).gameObject);
if (!m_Pickups.TryGetSpline(out var spline))
return;
var parent = transform;
// SplineData embedded in a Spline directly is accessed through a key value pair interface. You could also use TryGetObjectData
// if you do not want to create a new SplineData entry. This function
// will create a new SplineData object if one does not exist already.
if (!m_Pickups.TryGetObjectData(out var prefabs))
return;
foreach (var key in prefabs)
{
if (key.Value == null)
continue;
float t = spline.ConvertIndexUnit(key.Index, prefabs.PathIndexUnit, PathIndexUnit.Normalized);
m_Pickups.Container.Evaluate(m_Pickups.SplineIndex, t, out var position, out var tangent, out var normal);
var rotation = Vector3.SqrMagnitude(tangent) < float.Epsilon || Vector3.SqrMagnitude(normal) < float.Epsilon
? Quaternion.identity
: Quaternion.LookRotation(tangent, normal);
#if UNITY_EDITOR
if (PrefabUtility.InstantiatePrefab(key.Value, parent) is GameObject instance)
#else
if (Instantiate(key.Value, parent) is GameObject instance)
#endif
instance.transform.SetPositionAndRotation(position, rotation);
}
#if UNITY_EDITOR
Selection.activeObject = gameObject;
#endif
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d5e021ffcfaf47d28c0e5b0e01334c09
timeCreated: 1673290694

View File

@@ -0,0 +1,341 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Splines;
#endif
using System;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
using Interpolators = UnityEngine.Splines.Interpolators;
namespace Unity.Splines.Examples
{
[ExecuteInEditMode]
[DisallowMultipleComponent]
[RequireComponent(typeof(SplineContainer), typeof(MeshRenderer), typeof(MeshFilter))]
public class LoftRoadBehaviour : MonoBehaviour
{
[SerializeField]
List<SplineData<float>> m_Widths = new List<SplineData<float>>();
public List<SplineData<float>> Widths
{
get
{
foreach (var width in m_Widths)
{
if (width.DefaultValue == 0)
width.DefaultValue = 1f;
}
return m_Widths;
}
}
[SerializeField]
SplineContainer m_Spline;
public SplineContainer Container
{
get
{
if (m_Spline == null)
m_Spline = GetComponent<SplineContainer>();
return m_Spline;
}
set => m_Spline = value;
}
[SerializeField]
int m_SegmentsPerMeter = 1;
[SerializeField]
Mesh m_Mesh;
[SerializeField]
float m_TextureScale = 1f;
public IReadOnlyList<Spline> splines => LoftSplines;
public IReadOnlyList<Spline> LoftSplines
{
get
{
if (m_Spline == null)
m_Spline = GetComponent<SplineContainer>();
if (m_Spline == null)
{
Debug.LogError("Cannot loft road mesh because Spline reference is null");
return null;
}
return m_Spline.Splines;
}
}
[Obsolete("Use LoftMesh instead.", false)]
public Mesh mesh => LoftMesh;
public Mesh LoftMesh
{
get
{
if (m_Mesh != null)
return m_Mesh;
m_Mesh = new Mesh();
GetComponent<MeshRenderer>().sharedMaterial = Resources.Load<Material>("Road");
return m_Mesh;
}
}
[Obsolete("Use SegmentsPerMeter instead.", false)]
public int segmentsPerMeter => SegmentsPerMeter;
public int SegmentsPerMeter => Mathf.Min(10, Mathf.Max(1, m_SegmentsPerMeter));
List<Vector3> m_Positions = new List<Vector3>();
List<Vector3> m_Normals = new List<Vector3>();
List<Vector2> m_Textures = new List<Vector2>();
List<int> m_Indices = new List<int>();
bool m_LoftRoadsRequested = false;
void Update()
{
if (m_LoftRoadsRequested)
{
LoftAllRoads();
m_LoftRoadsRequested = false;
}
}
public void OnEnable()
{
// Avoid to point to an existing instance when duplicating the GameObject
if (m_Mesh != null)
m_Mesh = null;
if (m_Spline == null)
m_Spline = GetComponent<SplineContainer>();
LoftAllRoads();
#if UNITY_EDITOR
EditorSplineUtility.AfterSplineWasModified += OnAfterSplineWasModified;
EditorSplineUtility.RegisterSplineDataChanged<float>(OnAfterSplineDataWasModified);
Undo.undoRedoPerformed += LoftAllRoads;
#endif
SplineContainer.SplineAdded += OnSplineContainerAdded;
SplineContainer.SplineRemoved += OnSplineContainerRemoved;
SplineContainer.SplineReordered += OnSplineContainerReordered;
Spline.Changed += OnSplineChanged;
}
public void OnDisable()
{
#if UNITY_EDITOR
EditorSplineUtility.AfterSplineWasModified -= OnAfterSplineWasModified;
EditorSplineUtility.UnregisterSplineDataChanged<float>(OnAfterSplineDataWasModified);
Undo.undoRedoPerformed -= LoftAllRoads;
#endif
if (m_Mesh != null)
#if UNITY_EDITOR
DestroyImmediate(m_Mesh);
#else
Destroy(m_Mesh);
#endif
SplineContainer.SplineAdded -= OnSplineContainerAdded;
SplineContainer.SplineRemoved -= OnSplineContainerRemoved;
SplineContainer.SplineReordered -= OnSplineContainerReordered;
Spline.Changed -= OnSplineChanged;
}
void OnSplineContainerAdded(SplineContainer container, int index)
{
if (container != m_Spline)
return;
if (m_Widths.Count < LoftSplines.Count)
{
var delta = LoftSplines.Count - m_Widths.Count;
for (var i = 0; i < delta; i++)
{
#if UNITY_EDITOR
Undo.RecordObject(this, "Modifying Widths SplineData");
#endif
m_Widths.Add(new SplineData<float>() { DefaultValue = 1f });
}
}
LoftAllRoads();
}
void OnSplineContainerRemoved(SplineContainer container, int index)
{
if (container != m_Spline)
return;
if (index < m_Widths.Count)
{
#if UNITY_EDITOR
Undo.RecordObject(this, "Modifying Widths SplineData");
#endif
m_Widths.RemoveAt(index);
}
LoftAllRoads();
}
void OnSplineContainerReordered(SplineContainer container, int previousIndex, int newIndex)
{
if (container != m_Spline)
return;
LoftAllRoads();
}
void OnAfterSplineWasModified(Spline s)
{
if (LoftSplines == null)
return;
foreach (var spline in LoftSplines)
{
if (s == spline)
{
m_LoftRoadsRequested = true;
break;
}
}
}
void OnSplineChanged(Spline spline, int knotIndex, SplineModification modification)
{
OnAfterSplineWasModified(spline);
}
void OnAfterSplineDataWasModified(SplineData<float> splineData)
{
foreach (var width in m_Widths)
{
if (splineData == width)
{
m_LoftRoadsRequested = true;
break;
}
}
}
public void LoftAllRoads()
{
LoftMesh.Clear();
m_Positions.Clear();
m_Normals.Clear();
m_Textures.Clear();
m_Indices.Clear();
m_Positions.Capacity = 0;
m_Normals.Capacity = 0;
m_Textures.Capacity = 0;
m_Indices.Capacity = 0;
for (var i = 0; i < LoftSplines.Count; i++)
Loft(LoftSplines[i], i);
LoftMesh.SetVertices(m_Positions);
LoftMesh.SetNormals(m_Normals);
LoftMesh.SetUVs(0, m_Textures);
LoftMesh.subMeshCount = 1;
LoftMesh.SetIndices(m_Indices, MeshTopology.Triangles, 0);
LoftMesh.UploadMeshData(false);
GetComponent<MeshFilter>().sharedMesh = m_Mesh;
}
public void Loft(Spline spline, int widthDataIndex)
{
if (spline == null || spline.Count < 2)
return;
LoftMesh.Clear();
float length = spline.GetLength();
if (length <= 0.001f)
return;
var segmentsPerLength = SegmentsPerMeter * length;
var segments = Mathf.CeilToInt(segmentsPerLength);
var segmentStepT = (1f / SegmentsPerMeter) / length;
var steps = segments + 1;
var vertexCount = steps * 2;
var triangleCount = segments * 6;
var prevVertexCount = m_Positions.Count;
m_Positions.Capacity += vertexCount;
m_Normals.Capacity += vertexCount;
m_Textures.Capacity += vertexCount;
m_Indices.Capacity += triangleCount;
var t = 0f;
for (int i = 0; i < steps; i++)
{
SplineUtility.Evaluate(spline, t, out var pos, out var dir, out var up);
// If dir evaluates to zero (linear or broken zero length tangents?)
// then attempt to advance forward by a small amount and build direction to that point
if (math.length(dir) == 0)
{
var nextPos = spline.GetPointAtLinearDistance(t, 0.01f, out _);
dir = math.normalizesafe(nextPos - pos);
if (math.length(dir) == 0)
{
nextPos = spline.GetPointAtLinearDistance(t, -0.01f, out _);
dir = -math.normalizesafe(nextPos - pos);
}
if (math.length(dir) == 0)
dir = new float3(0, 0, 1);
}
var scale = transform.lossyScale;
var tangent = math.normalizesafe(math.cross(up, dir)) * new float3(1f / scale.x, 1f / scale.y, 1f / scale.z);
var w = 1f;
if (widthDataIndex < m_Widths.Count)
{
w = m_Widths[widthDataIndex].DefaultValue;
if (m_Widths[widthDataIndex] != null && m_Widths[widthDataIndex].Count > 0)
{
w = m_Widths[widthDataIndex].Evaluate(spline, t, PathIndexUnit.Normalized, new Interpolators.LerpFloat());
w = math.clamp(w, .001f, 10000f);
}
}
m_Positions.Add(pos - (tangent * w));
m_Positions.Add(pos + (tangent * w));
m_Normals.Add(up);
m_Normals.Add(up);
m_Textures.Add(new Vector2(0f, t * m_TextureScale));
m_Textures.Add(new Vector2(1f, t * m_TextureScale));
t = math.min(1f, t + segmentStepT);
}
for (int i = 0, n = prevVertexCount; i < triangleCount; i += 6, n += 2)
{
m_Indices.Add((n + 2) % (prevVertexCount + vertexCount));
m_Indices.Add((n + 1) % (prevVertexCount + vertexCount));
m_Indices.Add((n + 0) % (prevVertexCount + vertexCount));
m_Indices.Add((n + 2) % (prevVertexCount + vertexCount));
m_Indices.Add((n + 3) % (prevVertexCount + vertexCount));
m_Indices.Add((n + 1) % (prevVertexCount + vertexCount));
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d3f00a79918dc4a18a050534219a9842
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Splines;
namespace Unity.Splines.Examples
{
public class NPCSplinePathExample : MonoBehaviour
{
public GameObject npc;
public SplineContainer container1;
public SplineContainer container2;
public Vector3 offsetFromPath;
SplinePath path;
float t = 0f;
void Start()
{
var container1Transform = container1.transform.localToWorldMatrix;
var container2Transform = container2.transform.localToWorldMatrix;
// Create a SplinePath from a subset of Splines
path = new SplinePath(new[]
{
new SplineSlice<Spline>(container1.Splines[1], new SplineRange(0, 4), container1Transform),
new SplineSlice<Spline>(container1.Splines[2], new SplineRange(0, 4), container1Transform),
new SplineSlice<Spline>(container1.Splines[0], new SplineRange(4, -5), container1Transform),
new SplineSlice<Spline>(container1.Splines[1], new SplineRange(0, 2), container1Transform),
new SplineSlice<Spline>(container1.Splines[3], new SplineRange(0, 2), container1Transform),
new SplineSlice<Spline>(container2.Splines[0], new SplineRange(3, -4), container2Transform)
});
}
void Update()
{
Vector3 pos = path.EvaluatePosition(t);
npc.transform.position = pos + offsetFromPath;
t += 0.05f * Time.deltaTime;
if (t > 1f) t = 0f;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b80039d79eca67143b32a5c83ed395fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,129 @@
using System.Collections.Generic;
using System.Linq;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
using UnityEngine.UIElements;
namespace Unity.Splines.Examples
{
/// <summary>
/// This sample demonstrates how to create a spline from a collection of points drawn by the cursor.
/// </summary>
public class Paint : MonoBehaviour
{
// The minimum amount of cursor movement to be considered a new sample.
const float StrokeDeltaThreshold = .1f;
const int LeftMouseButton = 0;
[SerializeField]
Mesh m_SampleDot;
[SerializeField]
Material m_SampleMat, m_ControlPointMat;
// Point reduction epsilon determines how aggressive the point reduction algorithm is when removing redundant
// points. Lower values result in more accurate spline representations of the original line, at the cost of
// greater number knots.
[Range(0f, 1f), SerializeField]
float m_PointReductionEpsilon = .15f;
// Tension affects how "curvy" splines are at knots. 0 is a sharp corner, 1 is maximum curvitude.
[Range(0f, 1f), SerializeField]
float m_SplineTension = 1 / 4f;
Label m_Stats;
Camera m_Camera;
List<float3> m_Stroke = new List<float3>(1024);
List<float3> m_Reduced = new List<float3>(512);
bool m_Painting;
Vector3 m_LastMousePosition;
void Start()
{
m_Camera = Camera.main;
m_Stats = PaintUI.root.Q<Label>("Stats");
m_Stats.text = "";
var epsilonSlider = PaintUI.root.Q<Slider>("PointReductionEpsilonSlider");
epsilonSlider.RegisterValueChangedCallback(PointReductionEpsilonChanged);
epsilonSlider.value = m_PointReductionEpsilon;
var tensionSlider = PaintUI.root.Q<Slider>("SplineTensionSlider");
tensionSlider.RegisterValueChangedCallback(SplineTensionChanged);
tensionSlider.value = m_SplineTension;
}
void SplineTensionChanged(ChangeEvent<float> evt)
{
m_SplineTension = evt.newValue;
RebuildSpline();
}
void PointReductionEpsilonChanged(ChangeEvent<float> evt)
{
m_PointReductionEpsilon = evt.newValue;
RebuildSpline();
}
void RebuildSpline()
{
// Before setting spline knots, reduce the number of sample points.
SplineUtility.ReducePoints(m_Stroke, m_Reduced, m_PointReductionEpsilon);
var spline = GetComponent<SplineContainer>().Spline;
// Assign the reduced sample positions to the Spline knots collection. Here we are constructing new
// BezierKnots from a single position, disregarding tangent and rotation. The tangent and rotation will be
// calculated automatically in the next step wherein the tangent mode is set to "Auto Smooth."
spline.Knots = m_Reduced.Select(x => new BezierKnot(x));
var all = new SplineRange(0, spline.Count);
// Sets the tangent mode for all knots in the spline to "Auto Smooth."
spline.SetTangentMode(all, TangentMode.AutoSmooth);
// Sets the tension parameter for all knots. Note that the "Tension" parameter is only applicable to
// "Auto Smooth" mode knots.
spline.SetAutoSmoothTension(all, m_SplineTension);
m_Stats.text = $"Input Sample Count: {m_Stroke.Count}\nSpline Knot Count: {m_Reduced.Count}";
}
void AddSample(Vector2 p)
{
Vector3 wp = m_LastMousePosition = p;
wp.z = 10f;
m_Stroke.Add(m_Camera.ScreenToWorldPoint(wp));
}
void Update()
{
if (!PaintUI.PointerOverUI && Input.GetMouseButtonDown(LeftMouseButton))
{
m_Painting = true;
m_Stroke.Clear();
AddSample(Input.mousePosition);
}
if (Input.GetMouseButtonUp(LeftMouseButton))
{
m_Painting = false;
RebuildSpline();
}
if (m_Painting && Vector2.Distance(Input.mousePosition, m_LastMousePosition) > StrokeDeltaThreshold)
AddSample(Input.mousePosition);
foreach (var sample in m_Stroke)
Graphics.DrawMesh(m_SampleDot, Matrix4x4.TRS(sample, Quaternion.identity, new Vector3(.2f, .2f, .2f)),
m_SampleMat, 0);
foreach (var point in m_Reduced)
Graphics.DrawMesh(m_SampleDot,
Matrix4x4.TRS((Vector3)point + new Vector3(0f, 0f, -1f), Quaternion.identity,
new Vector3(.3f, .3f, .3f)), m_ControlPointMat, 0);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3afd7ccbd91bf490f947bea286824493
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using UnityEngine;
using UnityEngine.UIElements;
namespace Unity.Splines.Examples
{
/// <summary>
/// UI implementation for Paint Splines example.
/// </summary>
public class PaintUI : MonoBehaviour
{
static bool s_PointerOverUI;
public static bool PointerOverUI => s_PointerOverUI;
static UIDocument m_Document;
public static VisualElement root { get; private set; }
void Awake()
{
m_Document = GetComponent<UIDocument>();
root = m_Document.rootVisualElement;
ConnectVisualElements();
}
void ConnectVisualElements()
{
root.RegisterCallback<PointerEnterEvent>(OnPointerEnter);
root.RegisterCallback<PointerLeaveEvent>(OnPointerExit);
var pointReduceEpsilonSlider = root.Q<Slider>("PointReductionEpsilonSlider");
var pointReduceEpsilonLabel = root.Q<Label>("PointReductionEpsilonLabel");
pointReduceEpsilonSlider.RegisterValueChangedCallback(evt =>
pointReduceEpsilonLabel.text = evt.newValue.ToString());
var tensionSlider = root.Q<Slider>("SplineTensionSlider");
var tensionLabel = root.Q<Label>("SplineTensionLabel");
tensionSlider.RegisterValueChangedCallback(evt => tensionLabel.text = evt.newValue.ToString());
}
void OnPointerEnter(PointerEnterEvent evt) => s_PointerOverUI = true;
void OnPointerExit(PointerLeaveEvent evt) => s_PointerOverUI = false;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c99dd2a00aa504839914c7de85614714
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
using System;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
[DisallowMultipleComponent]
public class SelectSplinePath : MonoBehaviour
{
SplineContainer m_Container;
SplinePath<Spline> m_Amalgamate;
const int k_PreviewCurveResolution = 42;
LineRenderer m_LineRenderer;
Vector3[] m_CurvePoints;
void Start()
{
m_Container = GetComponent<SplineContainer>();
m_Amalgamate = new SplinePath<Spline>(m_Container.Splines);
m_LineRenderer = GetComponent<LineRenderer>();
m_LineRenderer.positionCount = k_PreviewCurveResolution;
m_CurvePoints = new Vector3[k_PreviewCurveResolution];
}
void Update()
{
using var native = new NativeSpline(m_Amalgamate, m_Container.transform.localToWorldMatrix);
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float distance = float.PositiveInfinity;
var nearest = new BezierCurve();
for (int i = 0; i < native.Count; ++i)
{
if(native.GetCurveLength(i) < float.Epsilon)
continue;
var curve = native.GetCurve(i);
var dist = CurveUtility.GetNearestPoint(curve, ray, out var p, out _);
if (dist < distance)
{
nearest = curve;
distance = dist;
}
}
for (int i = 0, c = m_CurvePoints.Length; i < c; ++i)
m_CurvePoints[i] = CurveUtility.EvaluatePosition(nearest, i / (c - 1f)) + new float3(0f, .1f, 0f);
m_LineRenderer.SetPositions(m_CurvePoints);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 60813d7e4b42848d1b09f81e83bb3370
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
using System;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace Unity.Splines.Examples
{
// Visualize the nearest point on a spline to a roving sphere.
[RequireComponent(typeof(LineRenderer))]
public class ShowNearestPoint : MonoBehaviour
{
// Boundary setup for the wandering sphere
Vector3 m_Center = Vector3.zero;
float m_Size = 50f;
// Store a collection of Splines to test for nearest to our position.
SplineContainer[] m_SplineContainer;
LineRenderer m_LineRenderer;
// This GameObject will be used to visualize the nearest point on the nearest spline to this transform.
[SerializeField]
Transform m_NearestPoint;
void Start()
{
if (!TryGetComponent(out m_LineRenderer))
Debug.LogError("ShowNearestPoint requires a LineRenderer.");
m_LineRenderer.positionCount = 2;
#if UNITY_2023_1_OR_NEWER
m_SplineContainer = FindObjectsByType<SplineContainer>(FindObjectsSortMode.None);
#else
m_SplineContainer = FindObjectsOfType<SplineContainer>();
#endif
if (m_NearestPoint == null)
Debug.LogError("Nearest Point GameObject is null");
}
void Update()
{
var position = CalculatePosition();
var nearest = new float4(0, 0, 0, float.PositiveInfinity);
foreach (var container in m_SplineContainer)
{
using var native = new NativeSpline(container.Spline, container.transform.localToWorldMatrix);
float d = SplineUtility.GetNearestPoint(native, transform.position, out float3 p, out float t);
if (d < nearest.w)
nearest = new float4(p, d);
}
m_LineRenderer.SetPosition(0, position);
m_LineRenderer.SetPosition(1, nearest.xyz);
m_NearestPoint.position = nearest.xyz;
transform.position = position;
}
Vector3 CalculatePosition()
{
float time = Time.time * .2f, time1 = time + 1;
float half = m_Size * .5f;
return m_Center + new Vector3(
Mathf.PerlinNoise(time, time) * m_Size - half,
0,
Mathf.PerlinNoise(time1, time1) * m_Size - half
);
}
void OnDrawGizmosSelected()
{
Gizmos.DrawWireCube(m_Center, new Vector3(m_Size, .1f, m_Size));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d0206caa731d581c98dd8df314b4614
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
using System.Collections.Generic;
using System.Linq;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
/// <summary>
/// Collection of commonly used functions in the Spline package.
/// </summary>
class SplineExamples : MonoBehaviour
{
void Example()
{
// Splines exist in a scene as properties in a SplineContainer. The relationship of splines to SplineContainer
// is similar to the relationship between Mesh and MeshFilter.
var container = GetComponent<SplineContainer>();
// SplineContainer can hold many splines. Access those splines through the Splines property.
IReadOnlyList<Spline> splines = container.Splines;
// Get the position along a spline at a ratio from 0 to 1. 0 is the beginning of the spline and 1 is the end of the spline.
// Call the SplineContainer version of EvaluatePosition to get results in world space.
float3 worldPosition = container.EvaluatePosition(.5f);
// Get the position, tangent, and direction of a spline at a location along a spline, and then rotate a GameObject to match the position and rotation of the spline.
container.Evaluate(.3f, out var position, out var tangent, out var normal);
transform.position = position;
transform.rotation = Quaternion.LookRotation(tangent);
// Knot connections are stored in the KnotLinkConnection type.
var links = container.KnotLinkCollection;
// Knots are referenced by an index to the SplineContainer.Splines array and Knot Index.
// This example queries whether any knots are linked to the fourth knot of the first spline.
var knotIndex = new SplineKnotIndex(0, 3);
if (links.TryGetKnotLinks(knotIndex, out var linked))
Debug.Log($"found {linked.Count} connected knots!");
// SplineSlice represents a partial or complete range of curves from another spline. Slices can iterate either forwards or backwards.
// A slice is a value type and does not make copies of the referenced spline. A slice is not resource intensive to create.
// Create a new spline from the first curve of another spline.
var slice = new SplineSlice<Spline>(splines.First(), new SplineRange(0, 2));
// Create a SplinePath to evaluate multiple slices of many splines as a single path.
var path = new SplinePath(new SplineSlice<Spline>[]
{
slice,
// This range starts at the fourth knot and iterates backwards by three indices.
new SplineSlice<Spline>(splines[1], new SplineRange(3, -3))
});
// SplinePath implements ISpline, which you can evaluate with any of the usual SplineUtility methods.
var _ = path.EvaluatePosition(.42f);
// If performance is a concern, use NativeSpline. NativeSpline is a NativeArray backed representation of
// any ISpline type. NativeSpline is very efficient to query because all transformations are baked at construction.
// Unlike Spline, NativeSpline is not mutable.
using var native = new NativeSpline(path, transform.localToWorldMatrix);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8dce1d0ff8c24d538d3c3e4f5a5eab5c
timeCreated: 1652903748

View File

@@ -0,0 +1,35 @@
using System.Linq;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace Unity.Splines.Examples.Editor
{
[RequireComponent(typeof(SplineContainer))]
class SplineOscillator : MonoBehaviour
{
Spline m_Spline;
BezierKnot[] m_Origins;
[SerializeField, Range(.1f, 10f)]
float m_Speed = 3f;
[SerializeField, Range(1f, 10f)]
float m_Frequency = 3.14f;
void Start()
{
m_Spline = GetComponent<SplineContainer>().Spline;
m_Origins = m_Spline.Knots.ToArray();
}
void Update()
{
for (int i = 0, c = m_Spline.Count; i < c; ++i)
{
var offset = i / (c - 1f) * m_Frequency;
m_Spline[i] = m_Origins[i] + math.cos((Time.time + offset) * m_Speed) * new float3(0, 1, 0);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fcd72b7e265696c4a96291318699084
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,109 @@
using System;
using System.Linq;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace Unity.Splines.Examples
{
[Serializable]
public struct SplineLineRendererSettings
{
public float width;
public Material material;
[Range(16, 512)]
public int subdivisions;
public Color startColor, endColor;
}
[RequireComponent(typeof(SplineContainer))]
public class SplineRenderer : MonoBehaviour
{
SplineContainer m_SplineContainer;
Spline[] m_Splines;
bool m_Dirty;
Vector3[] m_Points;
[SerializeField]
SplineLineRendererSettings m_LineRendererSettings = new SplineLineRendererSettings() {
width = .5f,
subdivisions = 64
};
LineRenderer[] m_Lines;
void Awake()
{
m_SplineContainer = GetComponent<SplineContainer>();
m_Splines = m_SplineContainer.Splines.ToArray();
}
void OnEnable()
{
Spline.Changed += OnSplineChanged;
}
void OnDisable()
{
Spline.Changed -= OnSplineChanged;
}
void OnSplineChanged(Spline spline, int knotIndex, SplineModification modificationType)
{
for (int i = 0, c = m_Splines.Length; !m_Dirty && i < c; ++i)
if (m_Splines[i] == spline)
m_Dirty = true;
}
void Update()
{
if (m_Lines?.Length != m_Splines.Length)
{
if (m_Lines != null)
foreach (var line in m_Lines) DestroyImmediate(line.gameObject);
m_Lines = new LineRenderer[m_Splines.Length];
for (int i = 0, c = m_Splines.Length; i < c; ++i)
{
m_Lines[i] = new GameObject().AddComponent<LineRenderer>();
m_Lines[i].gameObject.name = $"SplineRenderer {i}";
m_Lines[i].transform.SetParent(transform, true);
}
m_Dirty = true;
}
// It's nice to be able to see resolution changes at runtime
if (m_Points?.Length != m_LineRendererSettings.subdivisions)
{
m_Dirty = true;
m_Points = new Vector3[m_LineRendererSettings.subdivisions];
foreach (var line in m_Lines)
line.positionCount = m_LineRendererSettings.subdivisions;
}
if (!m_Dirty)
return;
m_Dirty = false;
var trs = m_SplineContainer.transform.localToWorldMatrix;
for (int s = 0, c = m_Splines.Length; s < c; ++s)
{
if (m_Splines[s].Count < 1)
continue;
for (int i = 0; i < m_LineRendererSettings.subdivisions; i++)
m_Points[i] = math.transform(trs, m_Splines[s].EvaluatePosition(i / (m_LineRendererSettings.subdivisions - 1f)));
m_Lines[s].widthCurve = new AnimationCurve(new Keyframe(0f, m_LineRendererSettings.width));
m_Lines[s].startColor = m_LineRendererSettings.startColor;
m_Lines[s].endColor = m_LineRendererSettings.endColor;
m_Lines[s].material = m_LineRendererSettings.material;
m_Lines[s].useWorldSpace = true;
m_Lines[s].SetPositions(m_Points);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d9dd4cf612254c08bf888d5bca6bb2d5
timeCreated: 1628867781

View File

@@ -0,0 +1,104 @@
using System;
using UnityEngine;
using UnityEngine.Splines;
namespace Unity.Splines.Examples
{
/// <summary>
/// A simple example showing how to pass Spline data to the GPU using SplineComputeBufferScope.
/// </summary>
[RequireComponent(typeof(LineRenderer), typeof(SplineContainer))]
public class SplineRendererCompute : MonoBehaviour
{
// Use with Shader/InterpolateSpline.compute
[SerializeField]
ComputeShader m_ComputeShader;
[SerializeField, Range(16, 512)]
int m_Segments = 128;
Spline m_Spline;
LineRenderer m_Line;
bool m_Dirty;
SplineComputeBufferScope<Spline> m_SplineBuffers;
Vector3[] m_Positions;
ComputeBuffer m_PositionsBuffer;
int m_GetPositionsKernel;
void Awake()
{
m_Spline = GetComponent<SplineContainer>().Spline;
// Set up the LineRenderer
m_Line = GetComponent<LineRenderer>();
m_Line.positionCount = m_Segments;
m_GetPositionsKernel = m_ComputeShader.FindKernel("GetPositions");
// Set up the spline evaluation compute shader. We'll use SplineComputeBufferScope to simplify the process.
// Note that SplineComputeBufferScope is optional, you can manage the Curve, Lengths, and Info properties
// yourself if preferred.
m_SplineBuffers = new SplineComputeBufferScope<Spline>(m_Spline);
m_SplineBuffers.Bind(m_ComputeShader, m_GetPositionsKernel, "info", "curves", "curveLengths");
// Set the compute shader properties necessary for accessing spline information. Most Spline functions in
// Spline.cginc require the info, curves, and curve length properties. This is equivalent to:
// m_ComputeShader.SetVector("info", m_SplineBuffers.Info);
// m_ComputeShader.SetBuffer(m_GetPositionsKernel, "curves", m_SplineBuffers.Curves);
// m_ComputeShader.SetBuffer(m_GetPositionsKernel, "curveLengths", m_SplineBuffers.CurveLengths);
m_SplineBuffers.Upload();
// m_Positions will be used to read back evaluated positions from the GPU
m_Positions = new Vector3[m_Segments];
// Set up our input and readback buffers. In this example we'll evaluate a set of positions along the spline
m_PositionsBuffer = new ComputeBuffer(m_Segments, sizeof(float) * 3);
m_PositionsBuffer.SetData(m_Positions);
m_ComputeShader.SetBuffer(m_GetPositionsKernel, "positions", m_PositionsBuffer);
m_ComputeShader.SetFloat("positionsCount", m_Segments);
m_Dirty = true;
}
void OnEnable()
{
Spline.Changed += OnSplineChanged;
}
void OnDisable()
{
Spline.Changed -= OnSplineChanged;
}
void OnSplineChanged(Spline spline, int knotIndex, SplineModification modificationType)
{
if (m_Spline == spline)
m_Dirty = true;
}
void OnDestroy()
{
m_PositionsBuffer?.Dispose();
m_SplineBuffers.Dispose();
}
void Update()
{
if (!m_Dirty)
return;
// Once initialized, call SplineComputeBufferScope.Upload() to update the GPU copies of spline data. This
// is only necessary here because we're constantly updating the Spline in this example. If the Spline is
// static, there is no need to call Upload every frame.
m_SplineBuffers.Upload();
m_ComputeShader.GetKernelThreadGroupSizes(m_GetPositionsKernel, out var threadSize, out _, out _);
m_ComputeShader.Dispatch(m_GetPositionsKernel, (int)threadSize, 1, 1);
m_PositionsBuffer.GetData(m_Positions);
m_Line.loop = m_Spline.Closed;
m_Line.SetPositions(m_Positions);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fcea5d0b9b5209031bf47c34bf1eb238
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
{
"name": "Unity.Splines.Examples",
"rootNamespace": "",
"references": [
"GUID:21d1eb854b91ade49bc69a263d12bee2",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:e43142fc3fec6554980dde6126631f1d"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9f08613db0663446b876a5eae626aa45
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: