#include <UT/UT_DSOVersion.h>
#include <OP/OP_OperatorTable.h>
#include <CH/CH_LocalVariable.h>
#include <PRM/PRM_Include.h>
#include <CHOP/PRM_ChopShared.h>
#include <CHOP/CHOP_VariableList.h>
#include <UT/UT_Interrupt.h>
#include <UT/UT_IStream.h>
#include "CHOP_Spring.h"
using namespace HDK_Sample;
CHOP_SWITCHER(7, "Spring");
static PRM_Name names[] = {
PRM_Name("springk", "Spring Constant"),
PRM_Name("mass", "Mass"),
PRM_Name("dampingk", "Damping Constant"),
PRM_Name("method", "Input Effect"),
PRM_Name("condfromchan", "Initial Conditions From Channel"),
PRM_Name("initpos", "Initial Position"),
PRM_Name("initspeed", "Initial Speed"),
PRM_Name(0),
};
static PRM_Name springMethodItems[] = {
PRM_Name("disp", "Position"),
PRM_Name("force", "Force"),
PRM_Name(0),
};
static PRM_ChoiceList springMethodMenu((PRM_ChoiceListType)
(PRM_CHOICELIST_EXCLUSIVE |
PRM_CHOICELIST_REPLACE),
springMethodItems);
static PRM_Range springConstantRange(PRM_RANGE_RESTRICTED, 0.0,
PRM_RANGE_UI, 1000.0);
static PRM_Range massRange(PRM_RANGE_UI, 0.1f,
PRM_RANGE_UI, 10.0f);
static PRM_Range dampingConstantRange(PRM_RANGE_RESTRICTED, 0.0,
PRM_RANGE_UI, 10.0);
static PRM_Range initDispRange(PRM_RANGE_UI, -10.0,
PRM_RANGE_UI, 10.0);
static PRM_Range initVelRange(PRM_RANGE_UI, -100.0,
PRM_RANGE_UI, 100.0);
static PRM_Default springConstantDefault(100.0);
static PRM_Default massDefault(1.0);
static PRM_Default dampingConstantDefault(1.0);
static PRM_Default initDispDefault(0.0);
static PRM_Default initVelDefault(0.0);
PRM_Template
CHOP_Spring::myTemplateList[] =
{
PRM_Template(PRM_SWITCHER, 2, &PRMswitcherName, switcher),
PRM_Template(PRM_FLT, 1, &names[0], &springConstantDefault, 0,
&springConstantRange),
PRM_Template(PRM_FLT, 1, &names[1], &massDefault, 0,
&massRange),
PRM_Template(PRM_FLT, 1, &names[2], &dampingConstantDefault, 0,
&dampingConstantRange),
PRM_Template(PRM_ORD, 1, &names[3], PRMzeroDefaults,
&springMethodMenu),
PRM_Template(PRM_TOGGLE, 1, &names[4], PRMoneDefaults),
PRM_Template(PRM_FLT, 1, &names[5], &initDispDefault, 0,
&initDispRange),
PRM_Template(PRM_FLT, 1, &names[6], &initVelDefault, 0,
&initVelRange),
PRM_Template(),
};
OP_TemplatePair CHOP_Spring::myTemplatePair(
CHOP_Spring::myTemplateList, &CHOP_Node::myTemplatePair);
unsigned
CHOP_Spring::disableParms()
{
unsigned changes = CHOP_Node::disableParms();
bool grab = GRAB_INITIAL();
changes += enableParm("initpos", !grab);
changes += enableParm("initspeed", !grab);
return changes;
}
enum
{
VAR_C = 200,
VAR_NC = 201
};
CH_LocalVariable
CHOP_Spring::myVariableList[] = {
{ "C", VAR_C, 0 },
{ "NC", VAR_NC, 0 },
{ 0, 0, 0 }
};
OP_VariablePair CHOP_Spring::myVariablePair(
CHOP_Spring::myVariableList, &CHOP_Node::myVariablePair);
float
CHOP_Spring::getVariableValue(int index, int thread)
{
switch(index)
{
case VAR_C:
myChannelDependent=1;
return (float)my_C;
case VAR_NC:
return (float)my_NC;
default:
break;
}
return CHOP_Node::getVariableValue(index, thread);
}
OP_Node *
CHOP_Spring::myConstructor(OP_Network *net,
const char *name,
OP_Operator *op)
{
return new CHOP_Spring(net, name, op);
}
CHOP_Spring::CHOP_Spring( OP_Network *net,
const char *name,
OP_Operator *op)
: CHOP_Realtime(net, name, op)
{
myParmBase = getParmList()->getParmIndex( names[0].getToken() );
mySteady = 0;
}
CHOP_Spring::~CHOP_Spring()
{
}
OP_ERROR
CHOP_Spring::cookMyChop(OP_Context &context)
{
const CL_Clip *clip = 0;
const CL_Track *track = 0;
CL_Track *new_track = 0;
int force_method;
int i, j,length, num_tracks, animated_parms;
float spring_constant;
float d1,d2,f,inc,d;
float mass;
float damping_constant;
float initial_displacement;
float initial_velocity;
float acc, vel;
UT_Interrupt *boss;
int stop;
int count = 0xFFFF;
bool grab_init = GRAB_INITIAL();
clip = copyInput(context, 0, 0, 1);
if (!clip)
return error();
force_method = METHOD();
my_NC = clip->getNumTracks();
my_C= 0;
myChannelDependent=0;
spring_constant = SPRING_CONSTANT(context.getTime());
mass = MASS(context.getTime());
damping_constant = DAMPING_CONSTANT(context.getTime());
animated_parms = myChannelDependent;
inc = 1.0F / myClip->getSampleRate();
if(!grab_init)
{
initial_displacement = INITIAL_DISPLACEMENT(context.getTime());
initial_velocity = INITIAL_VELOCITY(context.getTime());
}
if (error() >= UT_ERROR_ABORT)
return error();
if (mass < 0.001f)
{
mass = 0.001f;
SET_MASS(context.getTime(), mass);
}
boss = UTgetInterrupt();
stop = 0;
if(boss->opStart("Calculating Spring"))
{
if (clip)
{
num_tracks = clip->getNumTracks();
length = clip->getTrackLength();
for (i=0; i<num_tracks; i++)
{
my_C = i;
track = clip->getTrack(i);
new_track = myClip->getTrack(i);
if (!isScoped(track->getName()))
{
*new_track = *track;
continue;
}
if(grab_init || animated_parms)
{
if(animated_parms)
{
spring_constant = SPRING_CONSTANT(context.getTime());
mass = MASS(context.getTime());
if (mass < 0.001f)
mass = 0.001f;
damping_constant = DAMPING_CONSTANT(context.getTime());
}
if(grab_init)
{
initial_displacement = clip->evaluateSingle(track,0);
initial_velocity=(clip->evaluateSingle(track,1) -
initial_displacement);
}
}
d1 = initial_displacement;
d2 = d1 - initial_velocity * inc;
for(j=0; j<length; j++)
{
if(count--==0 && boss->opInterrupt())
{
stop = 1;
break;
}
f = track->getData()[j];
if(!force_method)
f *= spring_constant;
vel = (d1-d2) / inc;
acc = (f - vel*damping_constant - d1*spring_constant)/mass;
vel += acc * inc;
d = d1 + vel * inc;
new_track->getData()[j] = d;
d2 = d1;
d1 = d;
}
if(stop || boss->opInterrupt())
{
stop = 1;
break;
}
}
}
}
boss->opEnd();
return error();
}
namespace HDK_Sample {
class ut_SpringData : public ut_RealtimeData
{
public:
ut_SpringData(const char *name,float d,float v);
virtual ~ut_SpringData() {}
float myDn1;
float myDn2;
virtual bool loadStates(UT_IStream &is, int version);
virtual int saveStates(ostream &os, int binary);
};
}
ut_SpringData::ut_SpringData(const char *name, float d1, float d2)
: ut_RealtimeData(name),
myDn1(d1),
myDn2(d2)
{
}
bool
ut_SpringData::loadStates(UT_IStream &is, int version)
{
if (!ut_RealtimeData::loadStates(is, version))
return false;
if (!is.read(&myDn1))
return false;
if (!is.read(&myDn2))
return false;
return true;
}
int
ut_SpringData::saveStates(ostream &os, int binary)
{
ut_RealtimeData::saveStates(os,binary);
if(binary)
{
UTwrite(os, &myDn1);
UTwrite(os, &myDn2);
}
else
{
os << myDn1 << endl;
os << myDn2 << endl;
}
return 1;
}
int
CHOP_Spring::isSteady() const
{
return mySteady;
}
OP_ERROR
CHOP_Spring::cookMySlice(OP_Context &context, int start, int end)
{
const CL_Clip *clip = inputClip(0,context);
const CL_Track *track = 0;
CL_Track *new_track = 0;
int force_method;
int i, j;
float spring_constant;
float mass;
float d1,d2,f,t,inc,d, acc,vel,oldp;
float damping_constant;
ut_SpringData *block;
float delta;
int animated_parms;
force_method = METHOD();
my_NC = clip->getNumTracks();
my_C= 0;
myChannelDependent=0;
spring_constant = SPRING_CONSTANT(context.getTime());
mass = MASS(context.getTime());
damping_constant = DAMPING_CONSTANT(context.getTime());
animated_parms = myChannelDependent;
inc = 1.0F / myClip->getSampleRate();
if (mass < 0.001f)
mass = 0.001f;
mySteady = 1;
for(i=0; i<myClip->getNumTracks(); i++)
{
my_C = i;
track = clip->getTrack(i);
new_track = myClip->getTrack(i);
if(!isScoped(new_track->getName()))
{
clip->evaluateTime(track,
myClip->getTime(start+myClip->getStart()),
myClip->getTime(end+myClip->getStart()),
new_track->getData(), myClip->getTrackLength());
continue;
}
if(animated_parms)
{
spring_constant = SPRING_CONSTANT(context.getTime());
mass = MASS(context.getTime());
if (mass < 0.001f)
mass = 0.001f;
damping_constant = DAMPING_CONSTANT(context.getTime());
}
block = (ut_SpringData *) getDataBlock(i);
d1 = block->myDn1;
d2 = block->myDn2;
for(j=0; j<myClip->getTrackLength(); j++)
{
t = myClip->getTime(myClip->getStart() + j);
oldp = f = clip->evaluateSingleTime(track, t);
if(!force_method)
f *= spring_constant;
else
oldp /=spring_constant;
vel = (d1-d2) / inc;
acc = (f - vel*damping_constant - d1*spring_constant)/mass;
vel += acc * inc;
d = d1 + vel * inc;
delta = SYSabs(oldp - d);
if (delta > 0.001)
mySteady = 0;
new_track->getData()[j] = d;
d2 = d1;
d1 = d;
}
block->myDn1 = d1;
block->myDn2 = d2;
}
return error();
}
ut_RealtimeData *
CHOP_Spring::newRealtimeDataBlock(const char *name,
const CL_Track *track,
float t)
{
float d, d1, v, rate;
if(GRAB_INITIAL() && track)
{
const CL_Clip *clip = track ? track->getClip() : 0;
d = clip->evaluateSingle(track,clip->getIndex(t));
v = clip->evaluateSingle(track,clip->getIndex(t)+1) - d;
}
else
{
d = INITIAL_DISPLACEMENT(t);
v = INITIAL_VELOCITY(t);
}
rate = myClip->getSampleRate();
if(rate != 0.0F)
d1 = d - v/rate;
else
d1 = d;
return new ut_SpringData(name, d,d1);
}
void newChopOperator(OP_OperatorTable *table)
{
table->addOperator(new OP_Operator("hdk_spring",
"HDK Spring",
CHOP_Spring::myConstructor,
&CHOP_Spring::myTemplatePair,
1,
1,
&CHOP_Spring::myVariablePair));
}