void CreateBezier(Vector3 centre, bool defaultIs2D = false) { if (this.bezierPath != null) { this.bezierPath.OnModified -= this.BezierPathEdited; } var space = (defaultIs2D) ? PathSpace.Xy : PathSpace.Xyz; this.bezierPath = new BezierPath(centre, false, space); this.bezierPath.OnModified += this.BezierPathEdited; this.vertexPathUpToDate = false; if (this.BezierOrVertexPathModified != null) { this.BezierOrVertexPathModified(); } if (this.BezierCreated != null) { this.BezierCreated(); } }
/// Internal contructor VertexPath(BezierPath bezierPath, VertexPathUtility.PathSplitData pathSplitData) { this.Space = bezierPath.Space; this.IsClosedLoop = bezierPath.IsClosed; var numVerts = pathSplitData.vertices.Count; this.Length = pathSplitData.cumulativeLength[numVerts - 1]; this.Vertices = new Vector3[numVerts]; this.Normals = new Vector3[numVerts]; this.Tangents = new Vector3[numVerts]; this.CumulativeLengthAtEachVertex = new float[numVerts]; this.Times = new float[numVerts]; this.Bounds = new Bounds( (pathSplitData.minMax.Min + pathSplitData.minMax.Max) / 2, pathSplitData.minMax.Max - pathSplitData.minMax.Min); // Figure out up direction for path this.Up = (this.Bounds.size.z > this.Bounds.size.y) ? Vector3.up : -Vector3.forward; var lastRotationAxis = this.Up; // Loop through the data and assign to arrays. for (var i = 0; i < this.Vertices.Length; i++) { this.Vertices[i] = pathSplitData.vertices[i]; this.Tangents[i] = pathSplitData.tangents[i]; this.CumulativeLengthAtEachVertex[i] = pathSplitData.cumulativeLength[i]; this.Times[i] = this.CumulativeLengthAtEachVertex[i] / this.Length; // Calculate normals if (this.Space == PathSpace.Xyz) { if (i == 0) { this.Normals[0] = Vector3.Cross(lastRotationAxis, pathSplitData.tangents[0]).normalized; } else { // First reflection var offset = (this.Vertices[i] - this.Vertices[i - 1]); var sqrDst = offset.sqrMagnitude; var r = lastRotationAxis - offset * 2 / sqrDst * Vector3.Dot(offset, lastRotationAxis); var t = this.Tangents[i - 1] - offset * 2 / sqrDst * Vector3.Dot(offset, this.Tangents[i - 1]); // Second reflection var v2 = this.Tangents[i] - t; var c2 = Vector3.Dot(v2, v2); var finalRot = r - v2 * 2 / c2 * Vector3.Dot(v2, r); var n = Vector3.Cross(finalRot, this.Tangents[i]).normalized; this.Normals[i] = n; lastRotationAxis = finalRot; } } else { this.Normals[i] = Vector3.Cross(this.Tangents[i], this.Up) * ((bezierPath.FlipNormals) ? 1 : -1); } } // Apply correction for 3d normals along a closed path if (this.Space == PathSpace.Xyz && this.IsClosedLoop) { // Get angle between first and last normal (if zero, they're already lined up, otherwise we need to correct) var normalsAngleErrorAcrossJoin = Vector3.SignedAngle( this.Normals[this.Normals.Length - 1], this.Normals[0], this.Tangents[0]); // Gradually rotate the normals along the path to ensure start and end normals line up correctly if (Mathf.Abs(normalsAngleErrorAcrossJoin) > 0.1f) // don't bother correcting if very nearly correct { for (var i = 1; i < this.Normals.Length; i++) { var t = (i / (this.Normals.Length - 1f)); var angle = normalsAngleErrorAcrossJoin * t; var rot = Quaternion.AngleAxis(angle, this.Tangents[i]); this.Normals[i] = rot * this.Normals[i] * ((bezierPath.FlipNormals) ? -1 : 1); } } } // Rotate normals to match up with user-defined anchor angles if (this.Space == PathSpace.Xyz) { for (var anchorIndex = 0; anchorIndex < pathSplitData.anchorVertexMap.Count - 1; anchorIndex++) { var nextAnchorIndex = (this.IsClosedLoop) ? (anchorIndex + 1) % bezierPath.NumSegments : anchorIndex + 1; var startAngle = bezierPath.GetAnchorNormalAngle(anchorIndex) + bezierPath.GlobalNormalsAngle; var endAngle = bezierPath.GetAnchorNormalAngle(nextAnchorIndex) + bezierPath.GlobalNormalsAngle; var deltaAngle = Mathf.DeltaAngle(startAngle, endAngle); var startVertIndex = pathSplitData.anchorVertexMap[anchorIndex]; var endVertIndex = pathSplitData.anchorVertexMap[anchorIndex + 1]; var num = endVertIndex - startVertIndex; if (anchorIndex == pathSplitData.anchorVertexMap.Count - 2) { num += 1; } for (var i = 0; i < num; i++) { var vertIndex = startVertIndex + i; var t = i / (num - 1f); var angle = startAngle + deltaAngle * t; var rot = Quaternion.AngleAxis(angle, this.Tangents[vertIndex]); this.Normals[vertIndex] = (rot * this.Normals[vertIndex]) * ((bezierPath.FlipNormals) ? -1 : 1); } } } }
/// <summary> Splits bezier path into array of vertices along the path.</summary> ///<param name="maxAngleError">How much can the angle of the path change before a vertex is added. This allows fewer vertices to be generated in straighter sections.</param> ///<param name="minVertexDst">Vertices won't be added closer together than this distance, regardless of angle error.</param> ///<param name="accuracy">Higher value means the change in angle is checked more frequently.</param> public VertexPath(BezierPath bezierPath, float vertexSpacing) : this( bezierPath, VertexPathUtility.SplitBezierPathEvenly(bezierPath, Mathf.Max(vertexSpacing, MinVertexSpacing), Accuracy)) { }
/// <summary> Splits bezier path into array of vertices along the path.</summary> ///<param name="maxAngleError">How much can the angle of the path change before a vertex is added. This allows fewer vertices to be generated in straighter sections.</param> ///<param name="minVertexDst">Vertices won't be added closer together than this distance, regardless of angle error.</param> public VertexPath(BezierPath bezierPath, float maxAngleError = 0.3f, float minVertexDst = 0) : this( bezierPath, VertexPathUtility.SplitBezierPathByAngleError(bezierPath, maxAngleError, minVertexDst, Accuracy)) { }