public static Vector3 Calculate(Vector3 p0, Vector3 p1, Vector3 p2, float t) { Vector3 a, b, c, d; float ct = 0; if (t < 0.5f) { a = p0; b = p0; c = p1; d = p2; ct = t * 2.0f; } else if (t > 0.5f) { a = p0; b = p1; c = p2; d = p2; ct = t * 2.0f - 1.0f; } else { return(p1); } return(SplineMaths.CalculateHermite(a, b, c, d, ct, -0.80f, 0)); // return SplineMaths.CalculateBezierPoint(a, b, c, d, ct); // return SplineMaths.CalculateCatmullRom(a, b, c, d, ct); }
public Vector3 GetTrackCross(float t) { float curveT = 1.0f / numberOfCurves; int point = Mathf.FloorToInt(t / curveT); float ct = Mathf.Clamp01((t - point * curveT) * numberOfCurves); float hermite = SplineMaths.CalculateHermite(ct); TrackBuildRPoint pointA = GetPoint(point); TrackBuildRPoint pointB = GetPoint(point + 1); return(Vector3.Slerp(pointA.trackCross, pointB.trackCross, hermite)); }
public float GetTrackCrownAngle(float t) { float curveT = 1.0f / numberOfCurves; int point = Mathf.FloorToInt(t / curveT); float ct = Mathf.Clamp01((t - point * curveT) * numberOfCurves); float hermite = SplineMaths.CalculateHermite(ct); TrackBuildRPoint pointA = GetPoint(point); TrackBuildRPoint pointB = GetPoint(point + 1); return(Mathf.LerpAngle(pointA.crownAngle, pointB.crownAngle, hermite)); }
public Vector3 GetTrackDirection(float t) { float curveT = 1.0f / numberOfCurves; int point = Mathf.FloorToInt(t / curveT); float ct = Mathf.Clamp01((t - point * curveT) * numberOfCurves); float hermite = SplineMaths.CalculateHermite(ct); TrackBuildRPoint pointA = GetPoint(point); TrackBuildRPoint pointB = GetPoint(point + 1); Quaternion trackDirectionA = Quaternion.LookRotation(pointA.trackDirection); Quaternion trackDirectionB = Quaternion.LookRotation(pointB.trackDirection); Quaternion trackDirectionQ = Quaternion.Slerp(trackDirectionA, trackDirectionB, hermite); return(trackDirectionQ * Vector3.forward); }
public void RecalculateCurves() { if (_points.Count < 2)//there is no track with only one point :o) { return; } if (isDirty) { _tangentsGenerated = false; _lightmapGenerated = false; _optimised = false; } //calculate approx bezier arc length per curve for (int i = 0; i < realNumberOfPoints; i++) { TrackBuildRPoint pointA = GetPoint(i); TrackBuildRPoint pointB = GetPoint(i + 1); float thisArcLength = 0; thisArcLength += Vector3.Distance(pointA.position, pointA.forwardControlPoint); thisArcLength += Vector3.Distance(pointA.forwardControlPoint, pointB.backwardControlPoint); thisArcLength += Vector3.Distance(pointB.backwardControlPoint, pointB.position); _points[i].arcLength = thisArcLength; if (thisArcLength == 0) { DestroyImmediate(pointA); i--; } } for (int i = 0; i < realNumberOfPoints; i++) { TrackBuildRPoint pointA = GetPoint(i); TrackBuildRPoint pointB = GetPoint(i + 1); TrackBuildRPoint pointC = GetPoint(i - 1); pointA.nextPoint = pointB; pointA.lastPoint = pointC; pointA.pointName = "point " + i; } foreach (TrackBuildRPoint curve in _points) { if (!curve.curveIsDirty)//only recalculate modified points { continue; } if (curve.arcLength > 0) { TrackBuildRPoint pointA = curve; TrackBuildRPoint pointB = curve.nextPoint; //Build accurate arc length data into curve curve.center = Vector3.zero; int arcLengthResolution = Mathf.Max(Mathf.RoundToInt(curve.arcLength * 10), 1); float alTime = 1.0f / arcLengthResolution; float calculatedTotalArcLength = 0; curve.storedArcLengthsFull = new float[arcLengthResolution]; curve.storedArcLengthsFull[0] = 0.0f; Vector3 pA = curve.position; for (int i = 0; i < arcLengthResolution - 1; i++) { curve.center += pA; float altB = alTime * (i + 1) + alTime; Vector3 pB = SplineMaths.CalculateBezierPoint(altB, curve.position, curve.forwardControlPoint, pointB.backwardControlPoint, pointB.position); float arcLength = Vector3.Distance(pA, pB); calculatedTotalArcLength += arcLength; curve.storedArcLengthsFull[i + 1] = calculatedTotalArcLength; pA = pB;//switch over values so we only calculate the bezier once } curve.arcLength = calculatedTotalArcLength; curve.center /= arcLengthResolution; int storedPointSize = Mathf.RoundToInt(calculatedTotalArcLength / meshResolution); curve.storedPointSize = storedPointSize; curve.normalisedT = new float[storedPointSize]; curve.targetSize = new float[storedPointSize]; curve.midPointPerc = new float[storedPointSize]; curve.prevNormIndex = new int[storedPointSize]; curve.nextNormIndex = new int[storedPointSize]; curve.clipArrayLeft = new bool[storedPointSize]; curve.clipArrayRight = new bool[storedPointSize]; //calculate normalised spline data int normalisedIndex = 0; curve.normalisedT[0] = 0; for (int p = 1; p < storedPointSize; p++) { float t = p / (float)(storedPointSize - 1); float targetLength = t * calculatedTotalArcLength; curve.targetSize[p] = targetLength; int it = 1000; while (targetLength > curve.storedArcLengthsFull[normalisedIndex]) { normalisedIndex++; it--; if (it < 0) { curve.normalisedT[p] = 0; break; } } normalisedIndex = Mathf.Min(normalisedIndex, arcLengthResolution);//ensure we've not exceeded the length int prevNormIndex = Mathf.Max((normalisedIndex - 1), 0); int nextNormIndex = normalisedIndex; float lengthBefore = curve.storedArcLengthsFull[prevNormIndex]; float lengthAfter = curve.storedArcLengthsFull[nextNormIndex]; float midPointPercentage = (targetLength - lengthBefore) / (lengthAfter - lengthBefore); curve.midPointPerc[p] = midPointPercentage; curve.prevNormIndex[p] = prevNormIndex; curve.nextNormIndex[p] = nextNormIndex; float normalisedT = (normalisedIndex + midPointPercentage) / arcLengthResolution;//lerp between the values to get the exact normal T curve.normalisedT[p] = normalisedT; } curve.sampledPoints = new Vector3[storedPointSize]; curve.sampledLeftBoundaryPoints = new Vector3[storedPointSize]; curve.sampledRightBoundaryPoints = new Vector3[storedPointSize]; curve.sampledWidths = new float[storedPointSize]; curve.sampledCrowns = new float[storedPointSize]; curve.sampledTrackDirections = new Vector3[storedPointSize]; curve.sampledTrackUps = new Vector3[storedPointSize]; curve.sampledTrackCrosses = new Vector3[storedPointSize]; curve.sampledAngles = new float[storedPointSize]; for (int p = 0; p < storedPointSize; p++) { float tN = curve.normalisedT[p]; float tH = SplineMaths.CalculateHermite(tN); curve.sampledPoints[p] = SplineMaths.CalculateBezierPoint(tN, pointA.position, pointA.forwardControlPoint, pointB.backwardControlPoint, pointB.position); curve.sampledLeftBoundaryPoints[p] = SplineMaths.CalculateBezierPoint(tN, pointA.leftTrackBoundary, pointA.leftForwardControlPoint, pointB.leftBackwardControlPoint, pointB.leftTrackBoundary); curve.sampledRightBoundaryPoints[p] = SplineMaths.CalculateBezierPoint(tN, pointA.rightTrackBoundary, pointA.rightForwardControlPoint, pointB.rightBackwardControlPoint, pointB.rightTrackBoundary); curve.sampledWidths[p] = Mathf.LerpAngle(pointA.width, pointB.width, tH); curve.sampledCrowns[p] = Mathf.LerpAngle(pointA.crownAngle, pointB.crownAngle, tH); curve.sampledTrackUps[p] = Quaternion.Slerp(pointA.trackUpQ, pointB.trackUpQ, tH) * Vector3.forward; curve.clipArrayLeft[p] = true; curve.clipArrayRight[p] = true; } } } for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = _points[i]; if (!curve.curveIsDirty)//only recalculate modified points { continue; } if (curve.arcLength > 0) { int lastCurveIndex = (i > 0) ? i - 1 : (_looped) ? numberOfCurves - 1 : 0; int nextCurveIndex = (i < numberOfCurves - 1) ? i + 1 : (_looped) ? 0 : numberOfCurves - 1; TrackBuildRPoint lastcurve = _points[lastCurveIndex]; TrackBuildRPoint nextcurve = _points[nextCurveIndex]; int storedPointSize = curve.storedPointSize; for (int p = 0; p < storedPointSize; p++) { int pA = p - 1; int pB = p; int pC = p + 1; if (pA < 0) { pA = 0; } if (pC >= storedPointSize) { pC = storedPointSize - 1; } Vector3 sampledPointA = curve.sampledPoints[pA]; Vector3 sampledPointB = curve.sampledPoints[pB]; Vector3 sampledPointC = curve.sampledPoints[pC]; if (p == 0 && lastcurve != null && lastcurve.sampledPoints.Length > 1) { sampledPointA = lastcurve.sampledPoints[lastcurve.storedPointSize - 2];//retrieve the penultimate point from the last curve } if (p == storedPointSize - 1 && nextcurve != null && nextcurve.sampledPoints.Length > 1) { sampledPointC = nextcurve.sampledPoints[1];//retrieve the second point from the next curve } Vector3 sampledTrackDirectionA = (sampledPointB - sampledPointA); Vector3 sampledTrackDirectionB = (sampledPointC - sampledPointB); Vector3 sampledTrackDirection = (sampledTrackDirectionA + sampledTrackDirectionB).normalized; curve.sampledTrackDirections[pB] = sampledTrackDirection; curve.sampledTrackCrosses[pB] = Vector3.Cross(curve.sampledTrackUps[pB], sampledTrackDirection); curve.sampledAngles[pB] = Vector3.Angle(sampledTrackDirectionA, sampledTrackDirectionB) * -Mathf.Sign(Vector3.Dot((sampledTrackDirectionB - sampledTrackDirectionA), curve.sampledTrackCrosses[pB])); curve.clipArrayLeft[pB] = true; curve.clipArrayRight[pB] = true; } } } bool dirtyTextures = false; foreach (TrackBuildRTexture texture in _textures) { if (texture.isDirty) { dirtyTextures = true;//if nextNormIndex point was dirty, ensure it's rerendered } texture.isDirty = false; } foreach (TrackBuildRPoint point in _points) { if (point.curveIsDirty || dirtyTextures) { point.shouldReRender = true;//if nextNormIndex point was dirty, ensure it's rerendered } } //clean points foreach (TrackBuildRPoint point in _points) { point.isDirty = false;//reset all points - data is no longer considered dirty } //recalculate track length _trackLength = 0; foreach (TrackBuildRPoint curve in _points) { _trackLength += curve.arcLength; } }
public void RecalculateCurves() { if (_points.Count < 2)//there is no track with only one point :o) { return; } if (isDirty) { _tangentsGenerated = false; _lightmapGenerated = false; _optimised = false; } //calculate approx bezier arc length per curve float roughArcLength = 0; for (int i = 0; i < realNumberOfPoints; i++) { TrackBuildRPoint pointA = GetPoint(i); TrackBuildRPoint pointB = GetPoint(i + 1); roughArcLength += Vector3.Distance(pointA.position, pointA.forwardControlPoint); roughArcLength += Vector3.Distance(pointA.forwardControlPoint, pointB.backwardControlPoint); roughArcLength += Vector3.Distance(pointB.backwardControlPoint, pointB.position); _points[i].arcLength = roughArcLength; if (roughArcLength == 0) { DestroyImmediate(pointA); i--; } } for (int i = 0; i < realNumberOfPoints; i++) { TrackBuildRPoint pointA = GetPoint(i); TrackBuildRPoint pointB = GetPoint(i + 1); TrackBuildRPoint pointC = GetPoint(i - 1); pointA.nextPoint = pointB; pointA.lastPoint = pointC; pointA.pointName = "point " + i; } foreach (TrackBuildRPoint curve in _points) { if (!curve.curveIsDirty)//only recalculate modified points { continue; } if (curve.arcLength > 0) { TrackBuildRPoint pointA = curve; TrackBuildRPoint pointB = curve.nextPoint; //Build accurate arc length data into curve curve.center = Vector3.zero; Vector3 curveA = pointA.position; Vector3 curveP = pointA.forwardControlPoint; Vector3 curveQ = pointB.backwardControlPoint; Vector3 curveB = pointB.position; Vector3 curveALTB = pointA.leftTrackBoundary; Vector3 curveARTB = pointA.rightTrackBoundary; Vector3 curveBLTB = pointB.leftTrackBoundary; Vector3 curveBRTB = pointB.rightTrackBoundary; Vector3 curveALFCP = pointA.leftForwardControlPoint; // Vector3 curveALBCP = pointA.leftBackwardControlPoint; Vector3 curveARFCP = pointA.rightForwardControlPoint; // Vector3 curveARBCP = pointA.rightBackwardControlPoint; // Vector3 curveBLFCP = pointB.leftForwardControlPoint; Vector3 curveBLBCP = pointB.leftBackwardControlPoint; // Vector3 curveBRFCP = pointB.rightForwardControlPoint; Vector3 curveBRBCP = pointB.rightBackwardControlPoint; int _dataSize = Mathf.Max(Mathf.RoundToInt(curve.arcLength / meshResolution), 1); float normilisePercentAmount = 1.0f / (_dataSize * 10); float normalisePercent = 0; float targetMovement = curve.arcLength / (_dataSize - 1); List <float> normValues = new List <float>(); normValues.Add(0); float currentLength = 0; float targetLength = targetMovement; float totalArcLength = 0; Vector3 pA = curveA, pB; for (; normalisePercent < 1.0f - normilisePercentAmount; normalisePercent += normilisePercentAmount) { pB = SplineMaths.CalculateBezierPoint(normalisePercent, curveA, curveP, curveQ, curveB); float arcLength = Vector3.Distance(pA, pB); if (currentLength + arcLength > targetLength) { float lerpPoint = (targetLength - currentLength) / arcLength; float normValue = Mathf.Lerp(normalisePercent, normalisePercent + normilisePercentAmount, lerpPoint); normValues.Add(normValue); curve.center += pB; currentLength = targetLength; targetLength += targetMovement; } currentLength += arcLength; totalArcLength += arcLength; pA = pB; } normValues.Add(1); int storedPointSize = normValues.Count; curve.normalisedT = normValues.ToArray(); curve.arcLength = totalArcLength; curve.center /= (storedPointSize - 2); curve.storedPointSize = storedPointSize; curve.clipArrayLeft = new bool[storedPointSize]; curve.clipArrayRight = new bool[storedPointSize]; curve.sampledPoints = new Vector3[storedPointSize]; curve.sampledLeftBoundaryPoints = new Vector3[storedPointSize]; curve.sampledRightBoundaryPoints = new Vector3[storedPointSize]; curve.sampledWidths = new float[storedPointSize]; curve.sampledCrowns = new float[storedPointSize]; curve.sampledTrackDirections = new Vector3[storedPointSize]; curve.sampledTrackUps = new Vector3[storedPointSize]; curve.sampledTrackCrosses = new Vector3[storedPointSize]; curve.sampledAngles = new float[storedPointSize]; for (int p = 0; p < storedPointSize; p++) { float tN = curve.normalisedT[p]; float tH = SplineMaths.CalculateHermite(tN); curve.sampledPoints[p] = SplineMaths.CalculateBezierPoint(tN, curveA, curveP, curveQ, curveB); curve.sampledLeftBoundaryPoints[p] = SplineMaths.CalculateBezierPoint(tN, curveALTB, curveALFCP, curveBLBCP, curveBLTB); curve.sampledRightBoundaryPoints[p] = SplineMaths.CalculateBezierPoint(tN, curveARTB, curveARFCP, curveBRBCP, curveBRTB); curve.sampledWidths[p] = Mathf.LerpAngle(pointA.width, pointB.width, tH); curve.sampledCrowns[p] = Mathf.LerpAngle(pointA.crownAngle, pointB.crownAngle, tH); curve.sampledTrackUps[p] = Quaternion.Slerp(pointA.trackUpQ, pointB.trackUpQ, tH) * Vector3.forward; curve.clipArrayLeft[p] = true; curve.clipArrayRight[p] = true; } } } for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = _points[i]; if (!curve.curveIsDirty)//only recalculate modified points { continue; } if (curve.arcLength > 0) { int lastCurveIndex = (i > 0) ? i - 1 : (_looped) ? numberOfCurves - 1 : 0; int nextCurveIndex = (i < numberOfCurves - 1) ? i + 1 : (_looped) ? 0 : numberOfCurves - 1; TrackBuildRPoint lastcurve = _points[lastCurveIndex]; TrackBuildRPoint nextcurve = _points[nextCurveIndex]; int storedPointSize = curve.storedPointSize; for (int p = 0; p < storedPointSize; p++) { int pA = p - 1; int pB = p; int pC = p + 1; if (pA < 0) { pA = 0; } if (pC >= storedPointSize) { pC = storedPointSize - 1; } Vector3 sampledPointA = curve.sampledPoints[pA]; Vector3 sampledPointB = curve.sampledPoints[pB]; Vector3 sampledPointC = curve.sampledPoints[pC]; if (p == 0 && lastcurve != null && lastcurve.sampledPoints.Length > 1) { sampledPointA = lastcurve.sampledPoints[lastcurve.storedPointSize - 2];//retrieve the penultimate point from the last curve } if (p == storedPointSize - 1 && nextcurve != null && nextcurve.sampledPoints.Length > 1) { sampledPointC = nextcurve.sampledPoints[1];//retrieve the second point from the next curve } Vector3 sampledTrackDirectionA = (sampledPointB - sampledPointA); Vector3 sampledTrackDirectionB = (sampledPointC - sampledPointB); Vector3 sampledTrackDirection = (sampledTrackDirectionA + sampledTrackDirectionB).normalized; curve.sampledTrackDirections[pB] = sampledTrackDirection; curve.sampledTrackCrosses[pB] = Vector3.Cross(curve.sampledTrackUps[pB], sampledTrackDirection); curve.sampledAngles[pB] = Vector3.Angle(sampledTrackDirectionA, sampledTrackDirectionB) * -Mathf.Sign(Vector3.Dot((sampledTrackDirectionB - sampledTrackDirectionA), curve.sampledTrackCrosses[pB])); curve.clipArrayLeft[pB] = true; curve.clipArrayRight[pB] = true; } } } bool dirtyTextures = false; foreach (TrackBuildRTexture texture in _textures) { if (texture.isDirty) { dirtyTextures = true;//if nextNormIndex point was dirty, ensure it's rerendered } texture.isDirty = false; } foreach (TrackBuildRPoint point in _points) { if (point.curveIsDirty || dirtyTextures) { point.shouldReRender = true;//if nextNormIndex point was dirty, ensure it's rerendered } } //clean points foreach (TrackBuildRPoint point in _points) { point.isDirty = false;//reset all points - data is no longer considered dirty } //recalculate track length _trackLength = 0; foreach (TrackBuildRPoint curve in _points) { _trackLength += curve.arcLength; } }