//Caluclate tip path trajectory public void calculatePath() { if (arrowTipMode == ArrowTipMode.None) return; if (arrowTipPathType == ArrowTipPathType.FollowMainPath) { //path is caluclated by ArrowPath class, just calculate the length pathLength = 0; for (int i = 1; i < path.Count; i++) { pathLength += (path[i] - path[i - 1]).magnitude; } return; } //Default values for all functions if not set ArrowPath.validateAnimationFunction(pathAlongXFunction, 0, 0); ArrowPath.validateAnimationFunction(pathAlongYFunction, 0, 0); ArrowPath.validateAnimationFunction(pathAlongZFunction, 0, 0); float currentPercentage = 0; path = new List<Vector3>(); //Set the first path point Vector3 tipDirection = end - start; Quaternion arrowTipRotation = Quaternion.FromToRotation(Vector3.up, tipDirection); path.Add(start + arrowTipRotation * new Vector3(pathAlongXFunction.Evaluate(0), pathAlongYFunction.Evaluate(0), pathAlongZFunction.Evaluate(0))); pathLength = 0; int[] currentKey = new int[3] { 0, 0, 0 }; int currentKeyIndex = -1; float lastPathDifference = 0; //find the next Point increasePathPercentage(ref currentKeyIndex, ref currentPercentage, ref currentKey); while (true) { Vector3 pathItem; // add points until 100% if (currentPercentage >= 1.0f) { //Put the last point exacly at 100% pathItem = end + arrowTipRotation * new Vector3(pathAlongXFunction.Evaluate(pathAlongXFunctionLength), pathAlongYFunction.Evaluate(pathAlongYFunctionLength), pathAlongZFunction.Evaluate(pathAlongZFunctionLength)); lastPathDifference = (path[path.Count - 1] - pathItem).magnitude; if (lastPathDifference > errorRate) path.Add(pathItem); pathLength += lastPathDifference; break; } pathItem = start + currentPercentage * tipDirection + arrowTipRotation * new Vector3(pathAlongXFunction.Evaluate(currentPercentage * pathAlongXFunctionLength), pathAlongYFunction.Evaluate(currentPercentage * pathAlongYFunctionLength), pathAlongZFunction.Evaluate(currentPercentage * pathAlongZFunctionLength)); lastPathDifference = (path[path.Count - 1] - pathItem).magnitude; //never put the same path point twice if (lastPathDifference > errorRate) path.Add(pathItem); pathLength += lastPathDifference; increasePathPercentage(ref currentKeyIndex, ref currentPercentage, ref currentKey); } }
//Generate the extrude array public void generate() { if (arrowTipMode == ArrowTipMode.None) { return; } //set default functions if not set ArrowPath.validateAnimationFunction(widthFunction, 1, 0); ArrowPath.validateAnimationFunction(heightFunction, 1, 0); ArrowPath.validateAnimationFunction(rotateFunction, 0, 0); ArrowPath.validateAnimationFunction(shapeFunction, 0, 0); if (pathLength == 0) { return; } extrudePoints = new Matrix4x4[path.Count]; primitives = new Primitive[path.Count]; openClosed = new int[path.Count]; float currentLength = 0; extrudePoints[0] = new Matrix4x4(); //Calculate the first extrude Vector3 pathDiff = direction * (path[1] - path[0]); arrowPath.defaultRotateAngle = defaultRotateAngle; Quaternion nextDirectionX; Quaternion nextDirectionY; Quaternion extrudeDirectionX; Quaternion extrudeDirectionY; Quaternion lastDirectionX; Quaternion lastDirectionY; //First rotation depends on first path point direction and rotation from main path nextDirectionX = arrowPath.calculateUpDownQuaternion(pathDiff); nextDirectionY = arrowPath.calculateLeftRightQuaternion(pathDiff); lastDirectionX = nextDirectionX; lastDirectionY = nextDirectionY; float propagatingRotateAngle = 0; if (direction == -1) { propagatingRotateAngle = arrowPath.rotateFunction.Evaluate(0); } else { propagatingRotateAngle = arrowPath.rotateFunction.Evaluate(arrowPath.rotationFunctionLength); } extrudePoints[0].SetTRS(path[0], Quaternion.AngleAxis((rotateFunction.Evaluate(currentLength / pathLength * rotationFunctionLength) + propagatingRotateAngle) * 360, nextDirectionY * nextDirectionX * Vector2.up) * nextDirectionY * nextDirectionX, new Vector3(widthFunction.Evaluate(currentLength / pathLength * widthFunctionLength) * size.x, 1, heightFunction.Evaluate(currentLength / pathLength * heightFunctionLength) * size.z)); //choose shape int primitiveIndex = (int)shapeFunction.Evaluate(0); if (primitiveIndex < 0) { primitiveIndex = 0; } if (primitiveIndex >= templatePrimitives.primitivesList.Count) { primitiveIndex = templatePrimitives.primitivesList.Count - 1; } primitives[0] = templatePrimitives.primitivesList[primitiveIndex]; openClosed[0] = (getClosed()) ? 0 : 1; //Calculate the other extrude points for (int i = 1; i < path.Count; i++) { currentLength += (path[i] - path[i - 1]).magnitude; extrudePoints[i] = new Matrix4x4(); float scaleWidthByAngle = 1f; float scaleHeightByAngle = 1f; if (i < path.Count - 1) { pathDiff = direction * (path[i + 1] - path[i]); nextDirectionX = arrowPath.calculateUpDownQuaternion(pathDiff); nextDirectionY = arrowPath.calculateLeftRightQuaternion(pathDiff); //Points depend on previous and next point directions if (Quaternion.Angle(lastDirectionX, nextDirectionX) < 0.1f) { extrudeDirectionX = nextDirectionX; } else { extrudeDirectionX = Quaternion.Lerp(lastDirectionX, nextDirectionX, 0.5f); } if (Quaternion.Angle(lastDirectionY, nextDirectionY) < 0.1f) { extrudeDirectionY = nextDirectionY; } else { extrudeDirectionY = Quaternion.Lerp(lastDirectionY, nextDirectionY, 0.5f); } //Scale depends on angle between points scaleWidthByAngle = Mathf.Abs(Mathf.Cos(Mathf.Deg2Rad * Vector3.Angle(lastDirectionY * Vector3.forward, nextDirectionY * Vector3.forward) / 2)); scaleWidthByAngle = (scaleWidthByAngle < 0.5f) ? 1 : 1 / scaleWidthByAngle; scaleHeightByAngle = Mathf.Abs(Mathf.Cos(Mathf.Deg2Rad * Vector3.Angle(lastDirectionX * Vector3.up, nextDirectionX * Vector3.up) / 2)); scaleHeightByAngle = (scaleHeightByAngle < 0.5f) ? 1 : 1 / scaleHeightByAngle; } else { pathDiff = direction * (path[i] - path[i - 1]); nextDirectionX = arrowPath.calculateUpDownQuaternion(pathDiff); nextDirectionY = arrowPath.calculateLeftRightQuaternion(pathDiff); extrudeDirectionX = nextDirectionX; extrudeDirectionY = nextDirectionY; } extrudePoints[i].SetTRS(path[i], Quaternion.AngleAxis((rotateFunction.Evaluate(currentLength / pathLength * rotationFunctionLength) + propagatingRotateAngle) * 360, extrudeDirectionY * extrudeDirectionX * Vector3.up) * extrudeDirectionY * extrudeDirectionX, new Vector3(scaleWidthByAngle * widthFunction.Evaluate(currentLength / pathLength * widthFunctionLength) * size.x, 1, scaleHeightByAngle * heightFunction.Evaluate(currentLength / pathLength * heightFunctionLength) * size.z)); primitiveIndex = (int)(shapeFunction.Evaluate(currentLength / pathLength * shapeFunctionLength)); if (primitiveIndex < 0) { primitiveIndex = 0; } if (primitiveIndex >= templatePrimitives.primitivesList.Count) { primitiveIndex = templatePrimitives.primitivesList.Count - 1; } primitives[i] = templatePrimitives.primitivesList[primitiveIndex]; openClosed[i] = 1; lastDirectionX = nextDirectionX; lastDirectionY = nextDirectionY; } openClosed[path.Count - 1] = 2; }