示例#1
0
        /** Unwraps the funnel portals from 3D space to 2D space.
         * The result is stored in the \a left and \a right arrays which must have lengths equal to the funnel.left and funnel.right lists.
         *
         * The input is a funnel like in the image below. It may be rotated and twisted.
         * \shadowimage{funnel_unwrap_input.png}
         * The output will be a funnel in 2D space like in the image below. All twists and bends will have been straightened out.
         * \shadowimage{funnel_unwrap_output.png}
         *
         * \see Calculate(FunnelPortals,bool,bool)
         */
        public static void Unwrap(FunnelPortals funnel, Vector2[] left, Vector2[] right)
        {
            var normal = Vector3.Cross(funnel.right[1] - funnel.left[0], funnel.left[1] - funnel.left[0]);

            left[0] = right[0] = Vector2.zero;

            var portalLeft  = funnel.left[1];
            var portalRight = funnel.right[1];
            var prevPoint   = funnel.left[0];

            // The code below is equivalent to this matrix (but a lot faster)
            // This represents a rotation around a line in 3D space
            // Matrix4x4 m = Matrix4x4.TRS(Vector3.zero, Quaternion.FromToRotation(normal, Vector3.forward), Vector3.one) * Matrix4x4.TRS(-funnel.right[0], Quaternion.identity, Vector3.one);
            Quaternion mRot    = Quaternion.FromToRotation(normal, Vector3.forward);
            Vector3    mOffset = mRot * (-funnel.right[0]);

            for (int i = 1; i < funnel.left.Count; i++)
            {
                if (UnwrapHelper(portalLeft, portalRight, prevPoint, funnel.left[i], ref mRot, ref mOffset))
                {
                    prevPoint  = portalLeft;
                    portalLeft = funnel.left[i];
                }

                left[i] = mRot * funnel.left[i] + mOffset;

                if (UnwrapHelper(portalLeft, portalRight, prevPoint, funnel.right[i], ref mRot, ref mOffset))
                {
                    prevPoint   = portalRight;
                    portalRight = funnel.right[i];
                }

                right[i] = mRot * funnel.right[i] + mOffset;
            }
        }
示例#2
0
        public static void ShrinkPortals(FunnelPortals portals, float shrink)
        {
            if (shrink <= 0.00001f)
            {
                return;
            }

            for (int i = 0; i < portals.left.Count; i++)
            {
                var left  = portals.left[i];
                var right = portals.right[i];

                var length = (left - right).magnitude;
                if (length > 0)
                {
                    float s = Mathf.Min(shrink / length, 0.4f);
                    //Good Game

                    /*portals.left[i] = Vector3.Lerp(left, right, s);
                     *                  portals.right[i] = Vector3.Lerp(left, right, 1 - s);*/
                    portals.left[i]  = VInt3.Lerp(left, right, s);
                    portals.right[i] = VInt3.Lerp(left, right, 1 - s);
                }
            }
        }
示例#3
0
        /** Unwraps the funnel portals from 3D space to 2D space.
         * The result is stored in the \a left and \a right arrays which must be at least as large as the funnel.left and funnel.right lists.
         *
         * The input is a funnel like in the image below. It may be rotated and twisted.
         * \shadowimage{funnel_unwrap_input.png}
         * The output will be a funnel in 2D space like in the image below. All twists and bends will have been straightened out.
         * \shadowimage{funnel_unwrap_output.png}
         *
         * \see #Calculate(FunnelPortals,bool,bool)
         */
        //Good Game
        //public static void Unwrap (FunnelPortals funnel, Vector2[] left, Vector2[] right) {
        public static void Unwrap(FunnelPortals funnel, VInt2[] left, VInt2[] right)
        {
            int startingIndex = 1;
            //Good Game
            //var normal = Vector3.Cross(funnel.right[1] - funnel.left[0], funnel.left[1] - funnel.left[0]);
            var normal = VInt3.Cross(funnel.right[1] - funnel.left[0], funnel.left[1] - funnel.left[0]);

            // This handles the case when the starting point is colinear with the first portal.
            // Note that left.Length is only guaranteed to be at least as large as funnel.left.Count, it may be larger.
            //Good Game
            //while (normal.sqrMagnitude <= 0.00000001f && startingIndex + 1 < funnel.left.Count) {
            while (normal.sqrMagnitude <= 0.000001 && startingIndex + 1 < funnel.left.Count)
            {
                startingIndex++;
                //Good Game
                //normal = Vector3.Cross(funnel.right[startingIndex] - funnel.left[0], funnel.left[startingIndex] - funnel.left[0]);
                normal = VInt3.Cross(funnel.right[startingIndex] - funnel.left[0], funnel.left[startingIndex] - funnel.left[0]);
            }

            left[0] = right[0] = VInt2.zero;

            var portalLeft  = funnel.left[1];
            var portalRight = funnel.right[1];
            var prevPoint   = funnel.left[0];

            // The code below is equivalent to this matrix (but a lot faster)
            // This represents a rotation around a line in 3D space
            // Matrix4x4 m = Matrix4x4.TRS(Vector3.zero, Quaternion.FromToRotation(normal, Vector3.forward), Vector3.one) * Matrix4x4.TRS(-funnel.right[0], Quaternion.identity, Vector3.one);
            Quaternion mRot    = Quaternion.FromToRotation((Vector3)normal, Vector3.forward);
            Vector3    mOffset = mRot * (-(Vector3)funnel.right[0]);

            for (int i = 1; i < funnel.left.Count; i++)
            {
                //Good Game
                //if (UnwrapHelper((Vector3)portalLeft, (Vector3)portalRight, (Vector3)prevPoint, (Vector3)funnel.left[i], ref mRot, ref mOffset)) {
                if (UnwrapHelper((Vector3)portalLeft, (Vector3)portalRight, (Vector3)prevPoint, (Vector3)funnel.left[i], ref mRot, ref mOffset))
                {
                    prevPoint  = portalLeft;
                    portalLeft = funnel.left[i];
                }

                left[i] = VInt2.FromInt3XZ((VInt3)(mRot * (Vector3)funnel.left[i] + mOffset));

                //Good Game
                //if (UnwrapHelper(portalLeft, portalRight, prevPoint, funnel.right[i], ref mRot, ref mOffset)) {
                if (UnwrapHelper((Vector3)portalLeft, (Vector3)portalRight, (Vector3)prevPoint, (Vector3)funnel.right[i], ref mRot, ref mOffset))
                {
                    prevPoint   = portalRight;
                    portalRight = funnel.right[i];
                }

                right[i] = VInt2.FromInt3XZ((VInt3)(mRot * (Vector3)funnel.right[i] + mOffset));
            }
        }
示例#4
0
        /// <summary>
        /// Calculate the shortest path through the funnel.
        ///
        /// If the unwrap option is disabled the funnel will simply be projected onto the XZ plane.
        /// If the unwrap option is enabled then the funnel may be oriented arbitrarily and may have twists and bends.
        /// This makes it possible to support the funnel algorithm in XY space as well as in more complicated cases, such
        /// as on curved worlds.
        /// [Open online documentation to see images]
        ///
        /// [Open online documentation to see images]
        ///
        /// See: Unwrap
        /// </summary>
        /// <param name="funnel">The portals of the funnel. The first and last vertices portals must be single points (so for example left[0] == right[0]).</param>
        /// <param name="unwrap">Determines if twists and bends should be straightened out before running the funnel algorithm.</param>
        /// <param name="splitAtEveryPortal">If true, then a vertex will be inserted every time the path crosses a portal
        ///  instead of only at the corners of the path. The result will have exactly one vertex per portal if this is enabled.
        ///  This may introduce vertices with the same position in the output (esp. in corners where many portals meet).</param>
        public static List <Vector3> Calculate(FunnelPortals funnel, bool unwrap, bool splitAtEveryPortal)
        {
            if (funnel.left.Count != funnel.right.Count)
            {
                throw new System.ArgumentException("funnel.left.Count != funnel.right.Count");
            }

            // Get arrays at least as large as the number of portals
            var leftArr = ArrayPool <Vector2> .Claim(funnel.left.Count);

            var rightArr = ArrayPool <Vector2> .Claim(funnel.left.Count);

            if (unwrap)
            {
                Unwrap(funnel, leftArr, rightArr);
            }
            else
            {
                // Copy to arrays
                for (int i = 0; i < funnel.left.Count; i++)
                {
                    leftArr[i]  = ToXZ(funnel.left[i]);
                    rightArr[i] = ToXZ(funnel.right[i]);
                }
            }

            int startIndex         = FixFunnel(leftArr, rightArr, funnel.left.Count);
            var intermediateResult = ListPool <int> .Claim();

            if (startIndex == -1)
            {
                // If funnel algorithm failed, fall back to a simple line
                intermediateResult.Add(0);
                intermediateResult.Add(funnel.left.Count - 1);
            }
            else
            {
                bool lastCorner;
                Calculate(leftArr, rightArr, funnel.left.Count, startIndex, intermediateResult, int.MaxValue, out lastCorner);
            }

            // Get list for the final result
            var result = ListPool <Vector3> .Claim(intermediateResult.Count);

            Vector2 prev2D  = leftArr[0];
            var     prevIdx = 0;

            for (int i = 0; i < intermediateResult.Count; i++)
            {
                var idx = intermediateResult[i];

                if (splitAtEveryPortal)
                {
                    // Check intersections with every portal segment
                    var next2D = idx >= 0 ? leftArr[idx] : rightArr[-idx];
                    for (int j = prevIdx + 1; j < System.Math.Abs(idx); j++)
                    {
                        var factor = VectorMath.LineIntersectionFactorXZ(FromXZ(leftArr[j]), FromXZ(rightArr[j]), FromXZ(prev2D), FromXZ(next2D));
                        result.Add(Vector3.Lerp(funnel.left[j], funnel.right[j], factor));
                    }

                    prevIdx = Mathf.Abs(idx);
                    prev2D  = next2D;
                }

                if (idx >= 0)
                {
                    result.Add(funnel.left[idx]);
                }
                else
                {
                    result.Add(funnel.right[-idx]);
                }
            }

            // Release lists back to the pool
            ListPool <int> .Release(ref intermediateResult);

            ArrayPool <Vector2> .Release(ref leftArr);

            ArrayPool <Vector2> .Release(ref rightArr);

            return(result);
        }
示例#5
0
        /** Calculate the shortest path through the funnel.
         * \param funnel The portals of the funnel. The first and last vertices portals must be single points (so for example left[0] == right[0]).
         * \param unwrap Determines if twists and bends should be straightened out before running the funnel algorithm.
         * \param splitAtEveryPortal If true, then a vertex will be inserted every time the path crosses a portal
         *  instead of only at the corners of the path. The result will have exactly one vertex per portal if this is enabled.
         *  This may introduce vertices with the same position in the output (esp. in corners where many portals meet).
         *
         * If the unwrap option is disabled the funnel will simply be projected onto the XZ plane.
         * If the unwrap option is enabled then the funnel may be oriented arbitrarily and may have twists and bends.
         * This makes it possible to support the funnel algorithm in XY space as well as in more complicated cases, such
         * as on curved worlds.
         * \shadowimage{funnel_unwrap_illustration.png}
         *
         * \shadowimage{funnel_split_at_every_portal.png}
         *
         * \see Unwrap
         */
        public static List <Vector3> Calculate(FunnelPortals funnel, bool unwrap, bool splitAtEveryPortal)
        {
            var leftArr  = new Vector2[funnel.left.Count];
            var rightArr = new Vector2[funnel.left.Count];

            if (unwrap)
            {
                Unwrap(funnel, leftArr, rightArr);
            }
            else
            {
                // Copy to arrays
                for (int i = 0; i < leftArr.Length; i++)
                {
                    leftArr[i]  = ToXZ(funnel.left[i]);
                    rightArr[i] = ToXZ(funnel.right[i]);
                }
            }

            var origLeft   = leftArr;
            int startIndex = FixFunnel(ref leftArr, ref rightArr);

            var left3D  = funnel.left;
            var right3D = funnel.right;

            if (origLeft != leftArr)
            {
                // Flipped order
                left3D  = funnel.right;
                right3D = funnel.left;
            }

            var intermediateResult = ListPool <int> .Claim();

            if (startIndex == -1)
            {
                // If funnel algorithm failed, degrade to simple line
                intermediateResult.Add(0);
                intermediateResult.Add(funnel.left.Count - 1);
            }
            else
            {
                bool lastCorner;
                Calculate(leftArr, rightArr, startIndex, intermediateResult, int.MaxValue, out lastCorner);
            }

            // Get list for the final result
            var result = ListPool <Vector3> .Claim(intermediateResult.Count);

            Vector2 prev2D  = leftArr[0];
            var     prevIdx = 0;

            for (int i = 0; i < intermediateResult.Count; i++)
            {
                var idx = intermediateResult[i];

                if (splitAtEveryPortal)
                {
                    // Check intersections with every portal segment
                    var next2D = idx >= 0 ? leftArr[idx] : rightArr[-idx];
                    for (int j = prevIdx + 1; j < System.Math.Abs(idx); j++)
                    {
                        var factor = VectorMath.LineIntersectionFactorXZ(FromXZ(leftArr[j]), FromXZ(rightArr[j]), FromXZ(prev2D), FromXZ(next2D));
                        result.Add(Vector3.Lerp(left3D[j], right3D[j], factor));
                    }

                    prevIdx = Mathf.Abs(idx);
                    prev2D  = next2D;
                }

                if (idx >= 0)
                {
                    result.Add(left3D[idx]);
                }
                else
                {
                    result.Add(right3D[-idx]);
                }
            }

            // Release lists back to the pool
            ListPool <Vector3> .Release(funnel.left);

            ListPool <Vector3> .Release(funnel.right);

            ListPool <int> .Release(intermediateResult);

            return(result);
        }