Notifications
Clear all

[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.

18 Replies

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:

 PEN

Can you share what you are doing so we might help?

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.

btw does anyone know max’s prefered ease in & ease out time function ?

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 $
)	

Page 1 / 2