initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,165 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine.Splines;
|
||||
using Unity.Mathematics;
|
||||
using Interpolators = UnityEngine.Splines.Interpolators;
|
||||
using Quaternion = UnityEngine.Quaternion;
|
||||
|
||||
namespace Unity.Splines.Examples
|
||||
{
|
||||
public class AnimateCarAlongSpline: MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
SplineContainer m_SplineContainer;
|
||||
public SplineContainer splineContainer => m_SplineContainer;
|
||||
|
||||
[SerializeField]
|
||||
Car m_CarToAnimate;
|
||||
|
||||
[Min(0f)]
|
||||
public float m_DefaultSpeed = 10f;
|
||||
[Min(0f)]
|
||||
public float m_MaxSpeed = 30f;
|
||||
|
||||
[SerializeField]
|
||||
SplineData<float> m_Speed = new SplineData<float>();
|
||||
public SplineData<float> speed => m_Speed;
|
||||
|
||||
public Vector3 m_DefaultTilt = Vector3.up;
|
||||
[SerializeField]
|
||||
SplineData<float3> m_Tilt = new SplineData<float3>();
|
||||
public SplineData<float3> tilt => m_Tilt;
|
||||
|
||||
[SerializeField]
|
||||
DriftSplineData m_Drift;
|
||||
public DriftSplineData driftData
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_Drift == null)
|
||||
m_Drift = GetComponent<DriftSplineData>();
|
||||
|
||||
return m_Drift;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
PointSplineData m_LookAtPoints;
|
||||
public PointSplineData lookAtPoints
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_LookAtPoints == null)
|
||||
m_LookAtPoints = GetComponent<PointSplineData>();
|
||||
|
||||
return m_LookAtPoints;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
Transform m_LookTransform;
|
||||
|
||||
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 = m_DefaultSpeed;
|
||||
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_DefaultTilt;
|
||||
m_Tilt[index] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(lookAtPoints != null)
|
||||
lookAtPoints.container = splineContainer;
|
||||
|
||||
if(driftData != null)
|
||||
driftData.container = splineContainer;
|
||||
}
|
||||
|
||||
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_DefaultSpeed;
|
||||
|
||||
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.m_Default :
|
||||
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 == null || m_Tilt.Count == 0) ?
|
||||
m_DefaultTilt :
|
||||
(Vector3)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);
|
||||
|
||||
if (lookAtPoints != null && m_LookTransform != null && m_LookAtPoints.Count > 0)
|
||||
{
|
||||
var lookAtPoint = m_LookAtPoints.points.Evaluate(m_Spline, m_CurrentOffset, PathIndexUnit.Normalized, new Interpolators.LerpFloat2());
|
||||
direction = math.normalize(new float3(lookAtPoint.x, 0f, lookAtPoint.y) - (float3)m_LookTransform.position);
|
||||
m_LookTransform.transform.rotation = Quaternion.LookRotation(direction, up);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9eac6ceedc2724f27893c14cb52f1fc7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,32 @@
|
|||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
using Interpolators = UnityEngine.Splines.Interpolators;
|
||||
|
||||
namespace Unity.Splines.Examples
|
||||
{
|
||||
public class AnimateLookAt : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
SplineAnimate m_SplineAnimate;
|
||||
|
||||
[SerializeField]
|
||||
PointSplineData m_LookAtPoints;
|
||||
|
||||
[SerializeField]
|
||||
Transform m_LookTransform;
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if(m_SplineAnimate == null || m_LookAtPoints == null || m_LookTransform == null)
|
||||
return;
|
||||
|
||||
var spline = m_SplineAnimate.splineContainer.Spline;
|
||||
var t = m_SplineAnimate.normalizedTime;
|
||||
var lookAtPoint = m_LookAtPoints.points.Evaluate(spline, t, PathIndexUnit.Normalized, new Interpolators.SlerpFloat2());
|
||||
var direction = math.normalize(new float3(lookAtPoint.x, 0f, lookAtPoint.y) - (float3)m_LookTransform.position);
|
||||
|
||||
m_LookTransform.transform.rotation = Quaternion.LookRotation(direction, Vector3.up);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 65b93e0cb209641b7b5a15e349da0cd0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,42 @@
|
|||
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 closed = m_Extrude.spline.Closed;
|
||||
float t = closed
|
||||
? 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8632a8538dad4702a8e48b1a10c92ef4
|
||||
timeCreated: 1637694540
|
|
@ -0,0 +1,8 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Splines.Examples
|
||||
{
|
||||
public class Car : MonoBehaviour {}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e3af2c81f946c984ead177f24f06763b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace Unity.Splines.Examples
|
||||
{
|
||||
[RequireComponent(typeof(SplineContainer))]
|
||||
public class DisplayCurvatureOnSpline : MonoBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
public struct CurvatureConfig
|
||||
{
|
||||
public bool display;
|
||||
public float time;
|
||||
}
|
||||
|
||||
public List<CurvatureConfig> m_CurvatureTimes = new List<CurvatureConfig>();
|
||||
|
||||
SplineContainer m_Container;
|
||||
public SplineContainer container
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_Container == null)
|
||||
m_Container = GetComponent<SplineContainer>();
|
||||
return m_Container;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c5f13d731e8fcd04a85aa5137425f753
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,203 @@
|
|||
#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]
|
||||
SplineContainer m_Spline;
|
||||
|
||||
[SerializeField]
|
||||
int m_SegmentsPerMeter = 1;
|
||||
|
||||
[SerializeField]
|
||||
Mesh m_Mesh;
|
||||
|
||||
[SerializeField]
|
||||
float m_TextureScale = 1f;
|
||||
|
||||
WidthSplineData m_WidthData;
|
||||
|
||||
WidthSplineData widthData
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_WidthData == null)
|
||||
m_WidthData = GetComponent<WidthSplineData>();
|
||||
if(m_WidthData == null)
|
||||
m_WidthData = gameObject.AddComponent<WidthSplineData>();
|
||||
|
||||
if(m_WidthData.container == null)
|
||||
m_WidthData.container = m_Spline;
|
||||
|
||||
return m_WidthData;
|
||||
}
|
||||
}
|
||||
|
||||
public Spline spline
|
||||
{
|
||||
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.Spline;
|
||||
}
|
||||
}
|
||||
|
||||
public Mesh mesh
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Mesh != null)
|
||||
return m_Mesh;
|
||||
|
||||
m_Mesh = new Mesh();
|
||||
GetComponent<MeshRenderer>().sharedMaterial = Resources.Load<Material>("Road");
|
||||
return m_Mesh;
|
||||
}
|
||||
}
|
||||
|
||||
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>();
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
//Avoid to point to an existing instance when duplicating the GameObject
|
||||
if(m_Mesh != null)
|
||||
m_Mesh = null;
|
||||
|
||||
if(m_WidthData == null)
|
||||
m_WidthData = GetComponent<WidthSplineData>();
|
||||
if(m_WidthData == null)
|
||||
m_WidthData = gameObject.AddComponent<WidthSplineData>();
|
||||
|
||||
Loft();
|
||||
#if UNITY_EDITOR
|
||||
EditorSplineUtility.afterSplineWasModified += OnAfterSplineWasModified;
|
||||
EditorSplineUtility.RegisterSplineDataChanged<float>(OnAfterSplineDataWasModified);
|
||||
Undo.undoRedoPerformed += Loft;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void OnDisable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorSplineUtility.afterSplineWasModified -= OnAfterSplineWasModified;
|
||||
EditorSplineUtility.UnregisterSplineDataChanged<float>(OnAfterSplineDataWasModified);
|
||||
Undo.undoRedoPerformed -= Loft;
|
||||
#endif
|
||||
|
||||
if(m_Mesh != null)
|
||||
#if UNITY_EDITOR
|
||||
DestroyImmediate(m_Mesh);
|
||||
#else
|
||||
Destroy(m_Mesh);
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnAfterSplineWasModified(Spline s)
|
||||
{
|
||||
if(s == spline)
|
||||
Loft();
|
||||
}
|
||||
|
||||
void OnAfterSplineDataWasModified(SplineData<float> splineData)
|
||||
{
|
||||
if (splineData == m_WidthData.width)
|
||||
Loft();
|
||||
}
|
||||
|
||||
public void Loft()
|
||||
{
|
||||
if (spline == null || spline.Count < 2)
|
||||
return;
|
||||
|
||||
mesh.Clear();
|
||||
|
||||
float length = spline.GetLength();
|
||||
|
||||
if (length < 1)
|
||||
return;
|
||||
|
||||
int segments = (int)(segmentsPerMeter * length);
|
||||
int vertexCount = segments * 2, triangleCount = (spline.Closed ? segments : segments - 1) * 6;
|
||||
|
||||
m_Positions.Clear();
|
||||
m_Normals.Clear();
|
||||
m_Textures.Clear();
|
||||
m_Indices.Clear();
|
||||
|
||||
m_Positions.Capacity = vertexCount;
|
||||
m_Normals.Capacity = vertexCount;
|
||||
m_Textures.Capacity = vertexCount;
|
||||
m_Indices.Capacity = triangleCount;
|
||||
|
||||
for (int i = 0; i < segments; i++)
|
||||
{
|
||||
var index = i / (segments - 1f);
|
||||
var control = SplineUtility.EvaluatePosition(spline, index);
|
||||
var dir = SplineUtility.EvaluateTangent(spline, index);
|
||||
var up = SplineUtility.EvaluateUpVector(spline, index);
|
||||
|
||||
var scale = transform.lossyScale;
|
||||
//var tangent = math.normalize((float3)math.mul(math.cross(up, dir), new float3(1f / scale.x, 1f / scale.y, 1f / scale.z)));
|
||||
var tangent = math.normalize(math.cross(up, dir)) * new float3(1f / scale.x, 1f / scale.y, 1f / scale.z);
|
||||
|
||||
var w = widthData.m_DefaultWidth;
|
||||
if(widthData.width != null && widthData.Count > 0)
|
||||
{
|
||||
w = widthData.width.Evaluate(spline, index, PathIndexUnit.Normalized, new Interpolators.LerpFloat());
|
||||
w = math.clamp(w, .001f, 10000f);
|
||||
}
|
||||
|
||||
m_Positions.Add(control - (tangent * w));
|
||||
m_Positions.Add(control + (tangent * w));
|
||||
m_Normals.Add(Vector3.up);
|
||||
m_Normals.Add(Vector3.up);
|
||||
m_Textures.Add(new Vector2(0f, index * m_TextureScale));
|
||||
m_Textures.Add(new Vector2(1f, index * m_TextureScale));
|
||||
}
|
||||
|
||||
for (int i = 0, n = 0; i < triangleCount; i += 6, n += 2)
|
||||
{
|
||||
m_Indices.Add((n + 2) % vertexCount);
|
||||
m_Indices.Add((n + 1) % vertexCount);
|
||||
m_Indices.Add((n + 0) % vertexCount);
|
||||
m_Indices.Add((n + 2) % vertexCount);
|
||||
m_Indices.Add((n + 3) % vertexCount);
|
||||
m_Indices.Add((n + 1) % vertexCount);
|
||||
}
|
||||
|
||||
mesh.SetVertices(m_Positions);
|
||||
mesh.SetNormals(m_Normals);
|
||||
mesh.SetUVs(0, m_Textures);
|
||||
mesh.subMeshCount = 1;
|
||||
mesh.SetIndices(m_Indices, MeshTopology.Triangles, 0);
|
||||
mesh.UploadMeshData(false);
|
||||
|
||||
GetComponent<MeshFilter>().sharedMesh = m_Mesh;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d3f00a79918dc4a18a050534219a9842
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Splines.Examples;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
|
||||
public class Rollercoaster : MonoBehaviour, ISplineProvider
|
||||
{
|
||||
[SerializeField]
|
||||
RollercoasterTrack m_Track;
|
||||
|
||||
[SerializeField]
|
||||
Transform m_Cart;
|
||||
|
||||
[SerializeField]
|
||||
float m_Speed = .314f;
|
||||
|
||||
public IEnumerable<Spline> Splines => new[] { m_Track };
|
||||
|
||||
void Update()
|
||||
{
|
||||
var trs = transform;
|
||||
var t = math.frac(Time.time * m_Speed);
|
||||
m_Cart.position = trs.TransformPoint(m_Track.EvaluatePosition(t));
|
||||
m_Cart.rotation = Quaternion.LookRotation(trs.TransformDirection(m_Track.EvaluateTangent(t)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 51568c0108ad64e7f876de5f2595f260
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,125 @@
|
|||
using System;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace Unity.Splines.Examples
|
||||
{
|
||||
// Example showing how to extend Spline class through inheritance.
|
||||
[Serializable]
|
||||
public class RollercoasterTrack : Spline
|
||||
{
|
||||
const string k_GeneratedMeshDirectory = "Assets/Generated/Rollercoaster";
|
||||
|
||||
[SerializeField, Range(.1f, 10f)]
|
||||
float m_TrackWidth = 1f;
|
||||
|
||||
[SerializeField]
|
||||
int m_TracksPerMeter = 2;
|
||||
|
||||
[SerializeField]
|
||||
Mesh m_Mesh;
|
||||
|
||||
public Mesh mesh
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Mesh != null)
|
||||
return m_Mesh;
|
||||
|
||||
m_Mesh = new Mesh();
|
||||
|
||||
// In the editor it's usually convenient to save the mesh as an asset rather than generate it on the
|
||||
// fly any time the scene is loaded.
|
||||
#if UNITY_EDITOR
|
||||
Directory.CreateDirectory(k_GeneratedMeshDirectory);
|
||||
var path = AssetDatabase.GenerateUniqueAssetPath($"{k_GeneratedMeshDirectory}/Track.asset");
|
||||
AssetDatabase.CreateAsset(m_Mesh, path);
|
||||
AssetDatabase.ImportAsset(path);
|
||||
#endif
|
||||
return m_Mesh;
|
||||
}
|
||||
}
|
||||
|
||||
// When working in the editor, instead of rebuilding the mesh any time the spline changes (which can be often
|
||||
// especially when moving knots), we'll schedule updates once per-frame.
|
||||
#if UNITY_EDITOR
|
||||
[NonSerialized]
|
||||
bool m_RebuildScheduled;
|
||||
#endif
|
||||
|
||||
protected override void OnSplineChanged()
|
||||
{
|
||||
base.OnSplineChanged();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (m_RebuildScheduled)
|
||||
return;
|
||||
m_RebuildScheduled = true;
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
RebuildTracks();
|
||||
m_RebuildScheduled = false;
|
||||
};
|
||||
#else
|
||||
RebuildTracks();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Generate a set of ties along the spline path.
|
||||
public void RebuildTracks()
|
||||
{
|
||||
using var spline = new NativeSpline(this);
|
||||
var len = spline.GetLength();
|
||||
var tieCount = (int) (len * m_TracksPerMeter);
|
||||
|
||||
mesh.Clear();
|
||||
|
||||
Vector3[] positions = new Vector3[tieCount * 4];
|
||||
Vector3[] normals = new Vector3[tieCount * 4];
|
||||
int[] indices = new int[tieCount * 6];
|
||||
|
||||
for (int i = 0; i < tieCount; i++)
|
||||
{
|
||||
float t = (float) i / (tieCount - 1);
|
||||
|
||||
var position = SplineUtility.EvaluatePosition(spline, t);
|
||||
var forward = SplineUtility.EvaluateTangent(spline, t);
|
||||
var tangent = (quaternion) Quaternion.LookRotation(forward);
|
||||
|
||||
int a = i * 4 + 0,
|
||||
b = i * 4 + 1,
|
||||
c = i * 4 + 2,
|
||||
d = i * 4 + 3;
|
||||
|
||||
positions[a] = position + math.mul(tangent, new float3(-m_TrackWidth * .5f, 0f, -0.25f));
|
||||
positions[b] = position + math.mul(tangent, new float3( m_TrackWidth * .5f, 0f, -0.25f));
|
||||
positions[c] = position + math.mul(tangent, new float3(-m_TrackWidth * .5f, 0f, 0.25f));
|
||||
positions[d] = position + math.mul(tangent, new float3( m_TrackWidth * .5f, 0f, 0.25f));
|
||||
|
||||
normals[a] = Vector3.up;
|
||||
normals[b] = Vector3.up;
|
||||
normals[c] = Vector3.up;
|
||||
normals[d] = Vector3.up;
|
||||
|
||||
indices[i*6+0] = c;
|
||||
indices[i*6+1] = b;
|
||||
indices[i*6+2] = a;
|
||||
|
||||
indices[i*6+3] = b;
|
||||
indices[i*6+4] = c;
|
||||
indices[i*6+5] = d;
|
||||
}
|
||||
|
||||
mesh.vertices = positions;
|
||||
mesh.normals = normals;
|
||||
mesh.subMeshCount = 1;
|
||||
mesh.SetIndices(indices, MeshTopology.Triangles, 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0d3bd4b9fa514481a4646178f0503ea0
|
||||
timeCreated: 1618498598
|
|
@ -0,0 +1,71 @@
|
|||
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;
|
||||
m_SplineContainer = FindObjectsOfType<SplineContainer>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8d0206caa731d581c98dd8df314b4614
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,331 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
using Unity.Mathematics;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.Splines;
|
||||
#endif
|
||||
using Interpolators = UnityEngine.Splines.Interpolators;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Unity.Splines.Examples
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[DisallowMultipleComponent]
|
||||
public class SpawnWithinSplineBounds : MonoBehaviour
|
||||
{
|
||||
struct SpawnPoint
|
||||
{
|
||||
public float3 pos;
|
||||
public float3 right;
|
||||
public float3 up;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
SplineContainer m_SplineContainer;
|
||||
public SplineContainer splineContainer => m_SplineContainer;
|
||||
|
||||
[SerializeField]
|
||||
Transform m_SpawnContainer;
|
||||
[SerializeField]
|
||||
int m_MaxIterations;
|
||||
|
||||
[Header("Spawning")]
|
||||
[SerializeField]
|
||||
List<GameObject> m_Prefabs;
|
||||
[SerializeField]
|
||||
float m_SpawnSpacing;
|
||||
[SerializeField]
|
||||
[Range(0, 1)]
|
||||
float m_SpawnChance;
|
||||
[SerializeField]
|
||||
List<GameObject> m_BorderPrefabs;
|
||||
[SerializeField]
|
||||
float m_BorderSpawnSpacing;
|
||||
[SerializeField]
|
||||
[Range(0, 1)]
|
||||
float m_BorderSpawnChance;
|
||||
[SerializeField]
|
||||
SplineData<float> m_SpawnBorderData;
|
||||
public SplineData<float> spawnBorderData => m_SpawnBorderData;
|
||||
|
||||
[Header("Randomization")]
|
||||
[SerializeField]
|
||||
int m_RandomSeed;
|
||||
[SerializeField]
|
||||
Vector2 m_RotationRandomRange;
|
||||
|
||||
int m_Iterations;
|
||||
List<Vector2> m_SplineSegments = new List<Vector2>();
|
||||
List<Vector2> m_ParentPointSegments = new List<Vector2>();
|
||||
|
||||
const int k_SegmentsPerCurve = 10;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorSplineUtility.afterSplineWasModified += OnSplineModified;
|
||||
m_SpawnBorderData.changed += OnSpawnBorderDataChanged;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorSplineUtility.afterSplineWasModified -= OnSplineModified;
|
||||
m_SpawnBorderData.changed -= OnSpawnBorderDataChanged;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (m_SplineContainer != null && !EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
EditorApplication.delayCall += () => OnSplineModified(m_SplineContainer.Spline);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CleanUp()
|
||||
{
|
||||
if (m_SpawnContainer == null)
|
||||
return;
|
||||
|
||||
for (int i = m_SpawnContainer.childCount - 1; i >= 0; --i)
|
||||
{
|
||||
var child = m_SpawnContainer.GetChild(i);
|
||||
#if UNITY_EDITOR
|
||||
DestroyImmediate(child.gameObject);
|
||||
#else
|
||||
Destroy(child.gameObject);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void OnSplineModified(Spline spline)
|
||||
{
|
||||
if (spline == m_SplineContainer.Spline && m_SpawnContainer != null)
|
||||
{
|
||||
CleanUp();
|
||||
BuildSplineSegments();
|
||||
Random.InitState(m_RandomSeed);
|
||||
|
||||
if (m_Prefabs.Count > 0)
|
||||
SpawnObjectsWithinSpline(m_SplineContainer, m_SpawnChance, m_SpawnSpacing, false);
|
||||
|
||||
if (m_BorderPrefabs.Count > 0)
|
||||
SpawnObjectsWithinSpline(m_SplineContainer, m_BorderSpawnChance, m_BorderSpawnSpacing, true);
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnObjectsWithinSpline(SplineContainer splineContainer, float spawnChance, float spawnSpacing, bool spawnOnBorder)
|
||||
{
|
||||
var spline = splineContainer.Spline;
|
||||
var splineLen = spline.GetLength();
|
||||
var points = new List<SpawnPoint>();
|
||||
var splineTime = 0f;
|
||||
var spawnCount = Mathf.CeilToInt(splineLen / spawnSpacing);
|
||||
var splineXform = splineContainer.transform;
|
||||
for (int i = 0; i < spawnCount; ++i)
|
||||
{
|
||||
// Here we do not need to manually transform the output vectors as all SplineContainer's evaluation methods transform the results to world space.
|
||||
splineContainer.Evaluate(splineTime, out var _, out var dir, out var up);
|
||||
|
||||
// Spline's evaluation methods return results in spline space therfore manual transforming to world space is required.
|
||||
var pos = splineXform.TransformPoint(spline.GetPointAtLinearDistance(splineTime, spawnSpacing, out splineTime));
|
||||
var right = splineXform.TransformDirection(Vector3.ProjectOnPlane(math.cross(math.normalize(dir), up), up));
|
||||
var spawnBorder = m_SpawnBorderData.Evaluate(spline, splineTime, PathIndexUnit.Normalized, new Interpolators.LerpFloat());
|
||||
|
||||
if (spawnBorder <= spawnSpacing * 0.5f)
|
||||
{
|
||||
if (!spawnOnBorder)
|
||||
SpawnRandomPrefab(m_Prefabs, pos, -right, up, spawnChance);
|
||||
}
|
||||
else if (spawnOnBorder)
|
||||
SpawnRandomPrefab(m_BorderPrefabs, pos, -right, up, spawnChance);
|
||||
|
||||
points.Add(new SpawnPoint() { pos = pos, right = right, up = up });
|
||||
}
|
||||
|
||||
m_Iterations = 1;
|
||||
SpawnObjectsForPoints(points, spawnChance, spawnSpacing, spawnOnBorder);
|
||||
}
|
||||
|
||||
void SpawnObjectsForPoints(List<SpawnPoint> points, float spawnChance, float spawnSpacing, bool spawnOnBorder)
|
||||
{
|
||||
if (m_Iterations == m_MaxIterations)
|
||||
return;
|
||||
|
||||
// Backup parent points
|
||||
var parentPoints = new List<SpawnPoint>(points);
|
||||
|
||||
// Offset all child points along right vector
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
{
|
||||
var splinePoint = points[i];
|
||||
var nextIdx = (i == points.Count - 1 ? 0 : i + 1);
|
||||
var nextPoint = points[nextIdx];
|
||||
var right = (float3) Vector3.Slerp(splinePoint.right, nextPoint.right, 0.5f);
|
||||
var up = (float3) Vector3.Slerp(splinePoint.up, nextPoint.up, 0.5f);
|
||||
var pos = math.lerp(splinePoint.pos, nextPoint.pos, 0.5f);
|
||||
|
||||
splinePoint.pos = pos + right * spawnSpacing;
|
||||
splinePoint.right = right;
|
||||
splinePoint.up = up;
|
||||
|
||||
points[i] = splinePoint;
|
||||
}
|
||||
|
||||
var pointsToRemove = new SortedSet<int>();
|
||||
var spawnedPoints = new List<SpawnPoint>();
|
||||
|
||||
// Check if point should be discard - spawn otherwise
|
||||
for (int i = 0; i < points.Count; ++i)
|
||||
{
|
||||
var discardPoint = false;
|
||||
var pointWithinBorder = false;
|
||||
var pointSplineSpace = m_SplineContainer.transform.InverseTransformPoint(points[i].pos);
|
||||
|
||||
// Check against border
|
||||
var dist = SplineUtility.GetNearestPoint(m_SplineContainer.Spline, pointSplineSpace, out var _, out var splineTime);
|
||||
var spawnOffset = m_SpawnBorderData.Evaluate(m_SplineContainer.Spline, splineTime, PathIndexUnit.Normalized, new Interpolators.LerpFloat());
|
||||
if (dist < spawnOffset)
|
||||
pointWithinBorder = true;
|
||||
|
||||
// Check against child points
|
||||
for (int spawnedPointIdx = spawnedPoints.Count - 1; spawnedPointIdx >= 0; --spawnedPointIdx)
|
||||
{
|
||||
if (math.length(points[i].pos - spawnedPoints[spawnedPointIdx].pos) < spawnSpacing)
|
||||
{
|
||||
discardPoint = true;
|
||||
pointsToRemove.Add(i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check against parent points
|
||||
if (!discardPoint)
|
||||
{
|
||||
var nextIdx = i == points.Count - 1 ? 0 : i + 1;
|
||||
while (i != nextIdx)
|
||||
{
|
||||
if (math.length(points[i].pos - parentPoints[nextIdx].pos) < spawnSpacing * 0.9f)
|
||||
{
|
||||
discardPoint = true;
|
||||
pointsToRemove.Add(i);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
nextIdx = nextIdx == points.Count - 1 ? 0 : nextIdx + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure point is within parent points bounds
|
||||
if (!discardPoint)
|
||||
{
|
||||
m_ParentPointSegments.Clear();
|
||||
foreach (var parentPoint in parentPoints)
|
||||
m_ParentPointSegments.Add(new Vector2(parentPoint.pos.x, parentPoint.pos.z));
|
||||
|
||||
discardPoint = !PointInsidePolygon(new Vector2(points[i].pos.x, points[i].pos.z), m_ParentPointSegments);
|
||||
}
|
||||
|
||||
// Ensure point is roughly within spline bounds
|
||||
if (!discardPoint)
|
||||
discardPoint = !PointInsidePolygon(new Vector2(pointSplineSpace.x, pointSplineSpace.z), m_SplineSegments);
|
||||
|
||||
if (!discardPoint)
|
||||
{
|
||||
if (!pointWithinBorder)
|
||||
{
|
||||
if (!spawnOnBorder)
|
||||
SpawnRandomPrefab(m_Prefabs, points[i].pos, -points[i].right, points[i].up, spawnChance);
|
||||
}
|
||||
else if (spawnOnBorder)
|
||||
SpawnRandomPrefab(m_BorderPrefabs, points[i].pos, -points[i].right, points[i].up, spawnChance);
|
||||
spawnedPoints.Add(points[i]);
|
||||
}
|
||||
}
|
||||
|
||||
m_Iterations++;
|
||||
foreach (var point in pointsToRemove.Reverse())
|
||||
points.RemoveAt(point);
|
||||
|
||||
if (points.Count == 0)
|
||||
return;
|
||||
|
||||
SpawnObjectsForPoints(points, spawnChance, spawnSpacing, spawnOnBorder);
|
||||
}
|
||||
|
||||
void SpawnRandomPrefab(List<GameObject> prefabs, Vector3 position, Vector3 forward, Vector3 up, float spawnChance)
|
||||
{
|
||||
if (Random.Range(0f, 1f) > spawnChance)
|
||||
return;
|
||||
|
||||
var prefab = prefabs[Random.Range(0, prefabs.Count)];
|
||||
var go = Instantiate(prefab, position, quaternion.identity);
|
||||
go.transform.rotation = Quaternion.LookRotation(forward, up) * Quaternion.AngleAxis(Random.Range(m_RotationRandomRange.x, m_RotationRandomRange.y), Vector3.up);
|
||||
go.transform.SetParent(m_SpawnContainer, true);
|
||||
}
|
||||
|
||||
void OnSpawnBorderDataChanged()
|
||||
{
|
||||
OnSplineModified(m_SplineContainer.Spline);
|
||||
}
|
||||
|
||||
void BuildSplineSegments()
|
||||
{
|
||||
m_SplineSegments.Clear();
|
||||
|
||||
var spline = m_SplineContainer.Spline;
|
||||
var curveCount = spline.Closed ? spline.Count : spline.Count - 1;
|
||||
var stepSize = 1f / k_SegmentsPerCurve;
|
||||
|
||||
for (int curveIndex = 0; curveIndex < curveCount; ++curveIndex)
|
||||
{
|
||||
for (int step = 0; step < k_SegmentsPerCurve; ++step)
|
||||
{
|
||||
var splineTime = spline.CurveToSplineT(curveIndex + step * stepSize);
|
||||
var pos = spline.EvaluatePosition(splineTime);
|
||||
|
||||
m_SplineSegments.Add(new Vector2(pos.x, pos.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PointInsidePolygon(Vector2 point, List<Vector2> polygon)
|
||||
{
|
||||
Vector2 p1, p2;
|
||||
p1 = polygon[0];
|
||||
var counter = 0;
|
||||
for (int i = 1; i <= polygon.Count; i++)
|
||||
{
|
||||
p2 = polygon[i % polygon.Count];
|
||||
if (point.y > Mathf.Min(p1.y, p2.y))
|
||||
{
|
||||
if (point.y <= Mathf.Max(p1.y, p2.y))
|
||||
{
|
||||
if (point.x <= Mathf.Max(p1.x, p2.x))
|
||||
{
|
||||
if (p1.y != p2.y)
|
||||
{
|
||||
var xinters = (point.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
|
||||
if (p1.x == p2.x || point.x <= xinters)
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p1 = p2;
|
||||
}
|
||||
|
||||
if (counter % 2 == 0)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2e0318f4ace494445a245336e74fcd88
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 497b34923fb3579478af67bfb5a82674
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace Unity.Splines.Examples
|
||||
{
|
||||
public class DriftSplineData : MonoBehaviour
|
||||
{
|
||||
public float m_Default = 0f;
|
||||
|
||||
[SerializeField]
|
||||
SplineData<float> m_Drift;
|
||||
|
||||
public SplineData<float> drift => m_Drift;
|
||||
|
||||
SplineContainer m_SplineContainer;
|
||||
public SplineContainer container
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_SplineContainer == null)
|
||||
m_SplineContainer = GetComponent<SplineContainer>();
|
||||
return m_SplineContainer;
|
||||
}
|
||||
set => m_SplineContainer = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cd08d575c1c88a646835fb7c516cfa72
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace Unity.Splines.Examples
|
||||
{
|
||||
public class PointSplineData : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
SplineData<float2> m_Points;
|
||||
public SplineData<float2> points => m_Points;
|
||||
|
||||
public int Count => m_Points.Count;
|
||||
|
||||
SplineContainer m_SplineContainer;
|
||||
public SplineContainer container
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_SplineContainer == null)
|
||||
m_SplineContainer = GetComponent<SplineContainer>();
|
||||
return m_SplineContainer;
|
||||
}
|
||||
set => m_SplineContainer = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 18b78468053521b4dbb5d1a7dba64532
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace Unity.Splines.Examples
|
||||
{
|
||||
public class WidthSplineData : MonoBehaviour
|
||||
{
|
||||
public float m_DefaultWidth = 1f;
|
||||
|
||||
[SerializeField]
|
||||
SplineData<float> m_Width;
|
||||
|
||||
public SplineData<float> width => m_Width;
|
||||
|
||||
public int Count => m_Width.Count;
|
||||
|
||||
SplineContainer m_SplineContainer;
|
||||
|
||||
public SplineContainer container
|
||||
{
|
||||
get
|
||||
{
|
||||
if(m_SplineContainer == null)
|
||||
m_SplineContainer = GetComponent<SplineContainer>();
|
||||
return m_SplineContainer;
|
||||
}
|
||||
set => m_SplineContainer = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a787722e962040bca5a786da5aae63f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3fcd72b7e265696c4a96291318699084
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Splines;
|
||||
|
||||
namespace Unity.Splines.Examples
|
||||
{
|
||||
[RequireComponent(typeof(LineRenderer), typeof(SplineContainer))]
|
||||
public class SplineRenderer : MonoBehaviour
|
||||
{
|
||||
Spline m_Spline;
|
||||
LineRenderer m_Line;
|
||||
bool m_Dirty;
|
||||
Vector3[] m_Points;
|
||||
|
||||
[SerializeField, Range(16, 512)]
|
||||
int m_Segments = 128;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
m_Spline = GetComponent<SplineContainer>().Spline;
|
||||
m_Line = GetComponent<LineRenderer>();
|
||||
m_Spline.changed += () => m_Dirty = true;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// It's nice to be able to see resolution changes at runtime
|
||||
if (m_Points?.Length != m_Segments)
|
||||
{
|
||||
m_Dirty = true;
|
||||
m_Points = new Vector3[m_Segments];
|
||||
m_Line.loop = m_Spline.Closed;
|
||||
m_Line.positionCount = m_Segments;
|
||||
}
|
||||
|
||||
if (!m_Dirty)
|
||||
return;
|
||||
|
||||
m_Dirty = false;
|
||||
|
||||
for (int i = 0; i < m_Segments; i++)
|
||||
m_Points[i] = m_Spline.EvaluatePosition(i / (m_Segments - 1f));
|
||||
|
||||
m_Line.SetPositions(m_Points);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d9dd4cf612254c08bf888d5bca6bb2d5
|
||||
timeCreated: 1628867781
|
|
@ -0,0 +1,90 @@
|
|||
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);
|
||||
|
||||
// Subscribe to Spline.changed to be notified when the spline is modified
|
||||
m_Spline.changed += () => m_Dirty = true;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fcea5d0b9b5209031bf47c34bf1eb238
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9f08613db0663446b876a5eae626aa45
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue