/// Internal contructor VertexPath(BezierPath bezierPath, VertexPathUtility.PathSplitData pathSplitData, Transform transform) { this.transform = transform; space = bezierPath.Space; isClosedLoop = bezierPath.IsClosed; int numVerts = pathSplitData.vertices.Count; length = pathSplitData.cumulativeLength[numVerts - 1]; localPoints = new Vector3[numVerts]; localNormals = new Vector3[numVerts]; localTangents = new Vector3[numVerts]; cumulativeLengthAtEachVertex = new float[numVerts]; times = new float[numVerts]; bounds = new Bounds((pathSplitData.minMax.Min + pathSplitData.minMax.Max) / 2, pathSplitData.minMax.Max - pathSplitData.minMax.Min); // Figure out up direction for path up = (bounds.size.z > bounds.size.y) ? Vector3.up : -Vector3.forward; Vector3 lastRotationAxis = up; // Loop through the data and assign to arrays. for (int i = 0; i < localPoints.Length; i++) { localPoints[i] = pathSplitData.vertices[i]; localTangents[i] = pathSplitData.tangents[i]; cumulativeLengthAtEachVertex[i] = pathSplitData.cumulativeLength[i]; times[i] = cumulativeLengthAtEachVertex[i] / length; // Calculate normals if (space == PathSpace.xyz) { if (i == 0) { localNormals[0] = Vector3.Cross(lastRotationAxis, pathSplitData.tangents[0]).normalized; } else { // First reflection Vector3 offset = (localPoints[i] - localPoints[i - 1]); float sqrDst = offset.sqrMagnitude; Vector3 r = lastRotationAxis - offset * 2 / sqrDst * Vector3.Dot(offset, lastRotationAxis); Vector3 t = localTangents[i - 1] - offset * 2 / sqrDst * Vector3.Dot(offset, localTangents[i - 1]); // Second reflection Vector3 v2 = localTangents[i] - t; float c2 = Vector3.Dot(v2, v2); Vector3 finalRot = r - v2 * 2 / c2 * Vector3.Dot(v2, r); Vector3 n = Vector3.Cross(finalRot, localTangents[i]).normalized; localNormals[i] = n; lastRotationAxis = finalRot; } } else { localNormals[i] = Vector3.Cross(localTangents[i], up) * ((bezierPath.FlipNormals) ? 1 : -1); } } // Apply correction for 3d normals along a closed path if (space == PathSpace.xyz && isClosedLoop) { // Get angle between first and last normal (if zero, they're already lined up, otherwise we need to correct) float normalsAngleErrorAcrossJoin = Vector3.SignedAngle(localNormals[localNormals.Length - 1], localNormals[0], localTangents[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 (int i = 1; i < localNormals.Length; i++) { float t = (i / (localNormals.Length - 1f)); float angle = normalsAngleErrorAcrossJoin * t; Quaternion rot = Quaternion.AngleAxis(angle, localTangents[i]); localNormals[i] = rot * localNormals[i] * ((bezierPath.FlipNormals) ? -1 : 1); } } } // Rotate normals to match up with user-defined anchor angles if (space == PathSpace.xyz) { for (int anchorIndex = 0; anchorIndex < pathSplitData.anchorVertexMap.Count - 1; anchorIndex++) { int nextAnchorIndex = (isClosedLoop) ? (anchorIndex + 1) % bezierPath.NumSegments : anchorIndex + 1; float startAngle = bezierPath.GetAnchorNormalAngle(anchorIndex) + bezierPath.GlobalNormalsAngle; float endAngle = bezierPath.GetAnchorNormalAngle(nextAnchorIndex) + bezierPath.GlobalNormalsAngle; float deltaAngle = Mathf.DeltaAngle(startAngle, endAngle); int startVertIndex = pathSplitData.anchorVertexMap[anchorIndex]; int endVertIndex = pathSplitData.anchorVertexMap[anchorIndex + 1]; int num = endVertIndex - startVertIndex; if (anchorIndex == pathSplitData.anchorVertexMap.Count - 2) { num += 1; } for (int i = 0; i < num; i++) { int vertIndex = startVertIndex + i; float t = i / (num - 1f); float angle = startAngle + deltaAngle * t; Quaternion rot = Quaternion.AngleAxis(angle, localTangents[vertIndex]); localNormals[vertIndex] = (rot * localNormals[vertIndex]) * ((bezierPath.FlipNormals) ? -1 : 1); } } } }
/// 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); } } } }