Esempio n. 1
0
        void CreateBezier(Vector3 centre, bool defaultIs2D = false)
        {
            if (_bezierPath != null)
            {
                _bezierPath.OnModified -= BezierPathEdited;
            }

            PathSpace space = defaultIs2D ? PathSpace.xy : PathSpace.xyz;

            _bezierPath = new BezierPath(centre, false, space);

            _bezierPath.OnModified += BezierPathEdited;
            vertexPathUpToDate      = false;

            bezierOrVertexPathModified?.Invoke();
            bezierCreated?.Invoke();
        }
Esempio n. 2
0
        void DrawVertexPathSceneEditor()
        {
            Color bezierCol = GlobalDisplaySettings.BezierPath;

            bezierCol.a *= .5f;

            if (Data.showBezierPathInVertexMode)
            {
                for (int i = 0; i < BezierPath.NumSegments; i++)
                {
                    Vector3[] points = BezierPath.GetPointsInSegment(i);
                    for (int j = 0; j < points.Length; j++)
                    {
                        points[j] = MathUtility.TransformPoint(points[j], creator.transform, BezierPath.Space);
                    }

                    Handles.DrawBezier(points[0], points[3], points[1], points[2], bezierCol, null, 2);
                }
            }

            Handles.color = GlobalDisplaySettings.vertexPath;

            for (int i = 0; i < creator.VertexPath.NumPoints; i++)
            {
                int nextIndex = (i + 1) % creator.VertexPath.NumPoints;
                if (nextIndex != 0 || BezierPath.IsClosed)
                {
                    Handles.DrawLine(creator.VertexPath.GetPoint(i), creator.VertexPath.GetPoint(nextIndex));
                }
            }

            if (Data.showNormalsInVertexMode)
            {
                Handles.color = GlobalDisplaySettings.normals;
                Vector3[] normalLines = new Vector3[creator.VertexPath.NumPoints * 2];
                for (int i = 0; i < creator.VertexPath.NumPoints; i++)
                {
                    normalLines[i * 2]     = creator.VertexPath.GetPoint(i);
                    normalLines[i * 2 + 1] = creator.VertexPath.GetPoint(i) + creator.VertexPath.localNormals[i] * GlobalDisplaySettings.normalsLength;
                }
                Handles.DrawLines(normalLines);
            }
        }
Esempio n. 3
0
        void DrawHandle(int i)
        {
            Vector3 handlePosition = MathUtility.TransformPoint(BezierPath[i], creator.transform, BezierPath.Space);

            float anchorHandleSize  = GetHandleDiameter(GlobalDisplaySettings.anchorSize * Data.bezierHandleScale, BezierPath[i]);
            float controlHandleSize = GetHandleDiameter(GlobalDisplaySettings.controlSize * Data.bezierHandleScale, BezierPath[i]);

            bool  isAnchorPoint     = i % 3 == 0;
            bool  isInteractive     = isAnchorPoint || BezierPath.ControlPointMode != BezierPath.ControlMode.Automatic;
            float handleSize        = (isAnchorPoint) ? anchorHandleSize : controlHandleSize;
            bool  doTransformHandle = i == handleIndexToDisplayAsTransform;

            PathHandle.HandleColours handleColours = (isAnchorPoint) ? splineAnchorColours : splineControlColours;
            if (i == handleIndexToDisplayAsTransform)
            {
                handleColours.defaultColour = (isAnchorPoint) ? GlobalDisplaySettings.anchorSelected : GlobalDisplaySettings.controlSelected;
            }

            var cap = capFunctions[(isAnchorPoint) ? GlobalDisplaySettings.anchorShape : GlobalDisplaySettings.controlShape];

            handlePosition = PathHandle.DrawHandle(handlePosition, BezierPath.Space, isInteractive, handleSize, cap, handleColours, out PathHandle.HandleInputType handleInputType, i);

            if (doTransformHandle)
            {
                // Show normals rotate tool
                if (Data.showNormals && Tools.current == Tool.Rotate && isAnchorPoint && BezierPath.Space == PathSpace.xyz)
                {
                    Handles.color = handlesStartCol;

                    int     attachedControlIndex = (i == BezierPath.NumPoints - 1) ? i - 1 : i + 1;
                    Vector3 dir             = (BezierPath[attachedControlIndex] - handlePosition).normalized;
                    float   handleRotOffset = (360 + BezierPath.GlobalNormalsAngle) % 360;
                    anchorAngleHandle.radius = handleSize * 3;
                    anchorAngleHandle.angle  = handleRotOffset + BezierPath.GetAnchorNormalAngle(i / 3);
                    Vector3   handleDirection = Vector3.Cross(dir, Vector3.up);
                    Matrix4x4 handleMatrix    = Matrix4x4.TRS(
                        handlePosition,
                        Quaternion.LookRotation(handleDirection, dir),
                        Vector3.one
                        );

                    using (new Handles.DrawingScope(handleMatrix))
                    {
                        // draw the handle
                        EditorGUI.BeginChangeCheck();
                        anchorAngleHandle.DrawHandle();
                        if (EditorGUI.EndChangeCheck())
                        {
                            Undo.RecordObject(creator, "Set angle");
                            BezierPath.SetAnchorNormalAngle(i / 3, anchorAngleHandle.angle - handleRotOffset);
                        }
                    }
                }
                else
                {
                    handlePosition = Handles.DoPositionHandle(handlePosition, Quaternion.identity);
                }
            }

            switch (handleInputType)
            {
            case PathHandle.HandleInputType.LMBDrag:
                draggingHandleIndex             = i;
                handleIndexToDisplayAsTransform = -1;
                Repaint();
                break;

            case PathHandle.HandleInputType.LMBRelease:
                draggingHandleIndex             = -1;
                handleIndexToDisplayAsTransform = -1;
                Repaint();
                break;

            case PathHandle.HandleInputType.LMBClick:
                draggingHandleIndex = -1;
                if (Event.current.shift)
                {
                    handleIndexToDisplayAsTransform = -1;     // disable move tool if new point added
                }
                else
                {
                    if (handleIndexToDisplayAsTransform == i)
                    {
                        handleIndexToDisplayAsTransform = -1;     // disable move tool if clicking on point under move tool
                    }
                    else
                    {
                        handleIndexToDisplayAsTransform = i;
                    }
                }
                Repaint();
                break;

            case PathHandle.HandleInputType.LMBPress:
                if (handleIndexToDisplayAsTransform != i)
                {
                    handleIndexToDisplayAsTransform = -1;
                    Repaint();
                }
                break;
            }

            Vector3 localHandlePosition = MathUtility.InverseTransformPoint(handlePosition, creator.transform, BezierPath.Space);

            if (BezierPath[i] != localHandlePosition)
            {
                Undo.RecordObject(creator, "Move point");
                BezierPath.MovePoint(i, localHandlePosition);
            }
        }
Esempio n. 4
0
        void DrawBezierPathSceneEditor()
        {
            bool   displayControlPoints = Data.displayControlPoints && (BezierPath.ControlPointMode != BezierPath.ControlMode.Automatic || !GlobalDisplaySettings.hideAutoControls);
            Bounds bounds = BezierPath.CalculateBoundsWithTransform(creator.transform);

            if (Event.current.type == EventType.Repaint)
            {
                for (int i = 0; i < BezierPath.NumSegments; i++)
                {
                    Vector3[] points = BezierPath.GetPointsInSegment(i);
                    for (int j = 0; j < points.Length; j++)
                    {
                        points[j] = MathUtility.TransformPoint(points[j], creator.transform, BezierPath.Space);
                    }

                    if (Data.showPerSegmentBounds)
                    {
                        Bounds segmentBounds = PathUtility.CalculateSegmentBounds(points[0], points[1], points[2], points[3]);
                        Handles.color = GlobalDisplaySettings.segmentBounds;
                        Handles.DrawWireCube(segmentBounds.center, segmentBounds.size);
                    }

                    // Draw lines between control points
                    if (displayControlPoints)
                    {
                        Handles.color = (BezierPath.ControlPointMode == BezierPath.ControlMode.Automatic) ? GlobalDisplaySettings.handleDisabled : GlobalDisplaySettings.controlLine;
                        Handles.DrawLine(points[1], points[0]);
                        Handles.DrawLine(points[2], points[3]);
                    }

                    // Draw VertexPath
                    bool  highlightSegment = (i == selectedSegmentIndex && Event.current.shift && draggingHandleIndex == -1 && mouseOverHandleIndex == -1);
                    Color segmentCol       = (highlightSegment) ? GlobalDisplaySettings.highlightedPath : GlobalDisplaySettings.BezierPath;
                    Handles.DrawBezier(points[0], points[3], points[1], points[2], segmentCol, null, 2);
                }

                if (Data.showPathBounds)
                {
                    Handles.color = GlobalDisplaySettings.bounds;
                    Handles.DrawWireCube(bounds.center, bounds.size);
                }

                // Draw normals
                if (Data.showNormals)
                {
                    if (!hasUpdatedNormalsVertexPath)
                    {
                        normalsVertexPath           = new VertexPath(BezierPath, creator.transform, normalsSpacing);
                        hasUpdatedNormalsVertexPath = true;
                    }

                    if (editingNormalsOld != Data.showNormals)
                    {
                        editingNormalsOld = Data.showNormals;
                        Repaint();
                    }

                    Vector3[] normalLines = new Vector3[normalsVertexPath.NumPoints * 2];
                    Handles.color = GlobalDisplaySettings.normals;
                    for (int i = 0; i < normalsVertexPath.NumPoints; i++)
                    {
                        normalLines[i * 2]     = normalsVertexPath.GetPoint(i);
                        normalLines[i * 2 + 1] = normalsVertexPath.GetPoint(i) + normalsVertexPath.GetNormal(i) * GlobalDisplaySettings.normalsLength;
                    }
                    Handles.DrawLines(normalLines);
                }
            }

            if (Data.displayAnchorPoints)
            {
                for (int i = 0; i < BezierPath.NumPoints; i += 3)
                {
                    DrawHandle(i);
                }
            }
            if (displayControlPoints)
            {
                for (int i = 1; i < BezierPath.NumPoints - 1; i += 3)
                {
                    DrawHandle(i);
                    DrawHandle(i + 1);
                }
            }
        }
Esempio n. 5
0
        void ProcessBezierPathInput(Event e)
        {
            // Find which handle mouse is over. Start by looking at previous handle index first, as most likely to still be closest to mouse
            int previousMouseOverHandleIndex = (mouseOverHandleIndex == -1) ? 0 : mouseOverHandleIndex;

            mouseOverHandleIndex = -1;
            for (int i = 0; i < BezierPath.NumPoints; i += 3)
            {
                int     handleIndex  = (previousMouseOverHandleIndex + i) % BezierPath.NumPoints;
                float   handleRadius = GetHandleDiameter(GlobalDisplaySettings.anchorSize * Data.bezierHandleScale, BezierPath[handleIndex]) / 2f;
                Vector3 pos          = MathUtility.TransformPoint(BezierPath[handleIndex], creator.transform, BezierPath.Space);
                float   dst          = HandleUtility.DistanceToCircle(pos, handleRadius);
                if (dst == 0)
                {
                    mouseOverHandleIndex = handleIndex;
                    break;
                }
            }

            // Shift-left click (when mouse not over a handle) to split or add segment
            if (mouseOverHandleIndex == -1)
            {
                if (e.type == EventType.MouseDown && e.button == 0 && e.shift)
                {
                    UpdatePathMouseInfo();
                    // Insert point along selected segment
                    if (selectedSegmentIndex != -1 && selectedSegmentIndex < BezierPath.NumSegments)
                    {
                        Vector3 newPathPoint = pathMouseInfo.closestWorldPointToMouse;
                        newPathPoint = MathUtility.InverseTransformPoint(newPathPoint, creator.transform, BezierPath.Space);
                        Undo.RecordObject(creator, "Split segment");
                        BezierPath.SplitSegment(newPathPoint, selectedSegmentIndex, pathMouseInfo.timeOnBezierSegment);
                    }
                    // If VertexPath is not a closed loop, add new point on to the end of the VertexPath
                    else if (!BezierPath.IsClosed)
                    {
                        // If control/command are held down, the point gets pre-pended, so we want to check distance
                        // to the endpoint we are adding to
                        var pointIdx = e.control || e.command ? 0 : BezierPath.NumPoints - 1;
                        // insert new point at same dst from scene camera as the point that comes before it (for a 3d VertexPath)
                        var endPointLocal  = BezierPath[pointIdx];
                        var endPointGlobal =
                            MathUtility.TransformPoint(endPointLocal, creator.transform, BezierPath.Space);
                        var distanceCameraToEndpoint = (Camera.current.transform.position - endPointGlobal).magnitude;
                        var newPointGlobal           =
                            MouseUtility.GetMouseWorldPosition(BezierPath.Space, distanceCameraToEndpoint);
                        var newPointLocal =
                            MathUtility.InverseTransformPoint(newPointGlobal, creator.transform, BezierPath.Space);

                        Undo.RecordObject(creator, "Add segment");
                        if (e.control || e.command)
                        {
                            BezierPath.AddSegmentToStart(newPointLocal);
                        }
                        else
                        {
                            BezierPath.AddSegmentToEnd(newPointLocal);
                        }
                    }
                }
            }

            // Control click or backspace/delete to remove point
            if (e.keyCode == KeyCode.Backspace || e.keyCode == KeyCode.Delete || ((e.control || e.command) && e.type == EventType.MouseDown && e.button == 0))
            {
                if (mouseOverHandleIndex != -1)
                {
                    Undo.RecordObject(creator, "Delete segment");
                    BezierPath.DeleteSegment(mouseOverHandleIndex);
                    if (mouseOverHandleIndex == handleIndexToDisplayAsTransform)
                    {
                        handleIndexToDisplayAsTransform = -1;
                    }

                    mouseOverHandleIndex = -1;
                    Repaint();
                }
            }

            // Holding shift and moving mouse (but mouse not over a handle/dragging a handle)
            if (draggingHandleIndex == -1 && mouseOverHandleIndex == -1)
            {
                bool shiftDown = e.shift && !shiftLastFrame;
                if (shiftDown || ((e.type == EventType.MouseMove || e.type == EventType.MouseDrag) && e.shift))
                {
                    UpdatePathMouseInfo();

                    if (pathMouseInfo.mouseDstToLine < segmentSelectDistanceThreshold)
                    {
                        if (pathMouseInfo.closestSegmentIndex != selectedSegmentIndex)
                        {
                            selectedSegmentIndex = pathMouseInfo.closestSegmentIndex;
                            HandleUtility.Repaint();
                        }
                    }
                    else
                    {
                        selectedSegmentIndex = -1;
                        HandleUtility.Repaint();
                    }
                }
            }

            shiftLastFrame = e.shift;
        }
Esempio n. 6
0
        void DrawBezierPathInspector()
        {
            using (var check = new EditorGUI.ChangeCheckScope())
            {
                // Path options:
                Data.showPathOptions = EditorGUILayout.Foldout(Data.showPathOptions, new GUIContent("Bézier Path Options"), true, boldFoldoutStyle);
                if (Data.showPathOptions)
                {
                    BezierPath.Space            = (PathSpace)EditorGUILayout.Popup("Space", (int)BezierPath.Space, spaceNames);
                    BezierPath.ControlPointMode = (BezierPath.ControlMode)EditorGUILayout.EnumPopup(new GUIContent("Control Mode"), BezierPath.ControlPointMode);
                    if (BezierPath.ControlPointMode == BezierPath.ControlMode.Automatic)
                    {
                        BezierPath.AutoControlLength = EditorGUILayout.Slider(new GUIContent("Control Spacing"), BezierPath.AutoControlLength, 0, 1);
                    }

                    BezierPath.IsClosed    = EditorGUILayout.Toggle("Closed Path", BezierPath.IsClosed);
                    Data.showTransformTool = EditorGUILayout.Toggle(new GUIContent("Enable Transforms"), Data.showTransformTool);

                    Tools.hidden = !Data.showTransformTool;

                    // Check if out of bounds (can occur after undo operations)
                    if (handleIndexToDisplayAsTransform >= BezierPath.NumPoints)
                    {
                        handleIndexToDisplayAsTransform = -1;
                    }

                    // If a point has been selected
                    if (handleIndexToDisplayAsTransform != -1)
                    {
                        EditorGUILayout.LabelField("Selected Point:");

                        using (new EditorGUI.IndentLevelScope())
                        {
                            var currentPosition = creator.BezierPath[handleIndexToDisplayAsTransform];
                            var newPosition     = EditorGUILayout.Vector3Field("Position", currentPosition);
                            if (newPosition != currentPosition)
                            {
                                Undo.RecordObject(creator, "Move point");
                                creator.BezierPath.MovePoint(handleIndexToDisplayAsTransform, newPosition);
                            }
                            // Don't draw the angle field if we aren't selecting an anchor point/not in 3d space
                            if (handleIndexToDisplayAsTransform % 3 == 0 && creator.BezierPath.Space == PathSpace.xyz)
                            {
                                var anchorIndex  = handleIndexToDisplayAsTransform / 3;
                                var currentAngle = creator.BezierPath.GetAnchorNormalAngle(anchorIndex);
                                var newAngle     = EditorGUILayout.FloatField("Angle", currentAngle);
                                if (newAngle != currentAngle)
                                {
                                    Undo.RecordObject(creator, "Set Angle");
                                    creator.BezierPath.SetAnchorNormalAngle(anchorIndex, newAngle);
                                }
                            }
                        }
                    }

                    if (Data.showTransformTool & (handleIndexToDisplayAsTransform == -1))
                    {
                        if (GUILayout.Button("Centre Transform"))
                        {
                            Vector3 worldCentre  = BezierPath.CalculateBoundsWithTransform(creator.transform).center;
                            Vector3 transformPos = creator.transform.position;

                            if (BezierPath.Space == PathSpace.xy)
                            {
                                transformPos = new Vector3(transformPos.x, transformPos.y, 0);
                            }
                            else if (BezierPath.Space == PathSpace.xz)
                            {
                                transformPos = new Vector3(transformPos.x, 0, transformPos.z);
                            }

                            Vector3 worldCentreToTransform = transformPos - worldCentre;

                            if (worldCentre != creator.transform.position)
                            {
                                //Undo.RecordObject (creator, "Centralize Transform");
                                if (worldCentreToTransform != Vector3.zero)
                                {
                                    Vector3 localCentreToTransform = MathUtility.InverseTransformVector(worldCentreToTransform, creator.transform, BezierPath.Space);
                                    for (int i = 0; i < BezierPath.NumPoints; i++)
                                    {
                                        BezierPath.SetPoint(i, BezierPath.GetPoint(i) + localCentreToTransform, true);
                                    }
                                }

                                creator.transform.position = worldCentre;
                                BezierPath.NotifyPathModified();
                            }
                        }
                    }

                    if (GUILayout.Button("Reset Path"))
                    {
                        Undo.RecordObject(creator, "Reset Path");
                        bool in2DEditorMode = EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D;
                        Data.ResetBezierPath(creator.transform.position, in2DEditorMode);
                        EditorApplication.QueuePlayerLoopUpdate();
                    }

                    GUILayout.Space(inspectorSectionSpacing);
                }

                Data.showNormals = EditorGUILayout.Foldout(Data.showNormals, new GUIContent("Normals Options"), true, boldFoldoutStyle);
                if (Data.showNormals)
                {
                    BezierPath.FlipNormals = EditorGUILayout.Toggle(new GUIContent("Flip Normals"), BezierPath.FlipNormals);
                    if (BezierPath.Space == PathSpace.xyz)
                    {
                        BezierPath.GlobalNormalsAngle = EditorGUILayout.Slider(new GUIContent("Global Angle"), BezierPath.GlobalNormalsAngle, 0, 360);

                        if (GUILayout.Button("Reset Normals"))
                        {
                            Undo.RecordObject(creator, "Reset Normals");
                            BezierPath.FlipNormals = false;
                            BezierPath.ResetNormalAngles();
                        }
                    }
                    GUILayout.Space(inspectorSectionSpacing);
                }

                // Editor display options
                Data.showDisplayOptions = EditorGUILayout.Foldout(Data.showDisplayOptions, new GUIContent("Display Options"), true, boldFoldoutStyle);
                if (Data.showDisplayOptions)
                {
                    Data.showPathBounds       = GUILayout.Toggle(Data.showPathBounds, new GUIContent("Show Path Bounds"));
                    Data.showPerSegmentBounds = GUILayout.Toggle(Data.showPerSegmentBounds, new GUIContent("Show Segment Bounds"));
                    Data.displayAnchorPoints  = GUILayout.Toggle(Data.displayAnchorPoints, new GUIContent("Show Anchor Points"));

                    if (!(BezierPath.ControlPointMode == BezierPath.ControlMode.Automatic && GlobalDisplaySettings.hideAutoControls))
                    {
                        Data.displayControlPoints = GUILayout.Toggle(Data.displayControlPoints, new GUIContent("Show Control Points"));
                    }

                    Data.keepConstantHandleSize = GUILayout.Toggle(Data.keepConstantHandleSize, new GUIContent("Constant Point Size", constantSizeTooltip));
                    Data.bezierHandleScale      = Mathf.Max(0, EditorGUILayout.FloatField(new GUIContent("Handle Scale"), Data.bezierHandleScale));
                    DrawGlobalDisplaySettingsInspector();
                }

                if (check.changed)
                {
                    SceneView.RepaintAll();
                    EditorApplication.QueuePlayerLoopUpdate();
                }
            }
        }
Esempio n. 7
0
        /// Internal contructor
        VertexPath(BezierPath path, PathUtility.PathSplitData pathSplitData, Transform transform)
        {
            this.transform = transform;
            space          = path.Space;
            isClosedLoop   = path.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) * .5f, 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)
                {
                    localNormals[i] = Vector3.Cross(localTangents[i], up) * (path.FlipNormals ? 1 : -1);
                    continue;
                }

                if (i == 0)
                {
                    localNormals[0] = Vector3.Cross(lastRotationAxis, pathSplitData.tangents[0]).normalized;
                    continue;
                }

                // 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;
            }

            // Apply correction for 3d normals along a closed path
            if (space != PathSpace.xyz)
            {
                return;
            }

            if (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] * (path.FlipNormals ? -1 : 1);
                    }
                }
                return;
            }

            // Rotate normals to match up with user-defined anchor angles
            for (int anchorIndex = 0; anchorIndex < pathSplitData.anchorVertexMap.Count - 1; ++anchorIndex)
            {
                int nextAnchorIndex = isClosedLoop ? (anchorIndex + 1) % path.NumSegments : anchorIndex + 1;

                float startAngle = path.GetAnchorNormalAngle(anchorIndex) + path.GlobalNormalsAngle;
                float endAngle   = path.GetAnchorNormalAngle(nextAnchorIndex) + path.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]) * (path.FlipNormals ? -1 : 1);
                }
            }
        }
Esempio n. 8
0
 /// <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 path, Transform transform, float vertexSpacing) : this(path, PathUtility.SplitBezierPathEvenly(path, Mathf.Max(vertexSpacing, minVertexSpacing), VertexPath.accuracy), transform)
 {
 }
Esempio n. 9
0
 /// <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 path, Transform transform, float maxAngleError = 0.3f, float minVertexDst = 0) : this(path, PathUtility.SplitBezierPathByAngleError(path, maxAngleError, minVertexDst, VertexPath.accuracy), transform)
 {
 }
Esempio n. 10
0
        public ScreenSpacePolyLine(BezierPath BezierPath, Transform transform, float maxAngleError, float minVertexDst, float accuracy = 1)
        {
            this.transform    = transform;
            transformPosition = transform.position;
            transformRotation = transform.rotation;
            transformScale    = transform.localScale;

            // Split path in vertices based on angle error
            verticesWorld          = new List <Vector3>();
            vertexToPathSegmentMap = new List <int>();
            segmentStartIndices    = new int[BezierPath.NumSegments + 1];

            verticesWorld.Add(BezierPath[0]);
            vertexToPathSegmentMap.Add(0);
            Vector3 prevPointOnPath          = BezierPath[0];
            float   dstSinceLastVertex       = 0;
            Vector3 lastAddedPoint           = prevPointOnPath;
            float   dstSinceLastIntermediary = 0;

            for (int segmentIndex = 0; segmentIndex < BezierPath.NumSegments; segmentIndex++)
            {
                Vector3[] segmentPoints = BezierPath.GetPointsInSegment(segmentIndex);
                verticesWorld.Add(segmentPoints[0]);
                vertexToPathSegmentMap.Add(segmentIndex);
                segmentStartIndices[segmentIndex] = verticesWorld.Count - 1;

                prevPointOnPath          = segmentPoints[0];
                lastAddedPoint           = prevPointOnPath;
                dstSinceLastVertex       = 0;
                dstSinceLastIntermediary = 0;

                float estimatedSegmentLength = MathUtility.EstimateCurveLength(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3]);
                int   divisions = Mathf.CeilToInt(estimatedSegmentLength * accuracy * accuracyMultiplier);
                float increment = 1f / divisions;

                for (float t = increment; t <= 1; t += increment)
                {
                    Vector3 pointOnPath     = MathUtility.CubicBezier(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t);
                    Vector3 nextPointOnPath = MathUtility.CubicBezier(segmentPoints[0], segmentPoints[1], segmentPoints[2], segmentPoints[3], t + increment);

                    // angle at current point on path
                    float localAngle = 180 - MathUtility.MinAngle(prevPointOnPath, pointOnPath, nextPointOnPath);
                    // angle between the last added vertex, the current point on the path, and the next point on the path
                    float angleFromPrevVertex = 180 - MathUtility.MinAngle(lastAddedPoint, pointOnPath, nextPointOnPath);
                    float angleError          = Mathf.Max(localAngle, angleFromPrevVertex);


                    if (angleError > maxAngleError && dstSinceLastVertex >= minVertexDst)
                    {
                        dstSinceLastVertex       = 0;
                        dstSinceLastIntermediary = 0;
                        verticesWorld.Add(pointOnPath);
                        vertexToPathSegmentMap.Add(segmentIndex);
                        lastAddedPoint = pointOnPath;
                    }
                    else
                    {
                        if (dstSinceLastIntermediary > intermediaryThreshold)
                        {
                            verticesWorld.Add(pointOnPath);
                            vertexToPathSegmentMap.Add(segmentIndex);
                            dstSinceLastIntermediary = 0;
                        }
                        else
                        {
                            dstSinceLastIntermediary += (pointOnPath - prevPointOnPath).magnitude;
                        }

                        dstSinceLastVertex += (pointOnPath - prevPointOnPath).magnitude;
                    }
                    prevPointOnPath = pointOnPath;
                }
            }

            segmentStartIndices[BezierPath.NumSegments] = verticesWorld.Count;

            // ensure final point gets added (unless path is closed loop)
            verticesWorld.Add(!BezierPath.IsClosed ? BezierPath[BezierPath.NumPoints - 1] : BezierPath[0]);

            // Calculate length
            cumululativeLengthWorld = new float[verticesWorld.Count];
            for (int i = 0; i < verticesWorld.Count; i++)
            {
                verticesWorld[i] = MathUtility.TransformPoint(verticesWorld[i], transform, BezierPath.Space);
                if (i > 0)
                {
                    pathLengthWorld           += (verticesWorld[i - 1] - verticesWorld[i]).magnitude;
                    cumululativeLengthWorld[i] = pathLengthWorld;
                }
            }
        }