private static float GetNodeWeight(Vector3 prev, Vector3 node, Vector3 next, SerpentineFactors factors, float maxHeight) /// returns node candidate rating 0-infinity, lower is better { //incline Vector3 prevDelta = prev - node; float prevHorDist = ((Vector2D)prevDelta).Magnitude; float prevElevation = prevDelta.y > 0 ? prevDelta.y : -prevDelta.y; float prevIncline = prevElevation / prevHorDist; Vector3 nextDelta = node - next; float nextHorDist = ((Vector2D)nextDelta).Magnitude; float nextElevation = nextDelta.y > 0 ? nextDelta.y : -nextDelta.y; float nextIncline = nextElevation / nextHorDist; float inclineRating = prevIncline > nextIncline ? prevIncline : nextIncline; //0 (planar), 1 (45 degree), to infinity (90 degree) inclineRating = 1 - Mathf.Atan(inclineRating) / (Mathf.PI / 2f); //1 (planar), 0.5 (45 degree), 0 (90 degree) //inclineRating *= inclineRating; //length Vector2D vec = ((Vector2D)(prev - next)).Normalized; float prevDot = Vector2D.Dot(vec, ((Vector2D)(node - prev)).Normalized); float nextDot = Vector2D.Dot(vec, ((Vector2D)(node - next)).Normalized); float length = ((Vector2D)(prev - next)).Magnitude; float nLength = ((Vector2D)(prev - node)).Magnitude + ((Vector2D)(node - next)).Magnitude; float lengthRating = length / nLength; //1 (straight), 0.7071 (90 degree), 0 (sharp corner) //lengthRating *= lengthRating; //height float heightRating = node.y / maxHeight; if (factors.height < 0) { heightRating = 1 - heightRating; } // if (lengthFactor < 1) lengthFactor = 1; //now 1 to 2 only // lengthFactor -= 1; //now 0 to 1; // if (lengthFactor == 1) lengthFactor = float.MaxValue; // else lengthFactor = 1 / (1-lengthFactor); //now 0 to infinity float rating = inclineRating * factors.incline + lengthRating * factors.length + heightRating * Mathf.Abs(factors.height); //Mathf.Pow(inclineRating, (1-factors.incline)) * Mathf.Pow(lengthRating, (1-factors.length)); if (tmpIteration == tmpHighlightIteration && tmpNode == tmpHighlightNode) { DebugGizmos.DrawLabel("Serp", node, "Node:" + tmpNode + " var:" + tmpVariant + "\nInc:" + inclineRating.ToString() + "(" + prevIncline.ToString() + ", " + nextIncline.ToString() + ")" + "\nLen:" + lengthRating.ToString() + //"(" + length + ", " + nLength + ")" + "\nSum:" + rating.ToString(), additive: true); DebugGizmos.DrawLine("Serp", node, prev, Color.red, additive: true); DebugGizmos.DrawDot("Serp", node, 6, Color.red, additive: true); } return(rating); }
public static void SerpentineUnordered(MatrixWorld heights, Spline spline, float segLength, int iterations, SerpentineFactors factors) { DebugGizmos.Clear("Serp"); factors.Normalize(); float DistFn(Vector3 n1, Vector3 n2) => Mathf.Sqrt((n1.x - n2.x) * (n1.x - n2.x) + (n1.z - n2.z) * (n1.z - n2.z)); CoordRect rect = heights.rect; Coord rectMin = heights.rect.offset; Coord rectMax = heights.rect.offset + heights.rect.size; float pixelSize = heights.PixelSize.x; for (int i = 0; i < iterations; i++) { spline.SubdivideDist(segLength, DistFn); Floor(heights, spline); tmpIteration = i; tmpHighlightIteration = iterations - 1; //finding node with lowest rating float lowestRating = float.MaxValue; int lowestNum = 0; for (int n = 1; n < spline.nodes.Length - 1; n++) { float rating = GetNodeWeight(spline.nodes[n - 1], spline.nodes[n], spline.nodes[n + 1], factors, heights.worldSize.y); if (rating < lowestRating) { lowestRating = rating; lowestNum = n; } } tmpNode = lowestNum; float weight = GetNodeWeight(spline.nodes[lowestNum - 1], spline.nodes[lowestNum], spline.nodes[lowestNum + 1], factors, heights.worldSize.y); Vector3 moveVector = GetMoveVector(heights, spline.nodes[lowestNum - 1], spline.nodes[lowestNum], spline.nodes[lowestNum + 1], weight, 1, heights.PixelSize.x, factors); spline.nodes[lowestNum] += moveVector; //*heights.PixelSize*0.2f;// * (2f/iterations); spline.Weld(segLength / 2, DistFn); } }
private static Vector3 GetMoveVector(MatrixWorld heights, Vector3 prev, Vector3 node, Vector3 next, float thisWeight, int evalCount, float evalStep, SerpentineFactors factors) { Vector3 tan = (next - prev).normalized; Vector3 perp = Vector3.Cross(tan, Vector3.up); Vector3 minNode = node; float minWeight = thisWeight; float lWeights = 0; float rWeights = 0; float topLeftRating = 0; float topRightRating = 0; for (int p = 1; p <= evalCount; p++) { Vector3 lNode = node + perp * (evalStep * p); lNode.y = heights.GetWorldInterpolatedValue(lNode.x, lNode.z) * heights.worldSize.y; float lRating = GetNodeWeight(prev, lNode, next, factors, heights.worldSize.y); //lWeight = lWeight*(1-(float)p/evalCount) + thisWeight*((float)p/evalCount); lWeights += lRating; if (lRating > topLeftRating) { topLeftRating = lRating; } Vector3 rNode = node - perp * (evalStep * p); rNode.y = heights.GetWorldInterpolatedValue(rNode.x, rNode.z) * heights.worldSize.y; float rRating = GetNodeWeight(prev, rNode, next, factors, heights.worldSize.y); //rWeight = rWeight*(1-(float)p/evalCount) + thisWeight*((float)p/evalCount); rWeights += rRating; if (rRating > topRightRating) { topRightRating = rRating; } } //return perp*(lWeights-rWeights); //return perp*(topLeftWeight-topRightWeight); if (topLeftRating > topRightRating && topLeftRating > thisWeight) { return(perp * (topLeftRating - thisWeight)); } else if (topRightRating > topLeftRating && topRightRating > thisWeight) { return(-perp * (topRightRating - thisWeight)); } else { return(Vector3.zero); } }
public static void Serpentine(MatrixWorld heights, Spline spline, float segLength, int iterations, SerpentineFactors factors) { DebugGizmos.Clear("Serp"); factors.Normalize(); float DistFn(Vector3 n1, Vector3 n2) => Mathf.Sqrt((n1.x - n2.x) * (n1.x - n2.x) + (n1.z - n2.z) * (n1.z - n2.z)); for (int i = 0; i < iterations; i++) { tmpIteration = i; tmpHighlightIteration = iterations - 1; spline.SubdivideDist(segLength, DistFn); Floor(heights, spline); CoordRect rect = heights.rect; Coord rectMin = heights.rect.offset; Coord rectMax = heights.rect.offset + heights.rect.size; float pixelSize = heights.PixelSize.x; Vector3[] newNodes = new Vector3[spline.nodes.Length]; for (int n = 1; n < spline.nodes.Length - 1; n++) { //if (n>2) { newNodes[n]=spline.nodes[n]; continue; } tmpNode = n; float weight = GetNodeWeight(spline.nodes[n - 1], spline.nodes[n], spline.nodes[n + 1], factors, heights.worldSize.y); Vector3 moveVector = GetMoveVector(heights, spline.nodes[n - 1], spline.nodes[n], spline.nodes[n + 1], weight, 1, heights.PixelSize.x, factors); newNodes[n] = spline.nodes[n] + moveVector * 3f; //*heights.PixelSize*0.2f;// * (2f/iterations); if (tmpHighlightIteration == i) { DebugGizmos.DrawRay("Serp", newNodes[n], moveVector, Color.yellow, additive: true); } } newNodes[0] = spline.nodes[0]; newNodes[newNodes.Length - 1] = spline.nodes[spline.nodes.Length - 1]; spline.nodes = newNodes; spline.Weld(segLength / 2, DistFn); } }