618 lines
16 KiB
C#
618 lines
16 KiB
C#
|
using UnityEngine;
|
||
|
using System.Collections;
|
||
|
using System.Threading;
|
||
|
#if UNITY_EDITOR
|
||
|
using UnityEditor;
|
||
|
#endif
|
||
|
|
||
|
namespace Dreamteck.Splines
|
||
|
{
|
||
|
public class MeshGenerator : SplineUser
|
||
|
{
|
||
|
protected const int UNITY_16_VERTEX_LIMIT = 65535;
|
||
|
|
||
|
public float size
|
||
|
{
|
||
|
get { return _size; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _size)
|
||
|
{
|
||
|
_size = value;
|
||
|
Rebuild();
|
||
|
} else _size = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Color color
|
||
|
{
|
||
|
get { return _color; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _color)
|
||
|
{
|
||
|
_color = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Vector3 offset
|
||
|
{
|
||
|
get { return _offset; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _offset)
|
||
|
{
|
||
|
_offset = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public NormalMethod normalMethod
|
||
|
{
|
||
|
get { return _normalMethod; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _normalMethod)
|
||
|
{
|
||
|
_normalMethod = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool useSplineSize
|
||
|
{
|
||
|
get { return _useSplineSize; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _useSplineSize)
|
||
|
{
|
||
|
_useSplineSize = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool useSplineColor
|
||
|
{
|
||
|
get { return _useSplineColor; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _useSplineColor)
|
||
|
{
|
||
|
_useSplineColor = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool calculateTangents
|
||
|
{
|
||
|
get { return _calculateTangents; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _calculateTangents)
|
||
|
{
|
||
|
_calculateTangents = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public float rotation
|
||
|
{
|
||
|
get { return _rotation; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _rotation)
|
||
|
{
|
||
|
_rotation = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool flipFaces
|
||
|
{
|
||
|
get { return _flipFaces; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _flipFaces)
|
||
|
{
|
||
|
_flipFaces = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool doubleSided
|
||
|
{
|
||
|
get { return _doubleSided; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _doubleSided)
|
||
|
{
|
||
|
_doubleSided = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public UVMode uvMode
|
||
|
{
|
||
|
get { return _uvMode; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _uvMode)
|
||
|
{
|
||
|
_uvMode = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Vector2 uvScale
|
||
|
{
|
||
|
get { return _uvScale; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _uvScale)
|
||
|
{
|
||
|
_uvScale = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Vector2 uvOffset
|
||
|
{
|
||
|
get { return _uvOffset; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _uvOffset)
|
||
|
{
|
||
|
_uvOffset = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public float uvRotation
|
||
|
{
|
||
|
get { return _uvRotation; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _uvRotation)
|
||
|
{
|
||
|
_uvRotation = value;
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public UnityEngine.Rendering.IndexFormat meshIndexFormat
|
||
|
{
|
||
|
get { return _meshIndexFormat; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _meshIndexFormat)
|
||
|
{
|
||
|
_meshIndexFormat = value;
|
||
|
RefreshMesh();
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool baked
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _baked;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool markDynamic
|
||
|
{
|
||
|
get { return _markDynamic; }
|
||
|
set
|
||
|
{
|
||
|
if (value != _markDynamic)
|
||
|
{
|
||
|
_markDynamic = value;
|
||
|
RefreshMesh();
|
||
|
Rebuild();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public enum UVMode { Clip, UniformClip, Clamp, UniformClamp }
|
||
|
public enum NormalMethod { Recalculate, SplineNormals }
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private bool _baked = false;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private bool _markDynamic = true;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private float _size = 1f;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private Color _color = Color.white;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private Vector3 _offset = Vector3.zero;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private NormalMethod _normalMethod = NormalMethod.SplineNormals;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private bool _calculateTangents = true;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private bool _useSplineSize = true;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private bool _useSplineColor = true;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
[Range(-360f, 360f)]
|
||
|
private float _rotation = 0f;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private bool _flipFaces = false;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private bool _doubleSided = false;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private UVMode _uvMode = UVMode.Clip;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private Vector2 _uvScale = Vector2.one;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private Vector2 _uvOffset = Vector2.zero;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private float _uvRotation = 0f;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private UnityEngine.Rendering.IndexFormat _meshIndexFormat = UnityEngine.Rendering.IndexFormat.UInt16;
|
||
|
[SerializeField]
|
||
|
[HideInInspector]
|
||
|
private Mesh _bakedMesh;
|
||
|
|
||
|
[HideInInspector]
|
||
|
public float colliderUpdateRate = 0.2f;
|
||
|
protected bool _updateCollider = false;
|
||
|
protected float _lastUpdateTime = 0f;
|
||
|
|
||
|
private float _vDist = 0f;
|
||
|
protected static Vector2 __uvs = Vector2.zero;
|
||
|
|
||
|
protected virtual string meshName => "Mesh";
|
||
|
protected TS_Mesh _tsMesh { get; private set; }
|
||
|
protected Mesh _mesh;
|
||
|
|
||
|
protected MeshFilter filter;
|
||
|
protected MeshRenderer meshRenderer;
|
||
|
protected MeshCollider meshCollider;
|
||
|
|
||
|
#if UNITY_EDITOR
|
||
|
|
||
|
public void Bake(bool makeStatic, bool lightmapUV)
|
||
|
{
|
||
|
if (_mesh == null) return;
|
||
|
gameObject.isStatic = false;
|
||
|
UnityEditor.MeshUtility.Optimize(_mesh);
|
||
|
if (spline != null)
|
||
|
{
|
||
|
spline.Unsubscribe(this);
|
||
|
}
|
||
|
filter = GetComponent<MeshFilter>();
|
||
|
meshRenderer = GetComponent<MeshRenderer>();
|
||
|
filter.hideFlags = meshRenderer.hideFlags = HideFlags.None;
|
||
|
_bakedMesh = Instantiate(_mesh);
|
||
|
_bakedMesh.name = meshName + " - Baked";
|
||
|
if (lightmapUV)
|
||
|
{
|
||
|
Unwrapping.GenerateSecondaryUVSet(_bakedMesh);
|
||
|
}
|
||
|
filter.sharedMesh = _bakedMesh;
|
||
|
_mesh = null;
|
||
|
gameObject.isStatic = makeStatic;
|
||
|
_baked = true;
|
||
|
}
|
||
|
|
||
|
public void Unbake()
|
||
|
{
|
||
|
gameObject.isStatic = false;
|
||
|
_baked = false;
|
||
|
DestroyImmediate(_bakedMesh);
|
||
|
_bakedMesh = null;
|
||
|
CreateMesh();
|
||
|
spline.Subscribe(this);
|
||
|
Rebuild();
|
||
|
}
|
||
|
|
||
|
public override void EditorAwake()
|
||
|
{
|
||
|
GetComponents();
|
||
|
base.EditorAwake();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
protected override void Awake()
|
||
|
{
|
||
|
GetComponents();
|
||
|
base.Awake();
|
||
|
}
|
||
|
|
||
|
protected override void Reset()
|
||
|
{
|
||
|
base.Reset();
|
||
|
GetComponents();
|
||
|
#if UNITY_EDITOR
|
||
|
bool materialFound = false;
|
||
|
for (int i = 0; i < meshRenderer.sharedMaterials.Length; i++)
|
||
|
{
|
||
|
if (meshRenderer.sharedMaterials[i] != null)
|
||
|
{
|
||
|
materialFound = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!materialFound) meshRenderer.sharedMaterial = AssetDatabase.GetBuiltinExtraResource<Material>("Default-Diffuse.mat");
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
private void GetComponents()
|
||
|
{
|
||
|
filter = GetComponent<MeshFilter>();
|
||
|
meshRenderer = GetComponent<MeshRenderer>();
|
||
|
meshCollider = GetComponent<MeshCollider>();
|
||
|
}
|
||
|
|
||
|
public override void Rebuild()
|
||
|
{
|
||
|
if (_baked) return;
|
||
|
base.Rebuild();
|
||
|
}
|
||
|
|
||
|
public override void RebuildImmediate()
|
||
|
{
|
||
|
if (_baked) return;
|
||
|
base.RebuildImmediate();
|
||
|
}
|
||
|
|
||
|
protected override void OnEnable()
|
||
|
{
|
||
|
base.OnEnable();
|
||
|
}
|
||
|
|
||
|
protected override void OnDisable()
|
||
|
{
|
||
|
base.OnDisable();
|
||
|
}
|
||
|
|
||
|
protected override void OnDestroy()
|
||
|
{
|
||
|
base.OnDestroy();
|
||
|
MeshFilter filter = GetComponent<MeshFilter>();
|
||
|
MeshRenderer rend = GetComponent<MeshRenderer>();
|
||
|
if (filter != null) filter.hideFlags = HideFlags.None;
|
||
|
if (rend != null) rend.hideFlags = HideFlags.None;
|
||
|
}
|
||
|
|
||
|
|
||
|
public void UpdateCollider()
|
||
|
{
|
||
|
meshCollider = GetComponent<MeshCollider>();
|
||
|
if (meshCollider == null) meshCollider = gameObject.AddComponent<MeshCollider>();
|
||
|
meshCollider.sharedMesh = filter.sharedMesh;
|
||
|
}
|
||
|
|
||
|
protected override void LateRun()
|
||
|
{
|
||
|
if (_baked) return;
|
||
|
base.LateRun();
|
||
|
if (_updateCollider)
|
||
|
{
|
||
|
if (meshCollider != null)
|
||
|
{
|
||
|
if (Time.time - _lastUpdateTime >= colliderUpdateRate)
|
||
|
{
|
||
|
_lastUpdateTime = Time.time;
|
||
|
_updateCollider = false;
|
||
|
meshCollider.sharedMesh = filter.sharedMesh;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override void Build()
|
||
|
{
|
||
|
base.Build();
|
||
|
if (_tsMesh == null || _mesh == null)
|
||
|
{
|
||
|
CreateMesh();
|
||
|
}
|
||
|
|
||
|
if (sampleCount > 1)
|
||
|
{
|
||
|
BuildMesh();
|
||
|
} else
|
||
|
{
|
||
|
ClearMesh();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected override void PostBuild()
|
||
|
{
|
||
|
base.PostBuild();
|
||
|
WriteMesh();
|
||
|
}
|
||
|
|
||
|
protected virtual void ClearMesh()
|
||
|
{
|
||
|
_tsMesh.Clear();
|
||
|
_mesh.Clear();
|
||
|
}
|
||
|
|
||
|
protected virtual void BuildMesh()
|
||
|
{
|
||
|
//Logic for mesh generation, automatically called in the Build method
|
||
|
}
|
||
|
|
||
|
protected virtual void WriteMesh()
|
||
|
{
|
||
|
MeshUtility.TransformMesh(_tsMesh, trs.worldToLocalMatrix);
|
||
|
if (_doubleSided)
|
||
|
{
|
||
|
MeshUtility.MakeDoublesidedHalf(_tsMesh);
|
||
|
}
|
||
|
else if (_flipFaces)
|
||
|
{
|
||
|
MeshUtility.FlipFaces(_tsMesh);
|
||
|
}
|
||
|
|
||
|
if (_calculateTangents)
|
||
|
{
|
||
|
MeshUtility.CalculateTangents(_tsMesh);
|
||
|
}
|
||
|
|
||
|
if (_meshIndexFormat == UnityEngine.Rendering.IndexFormat.UInt16 && _tsMesh.vertexCount > UNITY_16_VERTEX_LIMIT)
|
||
|
{
|
||
|
Debug.LogError("WARNING: The generated mesh for " + name + " exceeds the maximum vertex count for standard meshes in Unity (" + UNITY_16_VERTEX_LIMIT + "). To create bigger meshes, set the Index Format inside the Vertices foldout to 32.");
|
||
|
}
|
||
|
|
||
|
_tsMesh.indexFormat = _meshIndexFormat;
|
||
|
|
||
|
_tsMesh.WriteMesh(ref _mesh);
|
||
|
|
||
|
if (_markDynamic)
|
||
|
{
|
||
|
_mesh.MarkDynamic();
|
||
|
}
|
||
|
|
||
|
if (_normalMethod == 0)
|
||
|
{
|
||
|
_mesh.RecalculateNormals();
|
||
|
}
|
||
|
|
||
|
if (filter != null)
|
||
|
{
|
||
|
filter.sharedMesh = _mesh;
|
||
|
}
|
||
|
_updateCollider = true;
|
||
|
}
|
||
|
|
||
|
protected virtual void AllocateMesh(int vertexCount, int trisCount)
|
||
|
{
|
||
|
if(trisCount < 0)
|
||
|
{
|
||
|
trisCount = 0;
|
||
|
}
|
||
|
if(vertexCount < 0)
|
||
|
{
|
||
|
vertexCount = 0;
|
||
|
}
|
||
|
if (_doubleSided)
|
||
|
{
|
||
|
vertexCount *= 2;
|
||
|
trisCount *= 2;
|
||
|
}
|
||
|
if (_tsMesh.vertexCount != vertexCount)
|
||
|
{
|
||
|
_tsMesh.vertices = new Vector3[vertexCount];
|
||
|
_tsMesh.normals = new Vector3[vertexCount];
|
||
|
_tsMesh.tangents = new Vector4[vertexCount];
|
||
|
_tsMesh.colors = new Color[vertexCount];
|
||
|
_tsMesh.uv = new Vector2[vertexCount];
|
||
|
}
|
||
|
if (_tsMesh.triangles.Length != trisCount)
|
||
|
{
|
||
|
_tsMesh.triangles = new int[trisCount];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void ResetUVDistance()
|
||
|
{
|
||
|
_vDist = 0f;
|
||
|
if (uvMode == UVMode.UniformClip)
|
||
|
{
|
||
|
_vDist = spline.CalculateLength(0.0, GetSamplePercent(0));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void AddUVDistance(int sampleIndex)
|
||
|
{
|
||
|
if (sampleIndex == 0) return;
|
||
|
SplineSample current = new SplineSample();
|
||
|
SplineSample last = new SplineSample();
|
||
|
GetSampleRaw(sampleIndex, ref current);
|
||
|
GetSampleRaw(sampleIndex - 1, ref last);
|
||
|
_vDist += Vector3.Distance(current.position, last.position);
|
||
|
}
|
||
|
|
||
|
protected void CalculateUVs(double percent, float u)
|
||
|
{
|
||
|
__uvs.x = u * _uvScale.x - _uvOffset.x;
|
||
|
switch (uvMode)
|
||
|
{
|
||
|
case UVMode.Clip: __uvs.y = (float)percent * _uvScale.y - _uvOffset.y; break;
|
||
|
case UVMode.Clamp: __uvs.y = (float)DMath.InverseLerp(clipFrom, clipTo, percent) * _uvScale.y - _uvOffset.y; break;
|
||
|
case UVMode.UniformClamp: __uvs.y = _vDist * _uvScale.y / (float)span - _uvOffset.y; break;
|
||
|
default: __uvs.y = _vDist * _uvScale.y - _uvOffset.y; break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected float GetBaseSize(SplineSample sample)
|
||
|
{
|
||
|
return _useSplineSize? sample.size: 1f;
|
||
|
}
|
||
|
|
||
|
protected Color GetBaseColor(SplineSample sample)
|
||
|
{
|
||
|
return _useSplineColor ? sample.color : Color.white;
|
||
|
}
|
||
|
|
||
|
protected virtual void CreateMesh()
|
||
|
{
|
||
|
_tsMesh = new TS_Mesh();
|
||
|
_mesh = new Mesh();
|
||
|
_mesh.name = meshName;
|
||
|
_mesh.indexFormat = _meshIndexFormat;
|
||
|
_tsMesh.indexFormat = _meshIndexFormat;
|
||
|
if (_markDynamic)
|
||
|
{
|
||
|
_mesh.MarkDynamic();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void RefreshMesh()
|
||
|
{
|
||
|
if (!Application.isPlaying)
|
||
|
{
|
||
|
DestroyImmediate(_mesh);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Destroy(_mesh);
|
||
|
}
|
||
|
_mesh = null;
|
||
|
_tsMesh.Clear();
|
||
|
_tsMesh = null;
|
||
|
CreateMesh();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|