WuhuIslandTesting/Library/PackageCache/com.unity.splines@1.0.1/Runtime/SplineExtrude.cs
2025-01-07 02:06:59 +01:00

231 lines
8 KiB
C#

using System;
using System.IO;
using UnityEngine;
namespace UnityEngine.Splines
{
/// <summary>
/// A component for creating a tube mesh from a Spline at runtime.
/// </summary>
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
[AddComponentMenu("Splines/Spline Extrude")]
public class SplineExtrude : MonoBehaviour
{
[SerializeField, Tooltip("The Spline to extrude.")]
SplineContainer m_Container;
[SerializeField, Tooltip("Enable to regenerate the extruded mesh when the target Spline is modified. Disable " +
"this option if the Spline will not be modified at runtime.")]
bool m_RebuildOnSplineChange;
[SerializeField, Tooltip("The maximum number of times per-second that the mesh will be rebuilt.")]
int m_RebuildFrequency = 30;
[SerializeField, Tooltip("Automatically update any Mesh, Box, or Sphere collider components when the mesh is extruded.")]
bool m_UpdateColliders = true;
[SerializeField, Tooltip("The number of sides that comprise the radius of the mesh.")]
int m_Sides = 8;
[SerializeField, Tooltip("The number of edge loops that comprise the length of one unit of the mesh. The " +
"total number of sections is equal to \"Spline.GetLength() * segmentsPerUnit\".")]
float m_SegmentsPerUnit = 4;
[SerializeField, Tooltip("Indicates if the start and end of the mesh are filled. When the Spline is closed this setting is ignored.")]
bool m_Capped = true;
[SerializeField, Tooltip("The radius of the extruded mesh.")]
float m_Radius = .25f;
[SerializeField, Tooltip("The section of the Spline to extrude.")]
Vector2 m_Range = new Vector2(0f, 1f);
Spline m_Spline;
Mesh m_Mesh;
bool m_RebuildRequested;
float m_NextScheduledRebuild;
/// <summary>The SplineContainer of the <see cref="Spline"/> to extrude.</summary>
public SplineContainer container
{
get => m_Container;
set => m_Container = value;
}
/// <summary>
/// Enable to regenerate the extruded mesh when the target Spline is modified. Disable this option if the Spline
/// will not be modified at runtime.
/// </summary>
public bool rebuildOnSplineChange
{
get => m_RebuildOnSplineChange;
set => m_RebuildOnSplineChange = value;
}
/// <summary>The maximum number of times per-second that the mesh will be rebuilt.</summary>
public int rebuildFrequency
{
get => m_RebuildFrequency;
set => m_RebuildFrequency = Mathf.Max(value, 1);
}
/// <summary>How many sides make up the radius of the mesh.</summary>
public int sides
{
get => m_Sides;
set => m_Sides = Mathf.Max(value, 3);
}
/// <summary>How many edge loops comprise the one unit length of the mesh.</summary>
public float segmentsPerUnit
{
get => m_SegmentsPerUnit;
set => m_SegmentsPerUnit = Mathf.Max(value, .0001f);
}
/// <summary>Whether the start and end of the mesh is filled. This setting is ignored when spline is closed.</summary>
public bool capped
{
get => m_Capped;
set => m_Capped = value;
}
/// <summary>The radius of the extruded mesh.</summary>
public float radius
{
get => m_Radius;
set => m_Radius = Mathf.Max(value, .00001f);
}
/// <summary>
/// The section of the Spline to extrude.
/// </summary>
public Vector2 range
{
get => m_Range;
set => m_Range = new Vector2(Mathf.Min(value.x, value.y), Mathf.Max(value.x, value.y));
}
/// <summary>The Spline to extrude.</summary>
public Spline spline
{
get
{
// m_Spline is cached in the Start() method, meaning that it is not valid in the Editor.
#if UNITY_EDITOR
return m_Container.Spline;
#else
return m_Spline;
#endif
}
}
void Reset()
{
TryGetComponent(out m_Container);
if (TryGetComponent<MeshFilter>(out var filter) && filter.sharedMesh != null)
m_Mesh = filter.sharedMesh;
else
filter.sharedMesh = m_Mesh = CreateMeshAsset();
if (TryGetComponent<MeshRenderer>(out var renderer) && renderer.sharedMaterial == null)
{
// todo Make Material.GetDefaultMaterial() public
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
var mat = cube.GetComponent<MeshRenderer>().sharedMaterial;
DestroyImmediate(cube);
renderer.sharedMaterial = mat;
}
Rebuild();
}
void Start()
{
if (m_Container == null || (m_Spline = m_Container.Spline) == null)
{
Debug.LogError("Spline Extrude does not have a valid SplineContainer set.");
return;
}
if((m_Mesh = GetComponent<MeshFilter>().sharedMesh) == null)
Debug.LogError("SplineExtrude.createMeshInstance is disabled, but there is no valid mesh assigned. " +
"Please create or assign a writable mesh asset.");
if (m_RebuildOnSplineChange)
m_Spline.changed += () => m_RebuildRequested = true;
Rebuild();
}
void Update()
{
if(m_RebuildRequested && Time.time >= m_NextScheduledRebuild)
Rebuild();
}
/// <summary>
/// Triggers the rebuild of a Spline's extrusion mesh and collider.
/// </summary>
public void Rebuild()
{
if(m_Mesh == null && (m_Mesh = GetComponent<MeshFilter>().sharedMesh) == null)
return;
float span = Mathf.Abs(range.y - range.x);
int segments = Mathf.Max((int)Mathf.Ceil(spline.GetLength() * span * m_SegmentsPerUnit), 1);
SplineMesh.Extrude(spline, m_Mesh, m_Radius, m_Sides, segments, m_Capped, m_Range);
m_NextScheduledRebuild = Time.time + 1f / m_RebuildFrequency;
if (m_UpdateColliders)
{
if (TryGetComponent<MeshCollider>(out var meshCollider))
meshCollider.sharedMesh = m_Mesh;
if (TryGetComponent<BoxCollider>(out var boxCollider))
{
boxCollider.center = m_Mesh.bounds.center;
boxCollider.size = m_Mesh.bounds.size;
}
if (TryGetComponent<SphereCollider>(out var sphereCollider))
{
sphereCollider.center = m_Mesh.bounds.center;
var ext = m_Mesh.bounds.extents;
sphereCollider.radius = Mathf.Max(ext.x, ext.y, ext.z);
}
}
}
void OnValidate()
{
Rebuild();
}
internal Mesh CreateMeshAsset()
{
var mesh = new Mesh();
mesh.name = name;
#if UNITY_EDITOR
var scene = SceneManagement.SceneManager.GetActiveScene();
var sceneDataDir = "Assets";
if (!string.IsNullOrEmpty(scene.path))
{
var dir = Path.GetDirectoryName(scene.path);
sceneDataDir = $"{dir}/{Path.GetFileNameWithoutExtension(scene.path)}";
if (!Directory.Exists(sceneDataDir))
Directory.CreateDirectory(sceneDataDir);
}
var path = UnityEditor.AssetDatabase.GenerateUniqueAssetPath($"{sceneDataDir}/{mesh.name}.asset");
UnityEditor.AssetDatabase.CreateAsset(mesh, path);
mesh = UnityEditor.AssetDatabase.LoadAssetAtPath<Mesh>(path);
UnityEditor.EditorGUIUtility.PingObject(mesh);
#endif
return mesh;
}
}
}