# Help with ColladaMaya bezier animation

I am having a tough time trying to get animations exported from Maya to playback the same on my custom viewer.

It seems I am not handling the bezier math correctly.
Here is the math I am using, which is from the Collada book( Arnaud, Barnes 2006).

s = (key-k1)/(k2-k1)
S= (s^3, s^2, s, 1 )
M = ( -1 3 -3 1, 3 -6 3 0, -3 0 3 0, 1 4 1 0)
C = ( V1, V2, T1, T2 )

Interpolated Value = S x M x C

My interpretation(very unsure) of the parameters are :
key - time used to obtain interpolated value (k1 <= key <= k2)
k1 - first key’s start time
k2 - second key’s start time
V1 - value of first key
V2 - value of second key
T1 - V1 + inTangent[V1]
T2 - V2 - outTangent[V2]

The matrix used for M appears to be for bsplines? Confused…

The Feeling Viewer also does not produce identical playback when playing back ColladaMaya files.

Can someone point me in the right direction?

Thank you!
nazure

Unfortunately I haven’t yet added support for Collada animations to my tools, but no one else has replied so maybe I can help. The discussion of the animation curves in the book confuses me. It seems fishy that the basis matrix given for Bezier curves is identital to the basis matrix used for B-Splines, just as you pointed out. Also I’m not sure why the spec defines cubic Bezier curves in terms of two control points and two tangents rather than four control points, which is how it’s usually defined.

Assuming there might be something wrong with the Bezier basis matrix given in the book (which I’m not sure about, but it’s worth a shot), let’s reformulate the curve segment as four control points and use the Bezier basis matrix given in the Foley/van Dam book.

M = (-1 3 -3 1, 3 -6 3 0, -3 3 0 0, 1 0 0 0) // from Foley, van Dam, Feiner, and Hughes
C = (p1, p2, p3, p4)
p1 = V1
p2 = p1 + 1/3 T1
p3 = p4 - 1/3 T2
p4 = V4

Everything else is the same as you have it above.

By the way, another possibility is that the animation data is being exported from Maya incorrectly. You could manually construct some simple Bezier curves to make sure the data being input to your program is correct.

Yes, you are correct, there is a typo in the book.
The matrix for 4 control points, which can be computed from the two points and the two tangents is the one you gave:
M = (-1 3 -3 1, 3 -6 3 0, -3 3 0 0, 1 0 0 0)

According to my math, if you substitute with:
T1 = 3 (P2 - P1)
T2 = 3 (P4 - P3)

to get back to the form in the book, you have:

M = (2 -3 0 1, -2 3 0 0, 1 -2 1 0, 1 -1 0 0)
C = (P1, P4, T1, T2)

Why is Collada using tangents even though four control points can express more curves? Isn’t this causing loss of information?

I am looking at ‘seymourplane.dae’ from the sample_data pack: (only relevant information)
<float_array id="…-output">0 360 </float_array>
<float_array id="…-intangents">0 6875.42 </float_array>
<float_array id="…-outtangents">6875.42 0 </float_array>
<Name_array id="…-interpolations">BEZIER BEZIER </Name_array>

The values 6875.42 are confusing me. Should they both be 360 to produce linear interpolation?

Actually they’re equivalent in what they’re capable of expressing. If you have two points and two tangents you can easily convert to four control points, and vice versa. The formulas were given above.

Having said that I’ve always seen Bezier interpolation expressed in terms of four control points, and Hermite as two points and two tangents. I think that’s pretty conventional and I’m not sure why they decided to do it differently with Collada.

Ok, I confess that I have seen Bezier equations for the first time today, but I think I am a reasonable mathematician so…

I see I can covert tangents to four control points, but I do not see how to do that the other way round without loss of information. For example, consider two ‘four control points’ curves:
Curve1: P1=[0,0] P2=[1,1] P3=[9,1] P4=[10,0]
Curve2: P1=[0,0] P2=[10,10] P3=[0,10] P4=[10,0]

I think that these curves are different. However, they both have same tangent form:
Tangent: P1=[0,0] T1=10 T2=-10 P4=[10,0]
(where I am taking the tangent value as (dy/dx)*(time interval) )

Other idea: Can these curves be expressed in tangent form at all?
Curve3: P1=[0,0] P2=[0,1] P3=[10,1] P4=[10,0] (Seems valid in Blender)
Curve4: P1=[0,0] P2=[-5,1] P3=[15,1] P4=[10,0] (Probably invalid in DCCs anyway)

EDIT: I see one reason to use tangents. If you space the points P1, P2, P3 and P4 equally in the x direction (eg 0, 1/3, 2/3, 1) then the interpolated time is linearly dependent on the parameter ‘s’ and vice versa. Therefore is it easy to get ‘s’ for a given time. If ‘four control points’ are used, I think that you need to solve cubic equation to get ‘s’ for a given time.
Anyway, this simplification for developers does not justify the possible loss of information due to the need to convert to the tangent form.

The key thing is that the curve is not parameterized as y = f(x). Many perfectly valid curves can’t be represented in that way, as you’ve discovered. One example is a line between (0,0) and (0,1).

Instead we re-parameterize x and y with respect to an independent variable t. These are called parametric curves and are far more flexible than curves of the form y = f(x). For example a line between any two points p0 and p1 can be represented as p(t) = p0 + t(p1-p0), where t is on the interval [0, 1]. Notice this form handles a line between (0,0) and (0,1) with no problems. In graphics we almost always uses parametric curves and surfaces.

In terms of derivatives, we compute dp/dt as [dx/dt, dy/dt], i.e. you compute the derivative of each component with respect to t independently. This yields the tangent vector.

This is an excellent point.
‘Four point Bezier’ curve is parametric curve and may not necessarily be expressed as y = f(x)
‘Tangent’ curve, however, is a polynomial (cubic) and therefore can be expressed as y = f(x)

Therefore there exist Bezier curves which can not be expressed in the Collada’s tangent form.

EDIT: In other words,
‘Four control point’ can express curves:
x(t) = at^3 + bt^2 + ct + d
y(t) = e
t^3 + ft^2 + gt + h
However, ‘tangent’ curve can express only curves:
x(t) = at
y(t) = e
t^3 + ft^2 + gt + h
(or also y(x) = ax^3 + bx^2 + c*x + d)
Right?

‘Tangent’ curve, however, is a polynomial (cubic) and therefore can be expressed as y = f(x)
I’m not sure about your reasoning there. Both the point-tangent and the 4-point forms are cubic polynomial curves (in terms of t), however this doesn’t mean that they can be expressed as y = f(x).

The Hermite curve is a good example of a curve that’s usually expressed in point-tangent form. In general, it can’t be expressed as y = f(x). For an example, consider the line we had from (0,0) to (0,1). As we discussed that can’t be expressed as y = f(x). But the Hermite curve with p1 = (0,0), t1 = (0,1), p2 = (0,1), t2 = (0,1) is one way of describing that line.

One thing to keep in mind is that the point-tangent form is also a parametric curve. The tangents being discussed are parametric tangent vectors (dx/dt, dy/dt), not a dy/dx tangent.

There was no reasoning But there was a warning that I like maths

Let P0 be 2-dimensional point [P0.x; P0.y]

Consider Bezier curve with control points P0, P1, P2 and P3:
x(t) = P0.x*(1-t)^3 + 3P1.xt(1-t)^2 + 3P2.xt^2(1-t) + P3.xt^3
y(t) = P0.y
(1-t)^3 + 3P1.yt(1-t)^2 + 3P2.yt^2(1-t) + P3.y*t^3
where 0 <= t <= 1

Add the constraint imposed by the tangential definition: the points P0, P1, P2 and P3 are equally spaced in x direction:
P0.x = a P1.x = a + b P2.x = a + 2b P3.x = a + 3b

Put into equation for x(t):
x(t) = a*(1-t)^3 + 3*(a + b)t(1-t)^2 + 3(a + 2b)t^2(1-t) + (a + 3b)t^3
= a { (1-t)^3 + 3
t(1-t)^2 + 3
t^2(1-t) + t^3 } + b { 3t(1-t)^2 + 6t^2(1-t) + 3t^3 }
= a { ((1-t)+t)^3 } + b { 3t (1 - 2t + t^2) + 6(t^2 - t^3) + 3t^3 }
= a { 1^3 } + b { 3t - 6t^2 + 3t^3 + 6t^2 - 6t^3 + 3
t^3 }
= a + b { 3t }
= 3tb + a
Therefore,
x = 3t
b + a
x - a = 3tb
t = (x - a)/(3b)
Now we can substitute ‘t’ into y(t) = P0.y
(1-t)^3 + 3P1.yt(1-t)^2 + 3P2.yt^2(1-t) + P3.y*t^3
That would be a lot of algebra, but since t is linearly dependent on x will can see that y will be cubic in terms of x.
Hence the tangential form can only express curves in form y = f(x).

Add the constraint imposed by the tangential definition: the points P0, P1, P2 and P3 are equally spaced in x direction:
Where does this constraint come from?

EDIT: Yep, this would work.

However, from the description it seems to me that the tangents are in scalar form dy/dx - more specificaly scalar (dy/dx)*(time interval)
This information is not sufficiant to produce all posible control points

There is only one value stored per tangent - I assume it defines the y coordinate of the control point. The x coordinate is not stored so I assume it is implicitly defined as one thrid of the time interval between keys. I got this from this post: https://collada.org/public_forum/viewtopic.php?t=277

I am using following equation to get the control points:

CP(0) = (input[N], output[N])
CP(1) = ((input[N] * 2 + input[N+1]) / 3, output[N] + out_tangent[N] / 3)
CP(2) = ((input[N] + input[N+1] * 2) / 3, output[N+1] - in_tangent[N+1] / 3)
CP(3) = (input[N+1], output[N+1])

PS: Going to bed

There is only one value stored per tangent
That’s not right. If your control points are 1-dimensional then sure, a 1-dimensional tangent is fine. But for 3d control points the tangents should be 3d also. Otherwise the tangents don’t give you enough information. So if you thought that each tangent is just a single scalar value, then yes I can see why the point-tangent form doesn’t seem to work out, but I’m sure you’re mistaken about the tangents.

I am using following equation to get the control points:

CP(0) = (input[N], output[N])
CP(1) = ((input[N] * 2 + input[N+1]) / 3, output[N] + out_tangent[N] / 3)
CP(2) = ((input[N] + input[N+1] * 2) / 3, output[N+1] - in_tangent[N+1] / 3)
CP(3) = (input[N+1], output[N+1])
The confusing thing about these equations is that it makes it look like the t-values (described with the “input” array) are a component of the resulting control point. They’re not, and I think that’s what was throwing you off. A better way of writing the above equations would be

CP0 = output[N]
CP1 = output[N] + out_tangent[N] / 3
CP2 = output[N+1] - in_tangent[N+1] / 3
CP3 = output[N+1]

The t-values / inputs are used for interpolating between these control points, but they’re not part of the control points. If you look at these equations you’ll see they look remarkably similar to what I originally posted in the thread:

They mean the same thing.

I am using following equation to get the control points:

CP(0) = (input[N], output[N])
CP(1) = ((input[N] * 2 + input[N+1]) / 3, output[N] + out_tangent[N] / 3)
CP(2) = ((input[N] + input[N+1] * 2) / 3, output[N+1] - in_tangent[N+1] / 3)
CP(3) = (input[N+1], output[N+1])
By the way I see that these equations also appear in the book (page 125), except that they’re missing the /3 on the tangents. This is basically a description of how to convert the control point-tangent form of the Bezier curve to the 4-control point form, but it’s not obvious that that’s what it is in the book. This description should probably either be removed or rewritten to be more clear, and the /3 math error fixed of course.

Yes, you are right, the problem is that I think that each tangent is single scalar value.

First, the topic of this post is “Help with ColladaMaya bezier animation” so we are talking about the <animation> element. Right?

Second, animations in Blender are done using four control points which are moved on a plane. This implies that the control points or tangents for animations need to be two dimensional. Right?

Third, here is a snippet from Seymour.dae from Sample Data from Collada DOM:
<animation id=“l_hip_rotateY”>
<float_array id="…-input">0 0.416667 0.833333 </float_array>
<float_array id="…-output">0 -5.89913 0 </float_array>
<float_array id="…-intangents">-5.89912 0 2.45797 </float_array>
<float_array id="…-outtangents">-2.45797 0 5.89912 </float_array>
<Name_array id="…-interpolations">BEZIER BEZIER BEZIER </Name_array>
</animation>
It seems to me that there is only one tangent stored per keyframe. How do I get four two dimensional control points from these data?

Second, animations in Blender are done using four control points which are moved on a plane. This implies that the control points or tangents for animations need to be two dimensional. Right?
When I say that the curve is n-dimensional, I’m not counting the independent variable t as one of the dimensions. I’m guessing what Blender does is provide independent graphs of x vs. t, y vs. t, and z vs. t. t is the independent variable and x, y, and z can all be expressed as functions of t (with all the limitations that come with the term “function”). The resulting curve is p(t) = [x(t), y(t), z(t)].

If all you’re animating is x, then a single scalar value is all you need to provide the tangent, since x = f(t) is a scalar function that you can compute the derivative of to get a scalar back. Note that just because x is graphed against t does not mean that you can describe any planar curve with x = f(t).

If instead your curve is [x(t), y(t)], then the tangent vector would be [x’(t), y’(t)]… it’s also a 2d vector.

It seems to me that there is only one tangent stored per keyframe. How do I get four two dimensional control points from these data?
The given control points are 1-dimensional (they’re angles), so you want four 1d control points, not four 2d control points. The keyframes form two of the control points, and the tangents can be used to compute two additional control points for Bezier interpolation using the formulas I gave above.

Ok I think I’m going to give up at this point. For more info behind the math you can google “vector functions”. Good luck.

Sorry, I hate when I do not understand something… it is my nature

Maybe an example will do. Here are four curves created in Blender. (Hence valid curves by definition)

Could you please tell me the values of the four scalar tangents? (Since the curves are different, I assume the tangents will be different) In other words, how do I save these curves in Collada?

Please do not give up. At least answer the last post - all I want is four numbers, nothing else.

Those look like generic 2d Bezier curves, e.g. x on the horizontal axis, y on the vertical axis, both parameterized with respect to an independent variable t. The tangents will be 2d and can be computed as 3(p2-p1) and 3(p4-p3), as was discussed before, where p1, p2, p3, and p4 are the middle control points.

If that’s meant to be a graph of x vs. t, then it’s wrong to allow the tangents to be moved in 2-dimensions. To see why consider what would happen if you pulled p2 to the left of p1, which is conceptually perfectly reasonable, even if Blender’s interface prevents you from doing it. If you did that then one t value would have multiple x values, so x could no longer be expressed as a function of t, which means that t would no longer be an independent variable and you’d need to reparameterize.