/// <summary> /// Rebuilds the mesh for the specified lofting group /// </summary> /// <param name="group">lofting group</param> private void RebuildMesh(LoftingGroup group) { if (!group.IsValid) { throw new ArgumentException("SegmentMesh and MeshFilter can't be null"); } LoftDirection.Normalize(); var result = group.ResultMesh; float segmentStep = group.IntervalLength / group.ActiveSegmentsNumber; for (int s = 0; s < group.ActiveSegmentsNumber; s++) { ManagedMesh segment = group.ResultSegments[s]; segment.Clear(); ManagedMesh source = group.mSegmentMesh; if (s == 0 && group.mStartPiece != null) { source = group.mStartPiece; } if (s == group.ActiveSegmentsNumber - 1 && group.mEndPiece != null) { source = group.mEndPiece; } Vector3 pivotShift = Vector3.Project(source.bounds.center, LoftDirection); Vector3 startSPos = pivotShift - source.bounds.extents.Mul(LoftDirection); Vector3 endSPos = pivotShift + source.bounds.extents.Mul(LoftDirection); Vector3 sourceDir = (endSPos - startSPos); float sourceLenght = sourceDir.magnitude; bool flipSegment = RollerCoasterFix && _flipNext; Vector3 dirS = LoftDirection; Vector3[] sourceVertices = source.vertices; Vector3[] resultVertices = new Vector3[source.vertexCount]; Vector3[] sourceNormals = source.normals; Vector3[] resultNormals = new Vector3[source.vertexCount]; Vector4[] sourceTangents = source.tangents; Vector4[] resultTangents = new Vector4[source.vertexCount]; for (int i = 0; i < source.vertexCount; i++) { Vector3 curSPos = sourceVertices[i] - startSPos; Vector3 projected = Vector3.Project(curSPos, dirS); Vector3 perp = curSPos - projected; perp.Scale(SegmentScale); float sourceT = Mathf.InverseLerp(0f, sourceLenght, projected.magnitude); float resultT = group.StartPosition + segmentStep * s + segmentStep * sourceT; int nodeIndex; float nodeT = GetNodeT(resultT, out nodeIndex); if (nodeIndex < 0) { nodeIndex = 0; } else if (nodeIndex > Nodes.Count - 1) { nodeIndex = Nodes.Count - 1; } int nextIndex = nodeIndex + 1; if (nodeIndex >= Nodes.Count - 1) { if (Loop) { nextIndex = 0; } else { nodeIndex = Nodes.Count - 2; nextIndex = Nodes.Count - 1; } } SplineNode fromNode = Nodes[nodeIndex]; SplineNode toNode = Nodes[nextIndex]; Vector3 startRPos = fromNode.LocalPosition; Vector3 endRPos = toNode.LocalPosition; Vector3 nodesDir = (endRPos - startRPos); Vector3 upwardVector = Vector3.up; float additionalAngle = 0f; if (RollerCoasterFix && s > 0 && nodeIndex > 0) { SplineNode prevNode = Nodes[nodeIndex - 1]; Vector3 prevNodesDir = (startRPos - prevNode.LocalPosition); Vector3 prevNodesDirXZ = prevNodesDir; prevNodesDirXZ.y = 0; Vector3 nodesDirXZ = nodesDir; nodesDirXZ.y = 0; float dot = Vector3.Dot(prevNodesDirXZ, nodesDirXZ); if (Vector3.Dot(Vector3.up, nodesDir) > 0.5f) { upwardVector = Vector3.left; additionalAngle = -90f * Mathf.Abs(Vector3.Dot(LoftDirection, Vector3.left)); _flipNext = true; } else if (Vector3.Dot(Vector3.up, nodesDir) < -0.5f) { upwardVector = Vector3.right; additionalAngle = 90f * Mathf.Abs(Vector3.Dot(LoftDirection, Vector3.left)); _flipNext = false; } else { if (flipSegment) { additionalAngle = 180f; } } } Vector3 startR_RightP = fromNode.RightP; Vector3 endRPos_RightP = toNode.RightP; Vector3 endRPos_LeftP = toNode.LeftP; float loftAngle = LoftAngle + Mathf.Lerp(fromNode.AdditionalLoftAngle, toNode.AdditionalLoftAngle, nodeT); Vector3 dirR = nodesDir.normalized; if (Loop || (nodeIndex > 0 && nodeIndex + 2 < Nodes.Count)) { dirR = (startR_RightP - startRPos).normalized; Vector3 dirN = (endRPos_RightP - endRPos).normalized; float t = QuadraticSmooth ? (nodeT * nodeT) : nodeT; dirR = Vector3.Slerp(dirR, dirN, t); } Quaternion directionRot = Quaternion.LookRotation(dirR, upwardVector) * Quaternion.Inverse(Quaternion.LookRotation(dirS, upwardVector)); Quaternion loftRot = Quaternion.AngleAxis(additionalAngle + loftAngle, dirR); resultVertices[i] = startRPos + loftRot * (directionRot * perp) + SplinePowerVector3Extensions.Bezier( startRPos - startRPos, startR_RightP - startRPos, endRPos_LeftP - startRPos, endRPos - startRPos, nodeT); if (group.ProcessOriginNormals) { resultNormals[i] = loftRot * directionRot * sourceNormals[i]; } if (group.ProcessOriginTangents) { resultTangents[i] = loftRot * directionRot * sourceTangents[i]; } } segment.vertices = resultVertices; segment.triangles = source.triangles; segment.uv = source.uv; segment.normals = resultNormals; segment.tangents = resultTangents; } for (int s = 0; s < group.ResultSegments.Length; s++) { if (group.RecalculateNormals) { group.ResultSegments[s].RecalculateNormals(); } if (s > 0) { if (group.SmoothNormals || group.Weld) { WeldAndSmooth(group, group.ResultSegments[s - 1], group.ResultSegments[s]); } } if (Loop && s == 0) { if (group.SmoothNormals || group.Weld) { WeldAndSmooth(group, group.ResultSegments[group.ResultSegments.Length - 1], group.ResultSegments[s]); } } } CombineSegments(result, group.ResultSegments); #if UNITY_EDITOR UnityEditor.MeshUtility.SetMeshCompression(result, UnityEditor.ModelImporterMeshCompression.High); result.name = ExportOptions.GetName(this, group); #endif result.Optimize(); result.hideFlags = HideFlags.DontSave; group.ResultMesh = result; if (group.MeshFilter != null) { group.MeshFilter.sharedMesh = null; group.MeshFilter.sharedMesh = result; } if (group.MeshCollider != null) { group.MeshCollider.sharedMesh = null; group.MeshCollider.sharedMesh = result; } }
private void OnDrawGizmos() { if (VisualOptions.ShowNodeLinks) { Gizmos.color = Color.white; for (int i = 1; i < Nodes.Count; i++) { Gizmos.DrawLine(Nodes[i - 1].transform.position, Nodes[i].transform.position); } } if (!VisualOptions.ShowSegmentsPath) { return; } Gizmos.color = Color.red; var ltw = transform.localToWorldMatrix; var wtl = transform.worldToLocalMatrix; float segmentStep = 1.0f / SegmentsNumber; Vector3[] points = new Vector3[SegmentsNumber]; for (int s = 0; s < SegmentsNumber; s++) { float resultT = segmentStep * s + segmentStep; int nodeIndex; float nodeT = GetNodeT(resultT, out nodeIndex); if (nodeIndex < 0) { nodeIndex = 0; } else if (nodeIndex > Nodes.Count - 1) { nodeIndex = Nodes.Count - 1; } int nextIndex = nodeIndex + 1; if (nodeIndex >= Nodes.Count - 1) { if (Loop) { nextIndex = 0; } else { nodeIndex = Nodes.Count - 2; nextIndex = Nodes.Count - 1; } } SplineNode fromNode = Nodes[nodeIndex]; SplineNode toNode = Nodes[nextIndex]; Vector3 startRPos = fromNode.LocalPosition; Vector3 endRPos = toNode.LocalPosition; Vector3 startR_RightP = fromNode.RightP; Vector3 endRPos_LeftP = toNode.LeftP; points[s] = startRPos + SplinePowerVector3Extensions.Bezier( startRPos - startRPos, startR_RightP - startRPos, endRPos_LeftP - startRPos, endRPos - startRPos, nodeT) + (Vector3)(wtl * transform.position); if (s > 0) { Gizmos.DrawLine(ltw * points[s - 1], ltw * points[s]); } } }