Preslerp for DS2

there's a new and better version called mcjSlerp

and can be found here

https://sites.google.com/site/mcasualsdazscripts/mcjslerp-for-ds-1-2-3-4

Obsolete

Obsolete

Obsolete

Obsolete

Obsolete

Preslerp : Using Quaternions and Slerp() to fix camera animations

-----

the camera below had its movement "recorded"

with the the following keys on its rotation

( 0, 80, 0 )

( 0, 100, 0 )

( 0, 120, 0 )

this key was automatically changed to ( -180, 60, -180 ) by DazStudio

rendering the animation shows that it introduced a wobble movement

after processing by preslerp.ds we get camera movements that match what was happening at record time

copy and save the script below or download it here

example 2

the movement of the flying car comes from a capture using an early version of my "FlyCam" script ( see in the script repository )

although the flips look fun, they did not really occur in uhhhh real life, they are the result of the way rotation angles are processed in daz studio

in the "after" photo you can see that preslerp.ds corrected this

before

after

here's the source code

// DAZ Studio version 2.3 filetype DAZ Script

//=====================================================

// preslerp3.ds

//=====================================================

// this script is meant to be used by people who understand

// the explanation below and figure out how to use it

// ( basically you select a camera that has a recorded animation )

// set the playrange to the range you want fixed and launch the script )

//

//=====================================================

// lets say you press DS's Timeline Play button and start moving a camera

// you are in fact recording the camera's movements

//

// we suppose there were no keys on this camera when recording started

// so there were no keys ahead of us at any moment during the recording

//

// the current values of XRotate YRotate and ZRotate during the recording

// were at the value we had left them upon the most recent keying

//

//example

// frame 80 : XRotate YRotate and ZRotate keyed with values ( 1, 1, 1 )

// frame 100: only YRotate is keyed with value 2,

// so the current angles are ( 1, 2, 1 )

// frame 120, XRotate YRotate and ZRotate keyed with values ( 3, 3, 3 )

//

// we stop recording

// now if we request to be shown the value of rotations at frame 100

// we get ( 2, 2, 2 )

// but we know that the real position at recording time was ( 1, 2, 1 )

//

// reason: XRot and ZRot were not keyed at frame 100,

// so we are shown interpolated values based on the values

// at frames 80 and 120

// FixPlayrange() corrects this

// it goes through all the keys in the playrange

// and generates keys with correct values

// in the example above,

// a key for XRotate is created at frame 100 with the value "left"

// by the XRotate key at frame 80

// a key for ZRotate is created at frame 100 with the value "left"

// by the ZRotate key at frame 80

// the keys at frame 100 are now ( 1, 2, 1 ) which correstponds to

// what was experienced during recording

// now we have keys we can build upon

// if you set your camera rotations to ( 0°, 100°, 0° )

// and rotate your mouse wheel to move the camera forward

// you'll notice that the rotations have been changed by Daz Studio

// to ( -180°, 80°, -180° )

// the camera is still pointed exactly like before, we just moved forth

// so those two sets of angles are equivalent

//

// but if you recorded/keyed ( 0°, 100°, 0° ) at frame 0

// and ( -180°, 80°, -180° ) at frame 30

// the animation doesnt show a camera moving forth while

// remaining pointed straight ahead

// it shows a camera doing a sort of quarter-spin while moving

// forth, in other situations you may

// even find your camera doing back flips

//

// Daz studio tends to force angles to remain within

// the range -180° to 180° for X and Z and -90° to 90° for Y

// ( probably due to the maths of quaternions-to-euler conversions )

//

// quaternions are a way to store and manipulate 3D space (XYZ) angles

// in fact theyy can store and manipulate 4D space angles, no kidding

// and Slerp() is a way to transition between two orientations

// expressed as quaternions

// the Quaternions/Slerp method is immune to the

// "spin and back flips" effect

//

// another method called squat() does the same thing but

// produces smoother results (splined interpolation)

// for now i'll implement Slerp and see if that's satisfactory

//

// SlerpPlayrange() goes through all the keys in the playrange

// and creates new keys for all the other frames in the playrange

// using Slerp()

//

// important: SlerpPlayrange() processes only between the first

// and last keys of the playrange, so normally you would set the

//========================================

FixPlayrange();

SlerpPlayrange();

App.flushLogBuffer() ;

//=====================================================

//

//=====================================================

function FixPlayrange()

{

numSelectedNodes = Scene.getNumSelectedNodes();

if( numSelectedNodes != 1 )

{

errmsg = "one and only one node must be selected \n";

errmsg += "but " + numSelectedNodes + " are presently selected";

errmsg += "no processing occured";

return;

}

var node = Scene.getSelectedNodeList()[0];

var xprop = node.findProperty( "XRotate" );

var yprop = node.findProperty( "YRotate" );

var zprop = node.findProperty( "ZRotate" );

if( (!xprop) || (!yprop) || (!zprop) )

{

errmsg = "Unable to find the required properties";

errmsg += "namely XRotate, YRotate, ZRotate";

errmsg += "no processing occured";

return;

}

var playrange = Scene.getPlayRange();

var timestep = Scene.getTimeStep();

var Xndx = new Number;

var Yndx = new Number;

var Zndx = new Number;

var Xiskey;

var Yiskey;

var Ziskey;

var t = playrange.start;

var prevx = xprop.getValue( t );

var prevy = yprop.getValue( t );

var prevz = zprop.getValue( t );

for( var t = playrange.start; t <= playrange.end; t += timestep )

{

Xiskey = xprop.isKey( t, Xndx );

Yiskey = yprop.isKey( t, Yndx );

Ziskey = zprop.isKey( t, Zndx );

if( Xiskey || Yiskey || Ziskey )

{

if( !Xiskey )

{

xprop.setValue( t, prevx );

}

else

{

prevx = xprop.getValue( t );

}

if( !Yiskey )

{

yprop.setValue( t, prevy );

}

else

{

prevy = yprop.getValue( t );

}

if( !Ziskey )

{

zprop.setValue( t, prevz );

}

else

{

prevz = zprop.getValue( t );

}

}

}

}

//=====================================================

//

//=====================================================

function getIndexForTime( t, prop )

{

var numKeys = prop.getNumKeys();

for( var i = 0; i < numKeys; i++ )

{

if( prop.getKeyTime( i ) == t )

{

return( i );

}

}

return( 0 );

}

//=====================================================

//

//=====================================================

function SlerpPlayrange()

{

var playrange = Scene.getPlayRange();

var timestep = Scene.getTimeStep();

var node = Scene.getSelectedNodeList()[0];

var xprop = node.findProperty( "XRotate" );

var index = new Number;

var isInbetween = false;

var prevQuat;

var nextQuat;

var prevT;

var nextT;

var q = new DzQuat();

for( var t = playrange.start; t <= playrange.end; t += timestep )

{

if( xprop.isKey( t, index ) )

{

//since isKey() doesnt seem to be returning a valid index

index = getIndexForTime( t, xprop );

isInbetween = true;

prevQuat = node.getWSRot( t );

prevT = t;

var numXkeys = xprop.getNumKeys();

if( ( index + 1 ) >= numXkeys )

{

break;

}

nextT = xprop.getKeyTime( index + 1 );

nextQuat = node.getWSRot( nextT );

}

else

{

if( isInbetween )

{

var f = ( t - prevT ) / ( nextT - prevT );

q = slerp( f, prevQuat, nextQuat );

node.setWSRot( t, q );

}

}

}

}

//=====================================================

//

//=====================================================

function dotproduct( u, v )

{

return( u.w*v.w + u.x*v.x + u.y*v.y + u.z*v.z )

}

function deg( rad )

{

return rad * 180 / Math.PI;

}

//=====================================================

// sin (1-t)A sin tA

// Slerp(q1,q2;t) = q1 ---------- + q2 ------, cos A = q1.q2

// sin A sin A

//=====================================================

function slerp( t, u, v )

{

var dp = dotproduct( u, v );

//this is the one change from version 2

//avoid 360° spins

if( dp < 0 )

{

u.w = -u.w

u.x = -u.x

u.y = -u.y

u.z = -u.z

dp = -dp ;

}

// make dp save for use in acos()

if( dp >= 1 ) dp = 0.999999;

var A = Math.acos( dp );

if( A < 0 ) A = -A;

var sa = Math.sin( A );

var sta = Math.sin( t * A );

var s1ta = Math.sin( ( 1 - t ) * A );

var f1 = s1ta / sa;

var f2 = sta / sa;

var r = new DzQuat();

r.w = f1*u.w + f2*v.w;

r.x = f1*u.x + f2*v.x;

r.y = f1*u.y + f2*v.y;

r.z = f1*u.z + f2*v.z;

r.normalize()

return( r );

}