[Closed] Mimicing TCB controllers
Has anyone had any joy doing this ? I’ve coded the the typical hermite curve and the Kochanek-Bartels in out vectors in mxs but cannot reproduce max’s interpolation.
got there in the end
though I still have a few issues with how max converts the tcb 0-50 range to a -1 to 1 range. I get perfect results at 0, 25, 50 but errors inbetween, perhaps it’s some kind of log scale :banghead:
i’ve only got a bit of testing code, you need to create a node with a tcb position controller with at least 4 key frames pass the time for key 2 and 3 as the start and end time and the node to the makespline function.
fn cp p = (p - 25.0) * 0.04;
fn hermite t &h1 &h2 &h3 &h4 =
(
local t2, t3;
t2 = t * t;
t3 = t * t2;
h2 = 3.0 * t2 - t3 - t3;
h1 = 1.0 - h2;
h4 = t3 - t2;
h3 = h4 - t2 + t;
)
fn outgoing prev key0 key1 =
(
a = (1 - (cp key0.tension)) * ( 1 + (cp key0.continuity)) * ( 1 + (cp key0.bias));
b = (1 - (cp key0.tension)) * ( 1 - (cp key0.continuity)) * ( 1 - (cp key0.bias)) ;
d = key1.value - key0.value;
if prev == undefined then
out = b * d;
else
(
t = ( key1.time - key0.time ) as float / ( key1.time - prev.time) as float;
out = t * ( a * (key0.value - prev.value ) + b * d );
)
out;
)
fn incoming key0 key1 next =
(
a = (1 - (cp key1.tension)) * (1 - (cp key1.continuity)) * ( 1 + (cp key1.bias));
b = (1 - (cp key1.tension)) * (1 + (cp key1.continuity)) * ( 1 - (cp key1.bias));
d = key1.value - key0.value;
if next == undefined then
inv = a * d;
else
(
t = ( key1.time - key0.time ) as float / ( next.time - key0.time ) as float;
inv = t * (b * (next.value - key1.value) + a * d);
)
inv;
)
fn tcb timev k1 k2 k3 k4 =
(
local h1, h2, h3, h4
t = ( timev - k2.time ) as float / ( k3.time - k2.time ) as float;
out = outgoing k1 k2 k3
inv = incoming k2 k3 k4;
hermite t &h1 &h2 &h3 &h4;
h1 * k2.value + h2 * k3.value + h3 * out + h4 * inv ;
)
fn MakeSpline tnode start_frame end_frame =
(
keys = tnode.position.controller.keys;
sp = splineshape();
addNewSpline sp;
for f = start_frame to end_frame do
(
addKnot sp 1 #corner #line (tcb f keys[1] keys[2] keys[3] keys[4] );
)
updateShape sp;
)
sorry edited because of a missing closing bracket and the in & out vector should be calculated only once for every segment and not for for every sample !
Klunk,
you probably have seen this link http://collada.org/public_forum/showthread.php/916-Animation-TCB-Spline-Interpolation-in-COLLADA
everything looks right for me… there might be some mistakes in the realization. but the logic and math is right. what is your real goal? do you want to convert TCB interpolation to the Bezier?
narrowed it down to the continuity but without seeing the source code i fear it’s impossible to reproduce max tcb interpolation. more precisely it’s the continuity when the key frame time deltas are not equal.
finally found the solution ! so for anyone else here it is turned out to be a linear interpolation between the keyframe time ratios tending towards 1 as the continuity increases/decreases
fn cp p = (p * 0.04 - 1.0)
fn lerp x y s = (x + s * (y - x))
--****************************************************************************************************
fn GetInTangent k keyframes =
(
key = keyframes[k];
if k == 1 then
prev = keyframes[k]
else
prev = keyframes[k - 1]
if k == keyframes.count then
next = keyframes[k];
else
next = keyframes[k+1];
a = (1 - (cp key.tension)) * (1 - (cp key.continuity)) * (1 + (cp key.bias)) * 0.5;
b = (1 - (cp key.tension)) * (1 + (cp key.continuity)) * (1 - (cp key.bias)) * 0.5;
ad = (key.value - prev.value)
bd = (next.value - key.value)
t = 2.0 * (key.time - prev.time) as float / (next.time - prev.time) as float;
(b * bd + a * ad ) * (lerp t 1.0 (abs (cp key.continuity)));
)
--****************************************************************************************************
fn GetOutTangent k keyframes =
(
key = keyframes[k];
if k == 1 then
prev = keyframes[k]
else
prev = keyframes[k - 1]
if k == keyframes.count then
next = keyframes[k];
else
next = keyframes[k+1];
a = (1 - (cp key.tension)) * (1 + (cp key.continuity)) * (1 + (cp key.bias)) * 0.5;
b = (1 - (cp key.tension)) * (1 - (cp key.continuity)) * (1 - (cp key.bias)) * 0.5;
ad = (key.value - prev.value);
bd = (next.value - key.value);
t = 2.0 * (next.time - key.time) as float / (next.time - prev.time) as float;
(b * bd + a * ad ) * (lerp t 1.0 (abs (cp key.continuity)));
)
--****************************************************************************************************
fn hermite t &h1 &h2 &h3 &h4 =
(
local t2, t3;
t2 = t * t;
t3 = t * t2;
h2 = 3.0 * t2 - t3 - t3;
h1 = 1.0 - h2;
h4 = t3 - t2;
h3 = h4 - t2 + t;
)
--****************************************************************************************************
fn tcb timev key0 key1 m0 m1 =
(
local h1, h2, h3, h4;
local t = ( timev - key0.time ) as float / ( key1.time - key0.time ) as float;
hermite t &h1 &h2 &h3 &h4;
h1 * key0.value + h2 * key1.value + h3 * m0 + h4 * m1;
)
--*************************************************************************************************
fn MakeSpline tnode start_frame end_frame =
(
keys = tnode.position.controller.keys;
sp = splineshape();
addNewSpline sp;
out = GetOutTangent 2 keys
inv = GetInTangent 3 keys
for f = start_frame to end_frame do
(
addKnot sp 1 #corner #line (tcb f keys[2] keys[3] out inv );
)
updateShape sp;
sp.vertexTicks = on
)
--**********************************************************************************************
(
clearlistener()
MakeSpline $point01 60f 90f
)
--*************************************************************************************************
make a scene with a point helper and 4 tcb keyframes (2 of the keys at 60 & 90) with trajectory on.
does anyone know the function for this ease in curve
0 0
0.1 0.02632
0.2 0.05556
0.3 0.08824
0.4 0.125
0.5 0.16666
0.6 0.20238
0.7 0.22527
0.8 0.23958
0.9 0.24747
1 0.25
tried cubic, quad, sin, bezier, catmull rom, hermite so far to no avail
still no easeTo & easefrom handling but computes the whole trajectory and handles start and end key frames correctly
fn lerp x y s = (x + s * (y - x))
fn cp p = (p * 0.04 - 1.0)
--****************************************************************************************************
-- compute the in tangent
fn GetInTangent k keyframes =
(
key = keyframes[k];
if k == 1 then
prev = keyframes[k]
else
prev = keyframes[k - 1]
if k == keyframes.count then
next = keyframes[k];
else
next = keyframes[k+1];
a = (key.value - prev.value) * (1 - (cp key.tension)) * (1 - (cp key.continuity)) * (1 + (cp key.bias));
b = (next.value - key.value) * (1 - (cp key.tension)) * (1 + (cp key.continuity)) * (1 - (cp key.bias));
t = (key.time - prev.time) as float/(next.time - prev.time) as float;
(b + a) * (lerp t 0.5 (abs (cp key.continuity)));
)
--****************************************************************************************************
-- compute the out tangent
fn GetOutTangent k keyframes =
(
key = keyframes[k];
if k == 1 then
prev = keyframes[k]
else
prev = keyframes[k - 1]
if k == keyframes.count then
next = keyframes[k];
else
next = keyframes[k+1];
a = (key.value - prev.value) * (1 - (cp key.tension)) * (1 + (cp key.continuity)) * (1 + (cp key.bias));
b = (next.value - key.value) * (1 - (cp key.tension)) * (1 - (cp key.continuity)) * (1 - (cp key.bias));
t = (next.time - key.time) as float/(next.time - prev.time) as float;
(b + a) * (lerp t 0.5 (abs (cp key.continuity)));
)
fn hermite t &h1 &h2 &h3 &h4 =
(
local t2, t3;
t2 = t * t;
t3 = t * t2;
h2 = 3.0 * t2 - t3 - t3;
h1 = 1.0 - h2;
h4 = t3 - t2;
h3 = h4 - t2 + t;
)
fn tcb timev key0 key1 m0 m1 =
(
local h1, h2, h3, h4;
local t = (timev - key0.time) as float / (key1.time - key0.time) as float;
hermite t &h1 &h2 &h3 &h4;
h1 * key0.value + h2 * key1.value + h3 * m0 + h4 * m1;
)
fn MakeTCBSpline tnode = if tnode != undefined and classof tnode.position.controller == tcb_position do
(
keys = tnode.position.controller.keys;
if keys.count >= 2 then
(
sp = splineshape();
addNewSpline sp;
-- create the first knot
addKnot sp 1 #corner #line keys[1].value
-- loop over the keys
for k = 1 to keys.count - 1 do
(
start_key = k;
end_key = k + 1;
-- create the segment in and out tangents
out = GetOutTangent start_key keys
inv = GetInTangent end_key keys
-- handle the end conditions
if start_key == 1 then out = out * 1.5 - inv * 0.5;
if end_key == keys.count then inv = inv * 1.5 - out * 0.5;
-- create the knots
for f = keys[start_key].time to keys[end_key].time do
(
if f != keys[start_key].time then
addKnot sp 1 #corner #line (tcb f keys[start_key] keys[end_key] out inv );
)
)
updateShape sp;
sp.vertexTicks = on
)
)
(
clearlistener()
MakeTCBSpline $
)