123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- namespace Pathfinding {
- using Pathfinding.Util;
-
-
-
-
-
- public class Funnel {
-
- public struct FunnelPortals {
- public List<Vector3> left;
- public List<Vector3> right;
- }
-
-
-
-
-
-
- public struct PathPart {
-
- public int startIndex;
-
- public int endIndex;
- public Vector3 startPoint, endPoint;
- public bool isLink;
- }
- public static List<PathPart> SplitIntoParts (Path path) {
- var nodes = path.path;
- var result = ListPool<PathPart>.Claim();
- if (nodes == null || nodes.Count == 0) {
- return result;
- }
-
-
- for (int i = 0; i < nodes.Count; i++) {
- if (nodes[i] is TriangleMeshNode || nodes[i] is GridNodeBase) {
- var part = new PathPart();
- part.startIndex = i;
- uint currentGraphIndex = nodes[i].GraphIndex;
-
-
- for (; i < nodes.Count; i++) {
- if (nodes[i].GraphIndex != currentGraphIndex && !(nodes[i] is NodeLink3Node)) {
- break;
- }
- }
- i--;
- part.endIndex = i;
-
-
-
- if (part.startIndex == 0) {
- part.startPoint = path.vectorPath[0];
- } else {
- part.startPoint = (Vector3)nodes[part.startIndex-1].position;
- }
- if (part.endIndex == nodes.Count-1) {
- part.endPoint = path.vectorPath[path.vectorPath.Count-1];
- } else {
- part.endPoint = (Vector3)nodes[part.endIndex+1].position;
- }
- result.Add(part);
- } else if (NodeLink2.GetNodeLink(nodes[i]) != null) {
- var part = new PathPart();
- part.startIndex = i;
- var currentGraphIndex = nodes[i].GraphIndex;
- for (i++; i < nodes.Count; i++) {
- if (nodes[i].GraphIndex != currentGraphIndex) {
- break;
- }
- }
- i--;
- if (i - part.startIndex == 0) {
-
- continue;
- } else if (i - part.startIndex != 1) {
- throw new System.Exception("NodeLink2 link length greater than two (2) nodes. " + (i - part.startIndex + 1));
- }
- part.endIndex = i;
- part.isLink = true;
- part.startPoint = (Vector3)nodes[part.startIndex].position;
- part.endPoint = (Vector3)nodes[part.endIndex].position;
- result.Add(part);
- } else {
- throw new System.Exception("Unsupported node type or null node");
- }
- }
- return result;
- }
- public static FunnelPortals ConstructFunnelPortals (List<GraphNode> nodes, PathPart part) {
- if (nodes == null || nodes.Count == 0) {
- return new FunnelPortals { left = ListPool<Vector3>.Claim(0), right = ListPool<Vector3>.Claim(0) };
- }
- if (part.endIndex < part.startIndex || part.startIndex < 0 || part.endIndex > nodes.Count) throw new System.ArgumentOutOfRangeException();
-
- var left = ListPool<Vector3>.Claim(nodes.Count+1);
- var right = ListPool<Vector3>.Claim(nodes.Count+1);
-
- left.Add(part.startPoint);
- right.Add(part.startPoint);
-
- for (int i = part.startIndex; i < part.endIndex; i++) {
-
- bool portalWasAdded = nodes[i].GetPortal(nodes[i+1], left, right, false);
- if (!portalWasAdded) {
-
- left.Add((Vector3)nodes[i].position);
- right.Add((Vector3)nodes[i].position);
- left.Add((Vector3)nodes[i+1].position);
- right.Add((Vector3)nodes[i+1].position);
- }
- }
-
- left.Add(part.endPoint);
- right.Add(part.endPoint);
- return new FunnelPortals { left = left, right = right };
- }
- 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);
- portals.left[i] = Vector3.Lerp(left, right, s);
- portals.right[i] = Vector3.Lerp(left, right, 1 - s);
- }
- }
- }
- static bool UnwrapHelper (Vector3 portalStart, Vector3 portalEnd, Vector3 prevPoint, Vector3 nextPoint, ref Quaternion mRot, ref Vector3 mOffset) {
-
- if (VectorMath.IsColinear(portalStart, portalEnd, nextPoint)) {
- return false;
- }
- var axis = portalEnd - portalStart;
- var sqrMagn = axis.sqrMagnitude;
- prevPoint -= Vector3.Dot(prevPoint - portalStart, axis)/sqrMagn * axis;
- nextPoint -= Vector3.Dot(nextPoint - portalStart, axis)/sqrMagn * axis;
- var rot = Quaternion.FromToRotation(nextPoint - portalStart, portalStart - prevPoint);
-
-
-
- mOffset += mRot * (portalStart - rot * portalStart);
- mRot *= rot;
- return true;
- }
-
-
-
-
-
-
-
-
-
-
-
- public static void Unwrap (FunnelPortals funnel, Vector2[] left, Vector2[] right) {
- int startingIndex = 1;
- var normal = Vector3.Cross(funnel.right[1] - funnel.left[0], funnel.left[1] - funnel.left[0]);
-
-
- while (normal.sqrMagnitude <= 0.00000001f && startingIndex + 1 < funnel.left.Count) {
- startingIndex++;
- normal = Vector3.Cross(funnel.right[startingIndex] - funnel.left[0], funnel.left[startingIndex] - funnel.left[0]);
- }
- left[0] = right[0] = Vector2.zero;
- var portalLeft = funnel.left[1];
- var portalRight = funnel.right[1];
- var prevPoint = funnel.left[0];
-
-
-
- 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;
- }
- }
-
-
-
-
- static int FixFunnel (Vector2[] left, Vector2[] right, int numPortals) {
- if (numPortals > left.Length || numPortals > right.Length) throw new System.ArgumentException("Arrays do not have as many elements as specified");
- if (numPortals < 3) {
- return -1;
- }
-
- int startIndex = 0;
- while (left[startIndex + 1] == left[startIndex + 2] && right[startIndex + 1] == right[startIndex + 2]) {
-
- left[startIndex + 1] = left[startIndex + 0];
- right[startIndex + 1] = right[startIndex + 0];
- startIndex++;
- if (numPortals - startIndex < 3) {
- return -1;
- }
- }
- return startIndex;
- }
- protected static Vector2 ToXZ (Vector3 p) {
- return new Vector2(p.x, p.z);
- }
- protected static Vector3 FromXZ (Vector2 p) {
- return new Vector3(p.x, 0, p.y);
- }
-
- protected static bool RightOrColinear (Vector2 a, Vector2 b) {
- return (a.x*b.y - b.x*a.y) <= 0;
- }
-
- protected static bool LeftOrColinear (Vector2 a, Vector2 b) {
- return (a.x*b.y - b.x*a.y) >= 0;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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");
-
- var leftArr = ArrayPool<Vector2>.Claim(funnel.left.Count);
- var rightArr = ArrayPool<Vector2>.Claim(funnel.left.Count);
- if (unwrap) {
- Unwrap(funnel, leftArr, rightArr);
- } else {
-
- 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) {
-
- intermediateResult.Add(0);
- intermediateResult.Add(funnel.left.Count - 1);
- } else {
- bool lastCorner;
- Calculate(leftArr, rightArr, funnel.left.Count, startIndex, intermediateResult, int.MaxValue, out lastCorner);
- }
-
- 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) {
-
- var next2D = idx >= 0 ? leftArr[idx] : rightArr[-idx];
- for (int j = prevIdx + 1; j < System.Math.Abs(idx); j++) {
-
- if (!VectorMath.LineLineIntersectionFactor(leftArr[j], rightArr[j] - leftArr[j], prev2D, next2D - prev2D, out float factor)) {
-
- factor = 0.5f;
- }
- 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]);
- }
- }
-
- ListPool<int>.Release(ref intermediateResult);
- ArrayPool<Vector2>.Release(ref leftArr);
- ArrayPool<Vector2>.Release(ref rightArr);
- return result;
- }
-
-
-
-
-
-
-
-
-
- static void Calculate (Vector2[] left, Vector2[] right, int numPortals, int startIndex, List<int> funnelPath, int maxCorners, out bool lastCorner) {
- if (left.Length != right.Length) throw new System.ArgumentException();
- lastCorner = false;
- int apexIndex = startIndex + 0;
- int rightIndex = startIndex + 1;
- int leftIndex = startIndex + 1;
- Vector2 portalApex = left[apexIndex];
- Vector2 portalLeft = left[leftIndex];
- Vector2 portalRight = right[rightIndex];
- funnelPath.Add(apexIndex);
- for (int i = startIndex + 2; i < numPortals; i++) {
- if (funnelPath.Count >= maxCorners) {
- return;
- }
- if (funnelPath.Count > 2000) {
- Debug.LogWarning("Avoiding infinite loop. Remove this check if you have this long paths.");
- break;
- }
- Vector2 pLeft = left[i];
- Vector2 pRight = right[i];
- if (LeftOrColinear(portalRight - portalApex, pRight - portalApex)) {
- if (portalApex == portalRight || RightOrColinear(portalLeft - portalApex, pRight - portalApex)) {
- portalRight = pRight;
- rightIndex = i;
- } else {
- portalApex = portalRight = portalLeft;
- i = apexIndex = rightIndex = leftIndex;
- funnelPath.Add(apexIndex);
- continue;
- }
- }
- if (RightOrColinear(portalLeft - portalApex, pLeft - portalApex)) {
- if (portalApex == portalLeft || LeftOrColinear(portalRight - portalApex, pLeft - portalApex)) {
- portalLeft = pLeft;
- leftIndex = i;
- } else {
- portalApex = portalLeft = portalRight;
- i = apexIndex = leftIndex = rightIndex;
-
-
- funnelPath.Add(-apexIndex);
- continue;
- }
- }
- }
- lastCorner = true;
- funnelPath.Add(numPortals-1);
- }
- }
- }
|