From BlenderWiki

Jump to: navigation, search


Description of "Curve Normals":


Curve normals are a necessary component in all major 3D modeling or animation applications, which make certain operations--such as bevob's and path animation--possible. When placing an object--or object data, such as is the case with bevobs--it is necessary to have complete orientation data, of which a curve tangent alone does not constitute. It is the curve normal that dictates which direction is "up" at any point along a curve, and the combination of the curve normal and tangent vectors together give us a complete orientation.

How it Works Now

Curve normals in Blender always use the local positive Z direction as the up vector. At any point along the curve, the curve tangent and the positive Z vector are used to create the orientation of any object or set of points at that location on the curve.

My level of mathematical understanding only goes as high as trig and vector math, so I can give examples in this form, but not with matrices or quaternions.

Z-axis-and-curve.jpg Z-axis-and-normals.jpg

positive Z vector: A
curve tangent vector: B

curve normal vector = B x (A x B)

This creates a new vector which is planar with the up vector, but perpendicular to the curve tangent.

Bevob-twist.jpg Bevob-twist-n-normals.jpg

As a result, the normal changes direction abruptly whenever the curve tangent is close to--or exactly--parallel with the local Z axis.
This unwanted effect is the reason for this new feature proposal.

How The New Method Works

I propose that we make this new feature an option, so that users can use the behavior Blender has now, or this new method.

I call this new approach, "Incremental Curve Normals". I call it this because each normal is calculated from the normal before it. The first normal of the curve is calculated with the curve object matrix as its reference, but each normal after that is calculated in order, and uses the normal of the previous curve segment in the equation.

This method is one that I devised. I haven't seen this feature in any of the other 3D apps that I've used, and I haven't heard anyone else talk about a method like this. If it has been done before, I don't mean to take credit for it.

I have tested this new approach in a number of ways. The first was to design a rig which closely mimicked the mathematical solution that I propose on this page. I have uploaded a demonstration blend file here:


The second step was to write a program to test the math. I'm not skilled enough with C, or capable enough with Blender source, so I did this in Blender with a python script.


In this image, seg1, seg2, and seg3, are segments between points of our curve. From these points, we calculate seg1 through seg3--and so forth--so that each is a vector.

|V| = V normalized

At p2, the tangent is found by getting the vector that intersects the angle between seg1 and seg2. |seg1| + |seg2| = tan1. |tan1| is the normal which defines plane1.

From the end of the first curve normal, we project a ray--parallel to the first curve segment--and find the point where it intersects plane1, which we call i1.

ray1 = (p1 + n1) + |seg1| * m

n2 = i1 - p2 (we may then want to make n2 = |n2|, even though it may already be normal, neglecting to normalize here, may lead to increasingly larger normals as we approach the other end of the curve)

From this point on, it is a recursive process. n1 was used to find n2, now n2 can be used to find n3, and so on.

There may be other methods to create these same results, and maybe more efficient too. The quality of the method can be measured by one property: the method must produce a set of normals, such that, no two adjacent normals are non-planar.

Special Cases

There are certain situations that need to be checked for with each recursion:
1: segX = the last segment
2: segX = (0,0,0)
3: |segX| = -|segY| where segY is the following adjacent segment

Situation 1 occurs when we reach the end of the curve. 'Nough said =).

Situation 2 is where our segment has 0 length. This could happen if the user places two curve control points in the same location. In this situation, we skip segY and do the equation with segX and segZ. If segZ also has 0 length, we skip it as well, and keep skipping until we find the next segment that doesn't have 0 length.


Situation 3 happens if the user places about 4 points in the same location, or exactly along the same ray, so they form a perfectly sharp 360º turn.

For this I propose a solution, but this will need some good testing to be sure it actually works.

When a user does this, it results in overlapping curve segments that face opposite directions, thus, |segX| = -|segY|.


In this case, the normal between seg1 and seg2, n2, should equal |seg1|. The normal between seg2 and seg3 should equal -n1, however, additional math may be needed to ensure that it is truly perpendicular to the curve tangent.

If a user tries to do this with an animated curve, they could be setting themselves up for some unexpected results, but it shouldn't be any worse than the curve flipping issue we have currently.


In blender, curves have a twist value. In edit mode, selecting a point and pressing T will allow you to twist the curve. I haven't seen this in any other package, and I think it's great that we already have this in blender. I think a little more work needs to be done though, so it can be truly useful to animators.

With the new system I propose, the twist values will still exist, but they will be relative to the incremental curve normals.

I also propose that we add a new feature that allows users to specify an object for any curve point, which is used to calculate the curve twist value at that point.

More on this soon.

Wavez 13:10, 21 May 2007 (CEST)