Could not work out or find any solution for this.
I have a spline which consists of bezier curves (in 3d space) — in fact, any straight line will also have such problem.
I take a point on the curve and need to get a normal to this point at any given angle 0 - 360 (say, to build a mesh around it or to calculate an offset for the movement).
I understand that in 3d space we have to define a plane to build normal, normally — by using Vector3.Cross(A, B); In general, one uses curve's tangent as A and, e.x., Vector3.up or right as B.
But, if curve becomes vertical (it can go in any direction, imagine any track that is ground-independent, like in space), it gets aligned with Vector3.up, and the direction of normals goes crazy, swaps direction and so on, which is quite logical. More, it does so not only in straight vertical position, but start to change gradually around it.
So, the question is: how can I get consistent (all in equally correct direction) normals, using any given curve point position and angle for normal rotation?
Update:
The initial approach looked like this:
public Vector3 GetTangent(...) {
Vector3 q2 = CalculateTangent (...);
Quaternion lookAt = Quaternion.LookRotation (q2);
Vector3 normal = lookAt * Vector3.up;
normal = Quaternion.AngleAxis (angle, q2) * normal;
return point + normal * size;
}
Which generated the following normals (blue is up):
Now, if I try to use the previous normal for the direction:
Quaternion lookAt = Quaternion.LookRotation (q2, previousNormal);
first of all, I don't understand what to place here:
Vector3 normal = lookAt * ?
And any combination of tangent, normal and rotation gives me something like this:
Where it can be clearly seen that normals on the vertical are improved (though not correct), but on the horizontal all went wrong, as if I just changed Vector.up to Vector3.right.
In addition, the code for generating the tube in its main part looks like this:
float vStep = (angleTo - angleFrom) / pipeSegmentCount;
if (b == 0 && _tP == 0f) _tP = 0.01f;
if (b == 0 && _t == 0f) _t = 0.01f;
if (b == spline.Nodes.Count - 2 && _tP == 1f) _tP = 0.9f;
if (b == spline.Nodes.Count - 2 && _t == 1f) _t = 0.9f;
Vector3 vertexA = spline.GetTangent (_pP, _tP, _bP, Vector3.up, pipeAngle, pipeRadius);
Vector3 vertexB = spline.GetTangent (_pointsTotal[b] [s], _t, b, Vector3.up, pipeAngle, pipeRadius);
for (int v = 1; v <= pipeSegmentCount; v++, i += 4) {
vertices [i] = vertexA;
vertices [i + 1] = vertexA = spline.GetTangent (_pP, _tP, _bP, Vector3.up, v * vStep + pipeAngle, pipeRadius);
vertices [i + 2] = vertexB;
vertices [i + 3] = vertexB = spline.GetTangent (_pointsTotal[b] [s], _t, b, Vector3.up, v * vStep + pipeAngle, pipeRadius);
}
Where 0.01f and 0.99f are because ends of spline do not have control points and LookRotation produces an error about zero rotation. b and _bP are node indexes, and _P suffix is for "previous".
This particular code is used to generate a round rail and move an object beneath it. Thus, it is important that the bottom normal is consistent and always at the bottom (when it is vertical, this bottom is not the world bottom of course, but it protrudes at the side of the tube, object will turn itself accordingly, looking forward the curve).