2025-01-26 21:10:16 -05:00
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 ( ) ;
2025-05-04 14:09:02 +08:00
}
else _size = value ;
2025-01-26 21:10:16 -05:00
}
}
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 ;
protected 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 ;
2025-05-04 14:09:02 +08:00
gameObject . isStatic = makeStatic ;
2025-01-26 21:10:16 -05:00
_baked = true ;
}
public void Unbake ( )
{
2025-05-04 14:09:02 +08:00
gameObject . isStatic = false ;
2025-01-26 21:10:16 -05:00
_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 > ( ) ;
2025-05-04 14:09:02 +08:00
if ( filter ! = null ) filter . hideFlags = HideFlags . None ;
if ( rend ! = null ) rend . hideFlags = HideFlags . None ;
2025-01-26 21:10:16 -05:00
}
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 ( ) ;
2025-05-04 14:09:02 +08:00
}
else
2025-01-26 21:10:16 -05:00
{
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
}
2025-05-04 14:09:02 +08:00
protected virtual void WriteMesh ( )
2025-01-26 21:10:16 -05:00
{
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 )
{
2025-05-04 14:09:02 +08:00
if ( trisCount < 0 )
2025-01-26 21:10:16 -05:00
{
trisCount = 0 ;
}
2025-05-04 14:09:02 +08:00
if ( vertexCount < 0 )
2025-01-26 21:10:16 -05:00
{
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 )
{
2025-05-04 14:09:02 +08:00
case UVMode . Clip : __uvs . y = CalculateUVClip ( percent ) ; break ;
case UVMode . Clamp : __uvs . y = CalculateUVClamp ( percent ) ; break ;
2025-01-26 21:10:16 -05:00
case UVMode . UniformClamp : __uvs . y = CalculateUVUniformClamp ( _vDist ) ; break ;
default : __uvs . y = CalculateUVUniformClip ( _vDist ) ; break ;
}
}
protected float CalculateUVUniformClamp ( float distance )
{
return distance * _uvScale . y / ( float ) span - _uvOffset . y ;
}
protected float CalculateUVUniformClip ( float distance )
{
return distance * _uvScale . y - _uvOffset . y ;
}
protected float CalculateUVClip ( double percent )
{
return ( float ) percent * _uvScale . y - _uvOffset . y ;
}
protected float CalculateUVClamp ( double percent )
{
return ( float ) DMath . InverseLerp ( clipFrom , clipTo , percent ) * _uvScale . y - _uvOffset . y ;
}
protected float GetBaseSize ( SplineSample sample )
{
2025-05-04 14:09:02 +08:00
return _useSplineSize ? sample . size : 1f ;
2025-01-26 21:10:16 -05:00
}
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 ) ;
2025-05-04 14:09:02 +08:00
}
2025-01-26 21:10:16 -05:00
else
{
Destroy ( _mesh ) ;
}
_mesh = null ;
_tsMesh . Clear ( ) ;
_tsMesh = null ;
CreateMesh ( ) ;
}
}
2025-05-04 14:09:02 +08:00
2025-01-26 21:10:16 -05:00
}