1

I'm facing an issue regarding the tangent when moving along my cubic bezier curve within 3D space where (x,z) are used rather than (x,y). An image of the curve has been added.

The issue occurs when the entity hits the red line indicated on the image. The tangent begins progressively from 0.0 to 1.0 but then hits the red line and starts to progressively decrease from 1.0 to 0.0, causing the entity's direction to flip.

Currently this snipped is responsible for the tangent:

const Vector3 Class::getTangent(const float s) const
{
    const float t { 1 - s };

    return  (3 * t * t * (points.at(1) - points.at(0)) + 
             6 * s * t * (points.at(2) - points.at(1)) + 
             3 * s * s * (points.at(3) - points.at(2)));
}

I then use this function for:

Vector3 firstDerivative = curve.getTangent(segment);
firstDerivative.normalise();

Vector3 normal = firstDerivative.crossProduct(Vector3(0, 0, 1)); // Z = forward
normal.normalise();

Vector3 b = firstDerivative.crossProduct(normal);
b.normalise();

Then I simple use b to set the entities direction along the curve. I did create a simple/dirty fix by switching the sign of b to -b when the point which flips the entity is reached. Now despite this "fixing" the issue I believe if the curve changes the fix would cause problems later.

So if anyone can spot where I may have gone wrong or what I may need to do to fix this, any help would be great and appreciated. Please could you also provide examples of your solution with the given problem, either via pseudo or c++ code.

enter image description here

Community
  • 1
  • 1
SharkBytes
  • 211
  • 4
  • 18
  • 1
    Your question isn't quite clear; your tangent function is correct, and will yield vectors with length much larger than 0 or 1. It sounds like you're asking about the normals instead, in which case: why are you using the cross product? Vector cross products only have meaning in 3d; for 2d we can just take the tangent vector for point `t`, normalize it to unit length, and then rotate it 90 degrees. Done! If your normalised tangent vector is (vx,vy), the normal vector is simply (-vy,vx), see http://pomax.github.io/bezierinfo/#pointvectors – Mike 'Pomax' Kamermans Jan 22 '15 at 00:07
  • Ah, probably should have mentioned I'm working in 3d space. The y vector is up, x and z are used for positioning like x and y usually. I think I've understood what you've said and tried (with normalised tangent): `float fdx = firstDerivative.x * cos(90) - firstDerivative.z * sin(90); float fdz = firstDerivative.x * sin(90) + firstDerivative.z * cos(90); Vector3 newVec = Vector3(fdx, 0, fdz);` But doing so gave the same results with the entity flipping after reaching the red line again. _However I have not used the normal for this case..._ – SharkBytes Jan 22 '15 at 02:43

3 Answers3

2

This is actually quite a common problem, that I also faced while making a race track generator for a game demo (wipeout97 style) and later a colleague faced when making a road generator in a terrain tool for a well known 3D commercial content creation tool.

To fix this you need one of 2 possible things:

  • a hard vertical
  • inertia

The hard vertical isn't hard to make, you simply make your cross products with a fixed vector (for example (0,1,0)) however your curve can never GO at any point exactly in this direction otherwise your cross product becomes null one day and all hell break loose.

The inertia system goes by following the curve iteratively, with one reference hard vector used in the beginning, but just one time.
Then the curve is followed by delta increments and each time the new tangent and bitangents are discovered by progressive alteration of the previous segment's tangents and bitangents (TBN basis). You use the fact that small t (curve parameter) increment cannot lead to huge TBN alterations, and you can use one of the previous TBN vectors to make the cross product with your new tangent to find the new B and N. This way it is almost guaranteed that you will never have these kinds of gimbal lock effects.

Then various possible funny parameters are also possible like force a maximum rate of rotation for the TBN basis at each step, or the likes, in the case of track creation this gives a lot of artistic power.

v.oddou
  • 6,476
  • 3
  • 32
  • 63
  • I don't suppose you'd be able to give me an example of both systems using pseudo or c++ code, especially inertia system because I haven't heard nor can I find any information relating to it? – SharkBytes Jan 22 '15 at 09:41
  • 1
    note that for Bezier curves, the notion that a small `t` increment cannot lead to a huge TBN delta is false, due to the fact that Bezier curves can model cusped curved. Small `t` increments across the cusp will yield wildly different tangent and bitangent values on either side. – Mike 'Pomax' Kamermans Jan 22 '15 at 18:33
  • @Mike'Pomax'Kamermans waw I had never heard of the word "cusped" before. You had me google it. And now I see what you mean, you are absolutely right. I had assumed an exclusion of those cases by "artist care" :-) But you are totally right in absolute, I remember creating such cusps when manipulating vectorial arrows in Paint Shop Pro. – v.oddou Feb 06 '15 at 01:08
0

Ok, with some additional experimentation I've managed to get it working and I believe this is a decent fix. If anyone could explain to me whether this may or may not, I'd be grateful.

Anyway the package I'm coding in had two functions available which were setDirection (originally used) and lookAt (now used).

The code before:

entity->setDirection (firstDerivative, WorldSpace, secondDerivative);

After:

entity->lookAt (point - firstDerivative, WorldSpace);

Now as a result the secondDerivative is never used now which is completely fine but is this a more savored solution or not?

SharkBytes
  • 211
  • 4
  • 18
0

Since your Bezier curve is on the (x, z) plane, I don't quite understand why do you want to take a cross product between first derivative and the z axis (0, 0, 1). The 'normal' vector found in this way is bound to become zero when the first derivative goes in the (0, 0, 1) direction.

If you want to find the normal direction at any given point on the curve, you should evaluate the first derivative C'(t) and the 2nd derivative C"(t) first, then the normal vector vec(n) can be computed as

vec(n) = C' X C" / |C'||C"|

and your binormal vector vec(b) can be computed as vec(b) = vec(t) X vec(n) where vec(t) is the unit tangent vector vec(t)=C'(t)/|C'(t)|.

fang
  • 3,473
  • 1
  • 13
  • 19