コード例 #1
0
        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);
            }
        }
コード例 #2
0
        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);
            }
        }