I am having a issue calculating the normal of a spline to extrude a mesh from.
Check the Image : As you can see, the normals in a certain area apear flipped, and I have no idea why.
Relevant Code :
public Vector3 GetNormal(float t)
{
var tangent = GetTangent(t);
var point = GetPoint(t);
var accel = GetAcceleration(t);
// we need to lerp up base don the control points
int i;
if (t >= 1f)
{
t = 1f;
i = _points.Length - 4;
}
else
{
t = Mathf.Clamp01(t) * CurveCount;
i = (int)t;
t -= i;
i *= 3;
}
//var rotaSource = GetControlPointRotation(i);
//var rotaDest = GetControlPointRotation(i + 2);
//var lerp = Mathf.Lerp(rotaSource, rotaDest,t);
//var normalRotation = Quaternion.AngleAxis(lerp,tangent);
var binormal = Vector3.Cross(tangent, accel).normalized;
var normalOr = Vector3.Cross(tangent, binormal).normalized;
Debug.DrawLine(point, point + accel * 5, Color.blue);
Debug.DrawLine(point, point + binormal * 5,Color.black);
Debug.DrawLine(point, point + normalOr * 5, Color.yellow);
Debug.DrawLine(point, point + tangent * 5, Color.magenta);
if (Vector3.Dot(tangent, accel) > 0)
return Vector3.up;
return normalOr;
//return (normalRotation*up).normalized;
}
public Vector3 GetAcceleration(float t)
{
int i;
if (t >= 1f)
{
t = 1f;
i = _points.Length - 4;
}
else
{
t = Mathf.Clamp01(t) * CurveCount;
i = (int)t;
t -= i;
i *= 3;
}
return Bezier.GetSecondDerivative(_points[i], _points[i + 1], _points[i + 2],
_points[i + 3], t).normalized;
}
public Vector3 GetTangent(float t)
{
int i;
if (t >= 1f)
{
t = 1f;
i = _points.Length - 4;
}
else
{
t = Mathf.Clamp01(t) * CurveCount;
i = (int)t;
t -= i;
i *= 3;
}
return
Bezier.GetFirstDerivative(_points[i], _points[i + 1], _points[i + 2],
_points[i + 3], t).normalized;
}
The Bezier Curve methods here :
public static Vector3 GetPoint(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
t = Mathf.Clamp01(t);
float OneMinusT = 1f - t;
return OneMinusT*OneMinusT*OneMinusT*p0 + 3f*OneMinusT*OneMinusT*t*p1 + 3f*OneMinusT*t*t*p2 + t*t*t*p3;
}
public static Vector3 GetFirstDerivative(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
t = Mathf.Clamp01(t);
float oneMinusT = 1f - t;
float it = oneMinusT;
float it2 = oneMinusT*oneMinusT;
float t2 = t*t;
return (p0 * -it2 +
p1 * (t * (3 * t - 4) + 1) +
p2 * (-3 * t2 + t * 2) +
p3* t2).normalized;
return 3f*oneMinusT*oneMinusT*(p1 - p0) + 6f*oneMinusT*t*(p2 - p1) + 3f*t*t*(p3 - p2);
}
public static Vector3 GetSecondDerivative(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
return (6*t*(p1 + 3*(p2 - p3) - p0) + 6*(p0 - 2*p2 + p3));
}
I have no idea why, or how to fix it. I have tried using a reference vector like Up to no avail, I even tried to negate the normal on certain conditions (Dot prouct of accel and tangent < -1).
The binormal can flip depending on the curvature of the curve. We are calculating B = T X dT Consider an S-shaped curve in the plane. At the start of the curve these will in one orientation, giving an outward pointing bi-normal. At the end the they are in the opposite orientation giving inward pointing normal.
This fliped binormal will result in a flipped normal. A simple fix is just to use
normalOr = accel.normalise
A more advanced technique would be to impose continuity between successive vectors. So we calculate T1, N1, B1 at the first point, and T2, N1, B1. Now calculate T1 . T2, N1 . N2, B1 . B2. For continuous vector fields each of these dot products should be positive, if any are negative just flip the appropriate vector.
You always get problems around points with zero curvature and torsion.