rabidus-test/Assets/Dreamteck/Splines/Components/ObjectBender.cs

814 lines
32 KiB
C#
Raw Permalink Normal View History

2023-07-24 16:38:13 +03:00
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace Dreamteck.Splines
{
[AddComponentMenu("Dreamteck/Splines/Users/Object Bender")]
public class ObjectBender : SplineUser
{
public enum Axis { X, Y, Z }
public enum NormalMode { Spline, Auto, Custom }
public enum ForwardMode { Spline, Custom }
public bool bend
{
get { return _bend; }
set
{
if(_bend != value)
{
_bend = value;
if (value)
{
UpdateReferences();
Rebuild();
} else Revert();
}
}
}
[SerializeField]
[HideInInspector]
private bool _bend = false;
public Axis axis
{
get { return _axis; }
set
{
if (spline != null && value != _axis)
{
_axis = value;
UpdateReferences();
Rebuild();
}
else _axis = value;
}
}
public NormalMode upMode
{
get { return _normalMode; }
set
{
if (spline != null && value != _normalMode)
{
_normalMode = value;
Rebuild();
} else _normalMode = value;
}
}
public Vector3 customNormal
{
get { return _customNormal; }
set
{
if (spline != null && value != _customNormal)
{
_customNormal = value;
Rebuild();
}
else _customNormal = value;
}
}
public ForwardMode forwardMode
{
get { return _forwardMode; }
set
{
if (spline != null && value != _forwardMode)
{
_forwardMode = value;
Rebuild();
} else _forwardMode = value;
}
}
public Vector3 customForward
{
get { return _customForward; }
set
{
if (spline != null && value != _customForward)
{
_customForward = value;
Rebuild();
}
else _customForward = value;
}
}
[HideInInspector]
public BendProperty[] bendProperties = new BendProperty[0];
[SerializeField]
[HideInInspector]
private bool _parentIsTheSpline = false;
[SerializeField]
[HideInInspector]
private TS_Bounds bounds = null;
[SerializeField]
[HideInInspector]
private Axis _axis = Axis.Z;
[SerializeField]
[HideInInspector]
private NormalMode _normalMode = NormalMode.Auto;
[SerializeField]
[HideInInspector]
private ForwardMode _forwardMode = ForwardMode.Spline;
[SerializeField]
[HideInInspector]
[UnityEngine.Serialization.FormerlySerializedAs("_upVector")]
private Vector3 _customNormal = Vector3.up;
[SerializeField]
[HideInInspector]
private Vector3 _customForward = Vector3.forward;
Matrix4x4 normalMatrix = new Matrix4x4();
Quaternion bendRotation = Quaternion.identity;
private void GetTransformsRecursively(Transform current, ref List<Transform> transformList)
{
transformList.Add(current);
foreach (Transform child in current)
{
GetTransformsRecursively(child, ref transformList);
}
}
private void GetObjects()
{
List<Transform> found = new List<Transform>();
GetTransformsRecursively(transform, ref found);
BendProperty[] newProperties = new BendProperty[found.Count];
for (int i = 0; i < found.Count; i++)
{
CreateProperty(ref newProperties[i], found[i]);
}
bendProperties = newProperties;
SplineComputer splineComponent = GetComponent<SplineComputer>();
_parentIsTheSpline = splineComponent == spline;
}
public TS_Bounds GetBounds()
{
return new TS_Bounds(bounds.min, bounds.max, bounds.center);
}
#if UNITY_EDITOR
public void EditorGenerateLightmapUVs()
{
for (int i = 0; i < bendProperties.Length; i++)
{
if (bendProperties[i].bendMesh)
{
if (bendProperties[i].filter == null) continue;
if (bendProperties[i].filter.sharedMesh == null) continue;
EditorUtility.DisplayProgressBar("Generating Lightmap UVS", bendProperties[i].filter.sharedMesh.name, (float)i / (bendProperties.Length - 1));
Unwrapping.GenerateSecondaryUVSet(bendProperties[i].filter.sharedMesh);
}
}
EditorUtility.ClearProgressBar();
}
#endif
private void CreateProperty(ref BendProperty property, Transform t)
{
property = new BendProperty(t, t == transform); //Create a new bend property for each child
for (int i = 0; i < bendProperties.Length; i++)
{
//Search for properties that have the same trasform and copy their settings
if (bendProperties[i].transform.transform == t)
{
property.enabled = bendProperties[i].enabled;
property.applyRotation = bendProperties[i].applyRotation;
property.applyScale = bendProperties[i].applyScale;
property.bendMesh = bendProperties[i].bendMesh;
property.bendCollider = bendProperties[i].bendCollider;
property.generateLightmapUVs = bendProperties[i].generateLightmapUVs;
property.colliderUpdateRate = bendProperties[i].colliderUpdateRate;
break;
}
}
if (t.transform != trs)
{
property.originalPosition = trs.InverseTransformPoint(t.position);
property.originalRotation = Quaternion.Inverse(trs.rotation) * t.rotation;
}
}
private void CalculateBounds()
{
if (bounds == null) bounds = new TS_Bounds(Vector3.zero, Vector3.zero);
bounds.min = bounds.max = Vector3.zero;
for (int i = 0; i < bendProperties.Length; i++)
{
CalculatePropertyBounds(ref bendProperties[i]);
}
for (int i = 0; i < bendProperties.Length; i++)
{
CalculatePercents(bendProperties[i]);
}
}
private void CalculatePropertyBounds(ref BendProperty property)
{
if (!property.enabled) return;
if (property.isParent && _parentIsTheSpline) return;
if (property.transform.transform == trs)
{
if (0f < bounds.min.x) bounds.min.x = 0f;
if (0f < bounds.min.y) bounds.min.y = 0f;
if (0f < bounds.min.z) bounds.min.z = 0f;
if (0f > bounds.max.x) bounds.max.x = 0f;
if (0f > bounds.max.y) bounds.max.y = 0f;
if (0f > bounds.max.z) bounds.max.z = 0f;
}
else
{
if (property.originalPosition.x < bounds.min.x) bounds.min.x = property.originalPosition.x;
if (property.originalPosition.y < bounds.min.y) bounds.min.y = property.originalPosition.y;
if (property.originalPosition.z < bounds.min.z) bounds.min.z = property.originalPosition.z;
if (property.originalPosition.x > bounds.max.x) bounds.max.x = property.originalPosition.x;
if (property.originalPosition.y > bounds.max.y) bounds.max.y = property.originalPosition.y;
if (property.originalPosition.z > bounds.max.z) bounds.max.z = property.originalPosition.z;
}
if (property.editMesh != null)
{
for (int n = 0; n < property.editMesh.vertices.Length; n++)
{
Vector3 localPos = property.transform.TransformPoint(property.editMesh.vertices[n]);
localPos = trs.InverseTransformPoint(localPos);
if (localPos.x < bounds.min.x) bounds.min.x = localPos.x;
if (localPos.y < bounds.min.y) bounds.min.y = localPos.y;
if (localPos.z < bounds.min.z) bounds.min.z = localPos.z;
if (localPos.x > bounds.max.x) bounds.max.x = localPos.x;
if (localPos.y > bounds.max.y) bounds.max.y = localPos.y;
if (localPos.z > bounds.max.z) bounds.max.z = localPos.z;
}
}
if (property.editColliderMesh != null)
{
for (int n = 0; n < property.editColliderMesh.vertices.Length; n++)
{
Vector3 localPos = property.transform.TransformPoint(property.editColliderMesh.vertices[n]);
localPos = trs.InverseTransformPoint(localPos);
if (localPos.x < bounds.min.x) bounds.min.x = localPos.x;
if (localPos.y < bounds.min.y) bounds.min.y = localPos.y;
if (localPos.z < bounds.min.z) bounds.min.z = localPos.z;
if (localPos.x > bounds.max.x) bounds.max.x = localPos.x;
if (localPos.y > bounds.max.y) bounds.max.y = localPos.y;
if (localPos.z > bounds.max.z) bounds.max.z = localPos.z;
}
}
if (property.originalSpline != null)
{
for (int n = 0; n < property.originalSpline.points.Length; n++)
{
Vector3 localPos = trs.InverseTransformPoint(property.originalSpline.points[n].position);
if (localPos.x < bounds.min.x) bounds.min.x = localPos.x;
if (localPos.y < bounds.min.y) bounds.min.y = localPos.y;
if (localPos.z < bounds.min.z) bounds.min.z = localPos.z;
if (localPos.x > bounds.max.x) bounds.max.x = localPos.x;
if (localPos.y > bounds.max.y) bounds.max.y = localPos.y;
if (localPos.z > bounds.max.z) bounds.max.z = localPos.z;
}
}
bounds.CreateFromMinMax(bounds.min, bounds.max);
}
public void CalculatePercents(BendProperty property)
{
if (property.transform.transform != trs) property.positionPercent = GetPercentage(trs.InverseTransformPoint(property.transform.position));
else property.positionPercent = GetPercentage(Vector3.zero);
if (property.editMesh != null)
{
if (property.vertexPercents.Length != property.editMesh.vertexCount) property.vertexPercents = new Vector3[property.editMesh.vertexCount];
if (property.editColliderMesh != null)
{
if (property.colliderVertexPercents.Length != property.editMesh.vertexCount) property.colliderVertexPercents = new Vector3[property.editColliderMesh.vertexCount];
}
for (int i = 0; i < property.editMesh.vertexCount; i++)
{
Vector3 localVertex = property.transform.TransformPoint(property.editMesh.vertices[i]);
localVertex = trs.InverseTransformPoint(localVertex);
property.vertexPercents[i] = GetPercentage(localVertex);
}
if (property.editColliderMesh != null)
{
for (int i = 0; i < property.editColliderMesh.vertexCount; i++)
{
Vector3 localVertex = property.transform.TransformPoint(property.editColliderMesh.vertices[i]);
localVertex = trs.InverseTransformPoint(localVertex);
property.colliderVertexPercents[i] = GetPercentage(localVertex);
}
}
}
if (property.splineComputer != null)
{
SplinePoint[] points = property.splineComputer.GetPoints();
property.splinePointPercents = new Vector3[points.Length];
property.primaryTangentPercents = new Vector3[points.Length];
property.secondaryTangentPercents = new Vector3[points.Length];
for (int i = 0; i < points.Length; i++)
{
property.splinePointPercents[i] = GetPercentage(trs.InverseTransformPoint(points[i].position));
property.primaryTangentPercents[i] = GetPercentage(trs.InverseTransformPoint(points[i].tangent));
property.secondaryTangentPercents[i] = GetPercentage(trs.InverseTransformPoint(points[i].tangent2));
}
}
}
private void Revert()
{
for (int i = 0; i < bendProperties.Length; i++)
{
bendProperties[i].Revert();
}
}
public void UpdateReferences()
{
if (!hasTransform)
{
CacheTransform();
}
if (_bend)
{
for (int i = 0; i < bendProperties.Length; i++) bendProperties[i].Revert();
}
GetObjects();
CalculateBounds();
if (_bend)
{
Bend();
for (int i = 0; i < bendProperties.Length; i++)
{
bendProperties[i].Apply(i > 0 || trs != spline.transform);
bendProperties[i].Update();
}
}
}
private void GetevalResult(Vector3 percentage)
{
switch (axis)
{
case Axis.X: Evaluate(percentage.x, ref evalResult); break;
case Axis.Y: Evaluate(percentage.y, ref evalResult); break;
case Axis.Z: Evaluate(percentage.z, ref evalResult); break;
}
switch (_normalMode)
{
case NormalMode.Auto: evalResult.up = Vector3.Cross(evalResult.forward, evalResult.right); break;
case NormalMode.Custom: evalResult.up = _customNormal; break;
}
if (_forwardMode == ForwardMode.Custom) evalResult.forward = customForward;
ModifySample(ref evalResult);
Vector3 right = evalResult.right;
Quaternion axisRotation = Quaternion.identity;
switch (axis)
{
case Axis.Z:
evalResult.position += right * Mathf.Lerp(bounds.min.x, bounds.max.x, percentage.x) * evalResult.size;
evalResult.position += evalResult.up * Mathf.Lerp(bounds.min.y, bounds.max.y, percentage.y) * evalResult.size;
break;
case Axis.X:
axisRotation = Quaternion.Euler(0f, -90f, 0f);
evalResult.position += right * Mathf.Lerp(bounds.max.z, bounds.min.z, percentage.z) * evalResult.size;
evalResult.position += evalResult.up * Mathf.Lerp(bounds.min.y, bounds.max.y, percentage.y) * evalResult.size;
break;
case Axis.Y:
axisRotation = Quaternion.Euler(90f, 0f, 0f);
evalResult.position += right * Mathf.Lerp(bounds.min.x, bounds.max.x, percentage.x) * evalResult.size;
evalResult.position += evalResult.up * Mathf.Lerp(bounds.min.z, bounds.max.z, percentage.z) * evalResult.size;
break;
}
bendRotation = evalResult.rotation * axisRotation;
normalMatrix = Matrix4x4.TRS(evalResult.position, bendRotation, Vector3.one * evalResult.size).inverse.transpose;
}
private Vector3 GetPercentage(Vector3 point)
{
point.x = Mathf.InverseLerp(bounds.min.x, bounds.max.x, point.x);
point.y = Mathf.InverseLerp(bounds.min.y, bounds.max.y, point.y);
point.z = Mathf.InverseLerp(bounds.min.z, bounds.max.z, point.z);
return point;
}
protected override void Build()
{
base.Build();
if (_bend) Bend();
}
private void Bend()
{
if (sampleCount <= 1) return;
if (bendProperties.Length == 0) return;
for (int i = 0; i < bendProperties.Length; i++)
{
BendObject(bendProperties[i]);
}
}
public void BendObject(BendProperty p)
{
if (!p.enabled) return;
if (p.isParent && _parentIsTheSpline) return;
GetevalResult(p.positionPercent);
p.transform.position = evalResult.position;
if (p.applyRotation)
{
//p.transform.rotation = evalResult.rotation * axisRotation * p.originalRotation;
p.transform.rotation = bendRotation * (Quaternion.Inverse(p.parentRotation) * p.originalRotation);
} else p.transform.rotation = p.originalRotation;
if (p.applyScale) p.transform.scale = p.originalScale * evalResult.size;
Matrix4x4 toLocalMatrix = Matrix4x4.TRS(p.transform.position, p.transform.rotation, p.transform.scale).inverse;
if (p.editMesh != null)
{
BendMesh(p.vertexPercents, p.normals, p.editMesh, toLocalMatrix);
p.editMesh.hasUpdate = true;
}
if (p._editColliderMesh != null)
{
BendMesh(p.colliderVertexPercents, p.colliderNormals, p.editColliderMesh, toLocalMatrix);
p.editColliderMesh.hasUpdate = true;
}
if (p.originalSpline != null && !p.isParent)
{
for (int n = 0; n < p.splinePointPercents.Length; n++)
{
SplinePoint point = p.originalSpline.points[n];
GetevalResult(p.splinePointPercents[n]);
point.position = evalResult.position;
GetevalResult(p.primaryTangentPercents[n]);
point.tangent = evalResult.position;
GetevalResult(p.secondaryTangentPercents[n]);
point.tangent2 = evalResult.position;
switch (axis)
{
case Axis.X: point.normal = Quaternion.LookRotation(evalResult.forward, evalResult.up) * Quaternion.FromToRotation(Vector3.up, evalResult.up) * point.normal; break;
case Axis.Y: point.normal = Quaternion.LookRotation(evalResult.forward, evalResult.up) * Quaternion.FromToRotation(Vector3.up, evalResult.up) * point.normal; break;
case Axis.Z: point.normal = Quaternion.LookRotation(evalResult.forward, evalResult.up) * point.normal; break;
}
p.destinationSpline.points[n] = point;
}
}
}
void BendMesh(Vector3[] vertexPercents, Vector3[] originalNormals, TS_Mesh mesh, Matrix4x4 worldToLocalMatrix)
{
if(mesh.vertexCount != vertexPercents.Length)
{
Debug.LogError("Vertex count mismatch");
return;
}
for (int i = 0; i < mesh.vertexCount; i++)
{
Vector3 percent = vertexPercents[i];
if (axis == Axis.Y) percent.z = 1f - percent.z;
GetevalResult(percent);
mesh.vertices[i] = worldToLocalMatrix.MultiplyPoint3x4(evalResult.position);
mesh.normals[i] = worldToLocalMatrix.MultiplyVector(normalMatrix.MultiplyVector(originalNormals[i]));
}
}
protected override void PostBuild()
{
base.PostBuild();
if (!_bend) return;
for (int i = 0; i < bendProperties.Length; i++)
{
bendProperties[i].Apply(i > 0 || trs != spline.transform);
bendProperties[i].Update();
}
}
protected override void LateRun()
{
base.LateRun();
for (int i = 0; i < bendProperties.Length; i++)
{
bendProperties[i].Update();
}
}
[System.Serializable]
public class BendProperty
{
public bool enabled = true;
public bool isValid
{
get
{
return transform != null && transform.transform != null;
}
}
public TS_Transform transform;
public bool applyRotation = true;
public bool applyScale = true;
public bool bendMesh
{
get { return _bendMesh; }
set
{
if (value != _bendMesh)
{
_bendMesh = value;
if (value)
{
if (filter != null && filter.sharedMesh != null)
{
normals = originalMesh.normals;
for (int i = 0; i < normals.Length; i++) normals[i] = transform.transform.TransformDirection(normals[i]);
}
} else RevertMesh();
}
}
}
public bool generateLightmapUVs = false;
public bool bendCollider
{
get { return _bendCollider; }
set
{
if (value != _bendCollider)
{
_bendCollider = value;
if (value)
{
if (collider != null && collider.sharedMesh != null && collider.sharedMesh != originalMesh) colliderNormals = originalColliderMesh.normals;
}
else RevertCollider();
}
}
}
public bool bendSpline
{
get { return _bendSpline; }
set
{
_bendSpline = value;
if (value)
{
}
}
}
[SerializeField]
[HideInInspector]
private bool _bendMesh = true;
[SerializeField]
[HideInInspector]
private bool _bendSpline = true;
[SerializeField]
[HideInInspector]
private bool _bendCollider = true;
private float colliderUpdateDue = 0f;
public float colliderUpdateRate = 0.2f;
private bool updateCollider = false;
public Vector3 originalPosition = Vector3.zero;
public Vector3 originalScale = Vector3.one;
public Quaternion originalRotation = Quaternion.identity;
public Quaternion parentRotation = Quaternion.identity;
public Vector3 positionPercent;
public Vector3[] vertexPercents = new Vector3[0];
public Vector3[] normals = new Vector3[0];
public Vector3[] colliderVertexPercents = new Vector3[0];
public Vector3[] colliderNormals = new Vector3[0];
[SerializeField]
[HideInInspector]
private Mesh originalMesh = null;
[SerializeField]
[HideInInspector]
private Mesh originalColliderMesh = null;
private Spline _originalSpline;
[SerializeField]
[HideInInspector]
private Mesh destinationMesh = null;
[SerializeField]
[HideInInspector]
private Mesh destinationColliderMesh = null;
public Spline destinationSpline;
public TS_Mesh editMesh
{
get
{
if (!bendMesh || originalMesh == null) _editMesh = null;
else if (_editMesh == null && originalMesh != null) _editMesh = new TS_Mesh(originalMesh);
return _editMesh;
}
}
public TS_Mesh editColliderMesh
{
get
{
if (!bendCollider || originalColliderMesh == null) _editColliderMesh = null;
else if (_editColliderMesh == null && originalColliderMesh != null && originalColliderMesh != originalMesh) _editColliderMesh = new TS_Mesh(originalColliderMesh);
return _editColliderMesh;
}
}
public Spline originalSpline
{
get
{
if (!bendSpline || splineComputer == null) _originalSpline = null;
else if (_originalSpline == null && splineComputer != null) {
_originalSpline = new Spline(splineComputer.type);
_originalSpline.points = splineComputer.GetPoints();
}
return _originalSpline;
}
}
public TS_Mesh _editMesh = null;
public TS_Mesh _editColliderMesh = null;
public MeshFilter filter = null;
public MeshCollider collider = null;
public SplineComputer splineComputer = null;
public Vector3[] splinePointPercents = new Vector3[0];
public Vector3[] primaryTangentPercents = new Vector3[0];
public Vector3[] secondaryTangentPercents = new Vector3[0];
[SerializeField]
[HideInInspector]
private bool parent = false;
public bool isParent {
get { return parent; }
}
public BendProperty(Transform t, bool parent = false)
{
this.parent = parent;
transform = new TS_Transform(t);
originalPosition = t.localPosition;
originalScale = t.localScale;
originalRotation = t.localRotation;
parentRotation = t.transform.rotation;
if (t.transform.parent != null) parentRotation = t.transform.parent.rotation;
filter = t.GetComponent<MeshFilter>();
collider = t.GetComponent<MeshCollider>();
if (filter != null && filter.sharedMesh != null)
{
originalMesh = filter.sharedMesh;
normals = originalMesh.normals;
for (int i = 0; i < normals.Length; i++) normals[i] = transform.transform.TransformDirection(normals[i]).normalized;
}
if (collider != null && collider.sharedMesh != null)
{
originalColliderMesh = collider.sharedMesh;
colliderNormals = originalColliderMesh.normals;
for (int i = 0; i < colliderNormals.Length; i++) colliderNormals[i] = transform.transform.TransformDirection(colliderNormals[i]);
}
if (!parent) splineComputer = t.GetComponent<SplineComputer>();
if (splineComputer != null)
{
if (splineComputer.isClosed) originalSpline.Close();
destinationSpline = new Spline(originalSpline.type);
destinationSpline.points = new SplinePoint[originalSpline.points.Length];
destinationSpline.points = splineComputer.GetPoints();
if (splineComputer.isClosed) destinationSpline.Close();
}
}
public void Revert()
{
if (!isValid) return;
RevertTransform();
RevertCollider();
RevertMesh();
if (splineComputer != null) splineComputer.SetPoints(_originalSpline.points);
}
private void RevertMesh()
{
if (filter != null) filter.sharedMesh = originalMesh;
destinationMesh = null;
}
private void RevertTransform()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
transform.transform.localPosition = originalPosition;
transform.transform.localRotation = originalRotation;
}
else
{
transform.localPosition = originalPosition;
transform.localRotation = originalRotation;
transform.Update();
}
#else
transform.localPosition = originalPosition;
transform.localRotation = originalRotation;
transform.Update();
#endif
transform.scale = originalScale;
transform.Update();
}
private void RevertCollider()
{
if (collider != null) collider.sharedMesh = originalColliderMesh;
destinationColliderMesh = null;
}
public void Apply(bool applyTransform)
{
if (!enabled) return;
if (!isValid) return;
if(applyTransform) transform.Update();
if (editMesh != null && editMesh.hasUpdate) ApplyMesh();
if (bendCollider && collider != null)
{
if (!updateCollider)
{
if((editColliderMesh == null && editMesh != null) || editColliderMesh != null)
{
updateCollider = true;
if(Application.isPlaying) colliderUpdateDue = Time.time + colliderUpdateRate;
}
}
}
if (splineComputer != null) ApplySpline();
}
public void Update()
{
if (Time.time >= colliderUpdateDue && updateCollider)
{
updateCollider = false;
ApplyCollider();
}
}
private void ApplyMesh()
{
if (filter == null) return;
MeshUtility.CalculateTangents(editMesh);
if (destinationMesh == null)
{
destinationMesh = new Mesh();
destinationMesh.name = originalMesh.name;
}
editMesh.WriteMesh(ref destinationMesh);
destinationMesh.RecalculateBounds();
filter.sharedMesh = destinationMesh;
}
private void ApplyCollider()
{
if (collider == null) return;
if (originalColliderMesh == originalMesh) collider.sharedMesh = filter.sharedMesh; //if the collider has the same mesh as the filter - just copy it
else
{
MeshUtility.CalculateTangents(editColliderMesh);
if (destinationColliderMesh == null)
{
destinationColliderMesh = new Mesh();
destinationColliderMesh.name = originalColliderMesh.name;
}
editColliderMesh.WriteMesh(ref destinationColliderMesh);
destinationColliderMesh.RecalculateBounds();
collider.sharedMesh = destinationColliderMesh;
}
}
private void ApplySpline()
{
if (destinationSpline == null) return;
splineComputer.SetPoints(destinationSpline.points);
}
}
}
}