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(); meshRenderer = GetComponent(); 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("Default-Diffuse.mat"); #endif } private void GetComponents() { filter = GetComponent(); meshRenderer = GetComponent(); meshCollider = GetComponent(); } 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(); MeshRenderer rend = GetComponent(); if (filter != null) filter.hideFlags = HideFlags.None; if (rend != null) rend.hideFlags = HideFlags.None; } public void UpdateCollider() { meshCollider = GetComponent(); if (meshCollider == null) meshCollider = gameObject.AddComponent(); 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(); } } }