203 lines
6.5 KiB
C#
203 lines
6.5 KiB
C#
#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;
|
|
}
|
|
}
|
|
}
|