814 lines
32 KiB
C#
814 lines
32 KiB
C#
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|