void OnSceneGUI() { GUI.changed = false; // Get the keyframes of the AnimationCurve to be manipulated Keyframe[] keys = script.spline.keys; // Set defaultLocalRotation so that the initial local rotation will be the zero point for the rotation limit if (!Application.isPlaying) { script.defaultLocalRotation = script.transform.localRotation; } if (script.axis == Vector3.zero) { return; } // Make the curve loop script.spline.postWrapMode = WrapMode.Loop; script.spline.preWrapMode = WrapMode.Loop; DrawRotationSphere(script.transform.position); // Display the main axis DrawArrow(script.transform.position, Direction(script.axis), colorDefault, "Axis", 0.02f); Vector3 swing = script.axis.normalized; // Editing tools GUI Handles.BeginGUI(); GUILayout.BeginArea(new Rect(10, Screen.height - 140, 400, 90), "Rotation Limit Spline", "Window"); // Scale Mode and Tangent Mode GUILayout.BeginHorizontal(); scaleMode = (ScaleMode)EditorGUILayout.EnumPopup("Drag Handle", scaleMode); tangentMode = (TangentMode)EditorGUILayout.EnumPopup("Drag Tangents", tangentMode); GUILayout.EndHorizontal(); EditorGUILayout.Space(); if (Inspector.Button("Rotate 90 degrees", "Rotate rotation limit around axis.", script, GUILayout.Width(220))) { if (!Application.isPlaying) { Undo.RecordObject(script, "Handle Value"); } for (int i = 0; i < keys.Length; i++) { keys[i].time += 90; } } // Cloning values from another RotationLimitSpline EditorGUILayout.BeginHorizontal(); if (Inspector.Button("Clone From", "Make this rotation limit identical to another", script, GUILayout.Width(220))) { CloneLimit(); keys = script.spline.keys; } clone = (RotationLimitSpline)EditorGUILayout.ObjectField("", clone, typeof(RotationLimitSpline), true); EditorGUILayout.EndHorizontal(); GUILayout.EndArea(); Handles.EndGUI(); // Draw keyframes for (int i = 0; i < keys.Length - 1; i++) { float angle = keys[i].time; // Start drawing handles Quaternion offset = Quaternion.AngleAxis(angle, swing); Quaternion rotation = Quaternion.AngleAxis(keys[i].value, offset * script.crossAxis); Vector3 position = script.transform.position + Direction(rotation * swing); Handles.Label(position, " " + i.ToString()); // Dragging Values if (selectedHandle == i) { Handles.color = colorHandles; switch (scaleMode) { case ScaleMode.Limit: float inputValue = keys[i].value; inputValue = Mathf.Clamp(Handles.ScaleValueHandle(inputValue, position, Quaternion.identity, 0.5f, Handles.SphereCap, 0), 0.01f, 180); if (keys[i].value != inputValue) { if (!Application.isPlaying) { Undo.RecordObject(script, "Handle Value"); } keys[i].value = inputValue; } break; case ScaleMode.Angle: float inputTime = keys[i].time; inputTime = Handles.ScaleValueHandle(inputTime, position, Quaternion.identity, 0.5f, Handles.SphereCap, 0); if (keys[i].time != inputTime) { if (!Application.isPlaying) { Undo.RecordObject(script, "Handle Angle"); } keys[i].time = inputTime; } break; } } // Handle select button if (selectedHandle != i) { Handles.color = Color.blue; if (Handles.Button(position, script.transform.rotation, 0.05f, 0.05f, Handles.SphereCap)) { selectedHandle = i; } } // Tangents if (selectedHandle == i) { // Evaluate positions before and after the key to get the tangent positions Vector3 prevPosition = GetAnglePosition(keys[i].time - 1); Vector3 nextPosition = GetAnglePosition(keys[i].time + 1); // Draw handles for the tangents Handles.color = Color.white; Vector3 toNext = (nextPosition - position).normalized * 0.3f; float outTangent = keys[i].outTangent; outTangent = Handles.ScaleValueHandle(outTangent, position + toNext, Quaternion.identity, 0.2f, Handles.SphereCap, 0); Vector3 toPrev = (prevPosition - position).normalized * 0.3f; float inTangent = keys[i].inTangent; inTangent = Handles.ScaleValueHandle(inTangent, position + toPrev, Quaternion.identity, 0.2f, Handles.SphereCap, 0); if (outTangent != keys[i].outTangent || inTangent != keys[i].inTangent) { selectedHandle = i; } // Make the other tangent match the dragged tangent (if in "Smooth" TangentMode) switch (tangentMode) { case TangentMode.Smooth: if (outTangent != keys[i].outTangent) { if (!Application.isPlaying) { Undo.RecordObject(script, "Tangents"); } keys[i].outTangent = outTangent; keys[i].inTangent = outTangent; } else if (inTangent != keys[i].inTangent) { if (!Application.isPlaying) { Undo.RecordObject(script, "Tangents"); } keys[i].outTangent = inTangent; keys[i].inTangent = inTangent; } break; case TangentMode.Independent: if (outTangent != keys[i].outTangent) { if (!Application.isPlaying) { Undo.RecordObject(script, "Tangents"); } keys[i].outTangent = outTangent; } else if (inTangent != keys[i].inTangent) { if (!Application.isPlaying) { Undo.RecordObject(script, "Tangents"); } keys[i].inTangent = inTangent; } break; } // Draw lines and labels to tangent handles Handles.color = Color.white; GUI.color = Color.white; Handles.DrawLine(position, position + toNext); Handles.Label(position + toNext, " Out"); Handles.DrawLine(position, position + toPrev); Handles.Label(position + toPrev, " In"); } } // Selected Point GUI if (selectedHandle != -1) { Handles.BeginGUI(); GUILayout.BeginArea(new Rect(Screen.width - 240, Screen.height - 200, 230, 150), "Handle " + selectedHandle.ToString(), "Window"); if (Inspector.Button("Delete", "Delete this handle", script)) { if (keys.Length > 4) { deleteHandle = selectedHandle; } else if (!Warning.logged) { script.LogWarning("Spline Rotation Limit should have at least 3 handles"); } } if (Inspector.Button("Add Handle", "Add a new handle next to this one", script)) { addHandle = selectedHandle; } // Clamp the key angles to previous and next handle angles float prevTime = 0, nextTime = 0; if (selectedHandle < keys.Length - 2) { nextTime = keys[selectedHandle + 1].time; } else { nextTime = keys[0].time + 360; } if (selectedHandle == 0) { prevTime = keys[keys.Length - 2].time - 360; } else { prevTime = keys[selectedHandle - 1].time; } // Angles float inputTime = keys[selectedHandle].time; inputTime = Mathf.Clamp(EditorGUILayout.FloatField(new GUIContent("Angle", "Angle of the point (0-360)."), inputTime), prevTime, nextTime); if (keys[selectedHandle].time != inputTime) { if (!Application.isPlaying) { Undo.RecordObject(script, "Handle Angle"); } keys[selectedHandle].time = inputTime; } // Limits float inputValue = keys[selectedHandle].value; inputValue = Mathf.Clamp(EditorGUILayout.FloatField(new GUIContent("Limit", "Max angular limit from Axis at this angle"), inputValue), 0, 180); if (keys[selectedHandle].value != inputValue) { if (!Application.isPlaying) { Undo.RecordObject(script, "Handle Limit"); } keys[selectedHandle].value = inputValue; } // In Tangents float inputInTangent = keys[selectedHandle].inTangent; inputInTangent = EditorGUILayout.FloatField(new GUIContent("In Tangent", "In tangent of the handle point on the spline"), inputInTangent); if (keys[selectedHandle].inTangent != inputInTangent) { if (!Application.isPlaying) { Undo.RecordObject(script, "Handle In Tangent"); } keys[selectedHandle].inTangent = inputInTangent; } // Out tangents float inputOutTangent = keys[selectedHandle].outTangent; inputOutTangent = EditorGUILayout.FloatField(new GUIContent("Out Tangent", "Out tangent of the handle point on the spline"), inputOutTangent); if (keys[selectedHandle].outTangent != inputOutTangent) { if (!Application.isPlaying) { Undo.RecordObject(script, "Handle Out Tangent"); } keys[selectedHandle].outTangent = inputOutTangent; } GUILayout.EndArea(); Handles.EndGUI(); } // Make sure the keyframes are valid; ValidateKeyframes(keys); // Replace the AnimationCurve keyframes with the manipulated keyframes script.spline.keys = keys; // Display limits for (int i = 0; i < 360; i += 2) { float evaluatedLimit = script.spline.Evaluate((float)i); Quaternion offset = Quaternion.AngleAxis(i, swing); Quaternion evaluatedRotation = Quaternion.AngleAxis(evaluatedLimit, offset * script.crossAxis); Quaternion testRotation = Quaternion.AngleAxis(179.9f, offset * script.crossAxis); Quaternion limitedRotation = script.LimitSwing(testRotation); Vector3 evaluatedDirection = evaluatedRotation * swing; Vector3 limitedDirection = limitedRotation * swing; // Display the limit points in red if they are out of range bool isValid = Vector3.Distance(evaluatedDirection, limitedDirection) < 0.01f && evaluatedLimit >= 0; Color color = isValid? colorDefaultTransparent: colorInvalid; Vector3 limitPoint = script.transform.position + Direction(evaluatedDirection); Handles.color = color; if (i == 0) { zeroPoint = limitPoint; } Handles.DrawLine(script.transform.position, limitPoint); if (i > 0) { Handles.color = isValid? colorDefault: colorInvalid; Handles.DrawLine(limitPoint, lastPoint); if (i == 358) { Handles.DrawLine(limitPoint, zeroPoint); } } lastPoint = limitPoint; } // Deleting points if (deleteHandle != -1) { DeleteHandle(deleteHandle); selectedHandle = -1; deleteHandle = -1; } // Adding points if (addHandle != -1) { AddHandle(addHandle); addHandle = -1; } Handles.color = Color.white; if (GUI.changed) { EditorUtility.SetDirty(script); } }
public void OnSceneGUI() { GUI.changed = false; // Set defaultLocalRotation so that the initial local rotation will be the zero point for the rotation limit if (!Application.isPlaying) { script.defaultLocalRotation = script.transform.localRotation; } if (script.axis == Vector3.zero) { return; } // Quick Editing Tools Handles.BeginGUI(); GUILayout.BeginArea(new Rect(10, Screen.height - 180, 550, 130), "Rotation Limit Polygonal", "Window"); // Cloning values from another RotationLimitPolygonal EditorGUILayout.BeginHorizontal(); if (Inspector.Button("Clone From", "Make this rotation limit identical to another", script, GUILayout.Width(220))) { CloneLimit(); } clone = (RotationLimitPolygonal)EditorGUILayout.ObjectField("", clone, typeof(RotationLimitPolygonal), true); EditorGUILayout.EndHorizontal(); // Symmetry symmetry = (Symmetry)EditorGUILayout.EnumPopup("Symmetry", symmetry, GUILayout.Width(220)); // Flipping EditorGUILayout.BeginHorizontal(); if (Inspector.Button("Flip X", "Flip points along local X axis", script, GUILayout.Width(100))) { FlipLimit(0); } if (Inspector.Button("Flip Y", "Flip points along local Y axis", script, GUILayout.Width(100))) { FlipLimit(1); } if (Inspector.Button("Flip Z", "Flip points along local Z axis", script, GUILayout.Width(100))) { FlipLimit(2); } GUILayout.Label("Flip everything along axis"); EditorGUILayout.EndHorizontal(); // Rotating EditorGUILayout.BeginHorizontal(); if (Inspector.Button("Rotate X", "Rotate points along X axis by Degrees", script, GUILayout.Width(100))) { RotatePoints(degrees, Vector3.right); } if (Inspector.Button("Rotate Y", "Rotate points along Y axis by Degrees", script, GUILayout.Width(100))) { RotatePoints(degrees, Vector3.up); } if (Inspector.Button("Rotate Z", "Rotate points along Z axis by Degrees", script, GUILayout.Width(100))) { RotatePoints(degrees, Vector3.forward); } degrees = EditorGUILayout.FloatField("Degrees", degrees, GUILayout.Width(200)); EditorGUILayout.EndHorizontal(); // Smooth/Optimize EditorGUILayout.BeginHorizontal(); if (Inspector.Button("Smooth", "Double the points", script)) { Smooth(); } if (Inspector.Button("Optimize", "Delete every second point", script)) { Optimize(); } EditorGUILayout.EndHorizontal(); GUILayout.EndArea(); Handles.EndGUI(); // Rebuild reach cones script.BuildReachCones(); // Draw a white transparent sphere DrawRotationSphere(script.transform.position); // Draw Axis DrawArrow(script.transform.position, Direction(script.axis), colorDefault, "Axis", 0.02f); // Display limit points for (int i = 0; i < script.points.Length; i++) { Color color = GetColor(i); // Paint the point in green or red if it belongs to an invalid reach cone Handles.color = color; GUI.color = color; // Line from the center to the point and the label Handles.DrawLine(script.transform.position, script.transform.position + Direction(script.points[i].point)); Handles.Label(script.transform.position + Direction(script.points[i].point + new Vector3(-0.02f, 0, 0)), " " + i.ToString()); // Selecting points Handles.color = colorHandles; if (Inspector.DotButton(script.transform.position + Direction(script.points[i].point), script.transform.rotation, 0.02f, 0.02f)) { selectedPoint = i; } Handles.color = Color.white; GUI.color = Color.white; // Limit point GUI if (i == selectedPoint) { Handles.BeginGUI(); GUILayout.BeginArea(new Rect(Screen.width - 240, Screen.height - 180, 230, 130), "Limit Point " + i.ToString(), "Window"); if (Inspector.Button("Delete", "Delete this point", script)) { if (script.points.Length > 3) { // Using the deletePoint index here because we dont want to delete points from the array that we are iterating deletePoint = i; } else if (!Warning.logged) { script.LogWarning("Polygonal Rotation Limit should have at least 3 limit points"); } } if (Inspector.Button("Add Point", "Add a new point next to this one", script)) { addPoint = i; } // Store point for undo Vector3 oldPoint = script.points[i].point; // Manual input for the point position Inspector.AddVector3(ref script.points[i].point, "Point", script, GUILayout.Width(210)); EditorGUILayout.Space(); // Tangent weight Inspector.AddFloat(ref script.points[i].tangentWeight, "Tangent Weight", "Weight of this point's tangent. Used in smoothing.", script, -Mathf.Infinity, Mathf.Infinity, GUILayout.Width(150)); GUILayout.EndArea(); Handles.EndGUI(); // Moving Points Vector3 pointWorld = Handles.PositionHandle(script.transform.position + Direction(script.points[i].point), Quaternion.identity); Vector3 newPoint = InverseDirection(pointWorld - script.transform.position); if (newPoint != script.points[i].point) { if (!Application.isPlaying) { Undo.RecordObject(script, "Move Limit Point"); } script.points[i].point = newPoint; } // Symmetry if (symmetry != Symmetry.Off && script.points.Length > 3 && oldPoint != script.points[i].point) { RotationLimitPolygonal.LimitPoint symmetryPoint = GetClosestPoint(Symmetrize(oldPoint, symmetry)); if (symmetryPoint != script.points[i]) { symmetryPoint.point = Symmetrize(script.points[i].point, symmetry); } } } // Normalize the point script.points[i].point = script.points[i].point.normalized; } // Display smoothed polygon for (int i = 0; i < script.P.Length; i++) { Color color = GetColor(i); // Smoothed triangles are transparent Handles.color = new Color(color.r, color.g, color.b, 0.25f); Handles.DrawLine(script.transform.position, script.transform.position + Direction(script.P[i])); Handles.color = color; if (i < script.P.Length - 1) { Handles.DrawLine(script.transform.position + Direction(script.P[i]), script.transform.position + Direction(script.P[i + 1])); } else { Handles.DrawLine(script.transform.position + Direction(script.P[i]), script.transform.position + Direction(script.P[0])); } Handles.color = Color.white; } // Deleting points if (deletePoint != -1) { DeletePoint(deletePoint); selectedPoint = -1; deletePoint = -1; } // Adding points if (addPoint != -1) { AddPoint(addPoint); addPoint = -1; } if (GUI.changed) { EditorUtility.SetDirty(script); } }