View Full Version : Script of damped oscillated train
galxcom 10-15-2010, 11:49 PM Hi everybody,
There is one truly useful script for After Effects, that adds oscillatory train to any animated property. Script is the following.
amp = .06;
freq = 3.0;
decay = 4.0;
n = 0;
if (numKeys > 0){
n = nearestKey(time).index;
if (key(n).time > time){
n--;
}
}
if (n == 0){
t = 0;
}else{
t = time - key(n).time;
}
if (n > 0){
v = velocityAtTime(key(n).time - thisComp.frameDuration/10);
value + v*amp*Math.sin(freq*t*2*Math.PI)/Math.exp(decay*t);
}else{
value;
}
Script can be added as expression to any property – move, size, rotation, opacity, color, etc - and influence this feature with the keys, adding oscillatory train in the end.
If it's possible to do this via expresso + c.o.f.f.e./python for C4D – that would be really great. There is somewhat similar object in CS_Tools, but it's much worse convenience wise comparing to this script.
I tried to adaptate the script for c4d, but can't get the way how to find the information of the object's keys that the feature is applied to.
I hope i made it clear enough.
My request to those who understand programming languages of C4D to help me out with this!
|
|
Scott Ayers
10-16-2010, 11:01 PM
That looks an awful lot like what the Vibrate tag does.
I can show you how to get at the timeline keys and do things with them like control the vibrate tag's attributes. But I'm afraid I don't know how to overcome C4D's infernal updating tom foolery when it come to the timeline.
In C4D:
1.You do something in memory
2. Then you execute the actual change in the scene with another piece of code.
3. Then if that's not enough of a hassle...You then have to tell the rest of the program that something has changed!
That last step always gives me the most grief when trying to get C4D to do something with code.
The timeline is the worst of the worst in this regard.
I've asked a lot of people about this and none of them can offer any insights to this problem.
So here's my little example.
Create a cube and put a coffee tag on it. Then put a vibrate tag on it.
Make sure the vibrate tag is the first tag on the cube's list of tags.
Put this code into the coffee tag: // This script will toggle the fillet option on/off for a cube whenever the scrubber hits a key
// It will also change the amplitude value of the vibrate tag that's on a Cube the same way
// The problem it the timeline doesn't update properly and only the last key works.
// The timeline refuses to update properly. Until it reaches the last key on the timeline!!!
main(doc, op)
{
var ftag = op->GetFirstTag();// make sure vibrate tag is the first tag on the object
var time = doc->GetTime();
var fps= doc->GetFps();
var currentFrame = time->GetFrame(fps);
var track = op->GetFirstCTrack();
//var curve = track->GetCurve(CC_CURVE, Null); //R11.5 and earlier version
var curve = track->GetCurve(CCURVE_CURVE, FALSE);//R12 version
var keyTotal = curve->GetKeyCount();// count how many keys are on the track
var shakeamount = 100;
while (track)// iterate through all recorded tracks
{
//curve = track->GetCurve(CC_CURVE, FALSE); //R11.5 and earlier version
curve = track->GetCurve(CCURVE_CURVE, FALSE); //Find the curve on a track
var i;
for(i=0; i < keyTotal; i++) //For deleting the last keys in the tracks
{
var keytime = curve->GetKey(i)->GetTime()->GetFrame(fps);// Get the frame the key is on
if(keytime==currentFrame) // If keys are at the same time as the scrubber
{
println(keytime);
op#PRIM_CUBE_DOFILLET = true;
ftag#VIBRATEEXPRESSION_POS_AMPLITUDE = vector(sin(keytime)*shakeamount,0,0);
}
else
{
ftag#VIBRATEEXPRESSION_POS_AMPLITUDE = vector(0,0,0);
op#PRIM_CUBE_DOFILLET = false;
}
}
track = track->GetNext();// go on and do the same thing to the next track until no more tracks are found
}
}
Create a couple of keys that move the cube in the X Axis.
The last key should make the cube vibrate and also turn on the fillet.
This same thing SHOULD happen when the scrubber hits every key. But like I said...I don't know how to make C4D update properly when it comes to things in the timeline.
-EventAdd();
-EventAdd(EVMSG_FCURVECHANGE);
-Message(MESSAGE_UPDATE);
None of them work for me. No matter how I try them.
Good Luck!
-ScottA
galxcom
10-17-2010, 03:41 PM
Thanks for answer! I'll try to comprehend it ))
Scott Ayers
10-21-2010, 08:07 PM
Hey Alex,
I took another crack at this problem. And I found out why only the last key was working.
It turns out the problem was a result of my while loop and else statements. So I took a different approach.
This script will now listen for any keys (only position keys) on an object. And when it hits one of those keys it will trigger whatever you want.
In my example. I chose to toggle the fillet option and the vibrate tag's amplitude option. But you can use anything you want.
Just put your code to turn them ON inside the three for loops. And your code to turn it OFF inside of the if statement code block at the end.
It probably looks long and scary. But don't let that scare you. It's mostly just the same repeated code for each X,Y,Z track of an object making it look more complicated than it actually is.
So any time you want an object to listen for keys. Create a coffee tag for it and put this code in it: //This script toggles a cube's fillet option on when a position key is found
//It also toggles a vibrate tag's amplitude value-->*Make sure the tag is the first one in the list!!
//Put this code in a coffee tag to control the object it's on
//This is an R12 version.
//Edit the GetCurve(CCURVE_CURVE, false); lines to make it work in earlier versions
main(doc,op)
{
var ftag = op->GetFirstTag();// make sure vibrate tag is the first tag on the object
var fps = doc->GetFps();
var currentFrame = GetActiveDocument()->GetTime()->GetFrame(fps);
var Xtrk = op->GetFirstCTrack(); // The X . position track
var Ytrk = op->GetFirstCTrack()->GetNext();// The Y . position track
var Ztrk = op->GetFirstCTrack()->GetNext()->GetNext();// The Z . position track
var Xcurve = Xtrk->GetCurve(CCURVE_CURVE, false);
var Ycurve = Ytrk->GetCurve(CCURVE_CURVE, false);
var Zcurve = Ztrk->GetCurve(CCURVE_CURVE, false);
var XkeyTotal = Xcurve->GetKeyCount();
var YkeyTotal = Ycurve->GetKeyCount();
var ZkeyTotal = Zcurve->GetKeyCount();
var off;// used to toggle the items off
var shakeamount = 200;
var X;
for(X=0; X < XkeyTotal; X++) //Gets the X position keys
{
var keytime = Xcurve->GetKey(X)->GetTime()->GetFrame(fps);// Get the frame the key is on
if(keytime==currentFrame) // If keys are at the same time as the scrubber
{
off = true;
op#PRIM_CUBE_DOFILLET= true;
ftag#VIBRATEEXPRESSION_POS_AMPLITUDE = vector(sin(keytime)*shakeamount,0,0);
}
}
var Y;
for(Y=0; Y < YkeyTotal; Y++) //Gets the Y position keys
{
var keytime = Ycurve->GetKey(Y)->GetTime()->GetFrame(fps);// Get the frame the key is on
if(keytime==currentFrame) // If keys are at the same time as the scrubber
{
off = true;
op#PRIM_CUBE_DOFILLET= true;
ftag#VIBRATEEXPRESSION_POS_AMPLITUDE = vector(sin(keytime)*shakeamount,0,0);
}
}
var Z;
for(Z=0; Z < ZkeyTotal; Z++) //Gets the Z position keys
{
var keytime = Zcurve->GetKey(Z)->GetTime()->GetFrame(fps);// Get the frame the key is on
if(keytime==currentFrame) // If keys are at the same time as the scrubber
{
off = true;
op#PRIM_CUBE_DOFILLET= true;
ftag#VIBRATEEXPRESSION_POS_AMPLITUDE = vector(sin(keytime)*shakeamount,0,0);
}
}
if(off == false)
{
//println("turn fillet off");
op#PRIM_CUBE_DOFILLET= false; // turns the fillet option off
ftag#VIBRATEEXPRESSION_POS_AMPLITUDE = vector(0,0,0);
}
}
This isn't as robust as the AE keys.
But at least it gives you a way to execute something in the scene whenever the time line scrubber hits a position key. What you chose to trigger with it is up to your imagination. :D
-ScottA
galxcom
10-21-2010, 09:09 PM
Scott, thank you very much for help!
About code. For me it works (on R11.5) only if i turn off all GetNext() statements:
var Xtrk = op->GetFirstCTrack(); // The X . position track
var Ytrk = op->GetFirstCTrack(); //->GetNext();// The Y . position track
var Ztrk = op->GetFirstCTrack(); //->GetNext();//->GetNext();// The Z . position track
else i getting COFFEE ERROR! (5) Incompatible values... NIL / OBJECT
Thus it is assured turn the fillet on|off but only if locate key on x.positon track.
Scott Ayers
10-21-2010, 09:29 PM
I don't have 11.5 installed so I can't test this. But I think the only things that should need to be changed for 11.5 are these:
from this:var Xcurve = Xtrk->GetCurve(CCURVE_CURVE, false);
var Ycurve = Ytrk->GetCurve(CCURVE_CURVE, false);
var Zcurve = Ztrk->GetCurve(CCURVE_CURVE, false);
to this: var Xcurve = Xtrk->GetCurve(CC_CURVE, Null);
var Ycurve = Ytrk->GetCurve(CC_CURVE, Null);
var Zcurve = Ztrk->GetCurve(CC_CURVE, Null);
Also. Make sure that you have at least one key set on the first frame of the X,Y, and Z position tracks.
An object doesn't have any tracks until the first key is recorded. And I didn't add any error handling abilities to handle if these tracks don't exist.
-ScottA
galxcom
10-21-2010, 09:53 PM
I've changed code for 11.5 right away as you wrote, but error was in another place :(
Ok i'll try to figure out why it shows error in lines with GetNext()!
Scott Ayers
10-22-2010, 01:52 AM
Doh!
Before you get too far with this Alex. I just noticed that even though this script works in the scene view.
I just noticed that it doesn't render!
It never even occurred to me that it wouldn't render. :argh:
I don't know if this sort of thing is possible with C++ or not. But it seems that using Coffee to make changes to objects on the fly, with time line running, don't show up in the renders.
Sorry about that.
-ScottA
galxcom
10-22-2010, 02:07 AM
now it works, thank you! the problem was i didn't set keys for y and z coords
about render... well, it seems user will have to bake keys :(
galxcom
10-22-2010, 12:49 PM
By the way can u please give a hint how to get value of previous frame?
for example we can get value in current frame:
var Xvalue = Xcurve->GetValue(currentTime,fps); // The x position at current frame; but I cant figure out how to get it in previous one!
Scott Ayers
10-22-2010, 04:46 PM
There isn't any built in Coffee functions to do this in 11.5.
I think there are some functions to do this in R12 with python. But I haven't really messed with python much yet.
So you have to do this manually and use integers to jump to specific keys.
To get to the next key from where you are you do a +1.
like this: GetKey(currentkey +1)->GetValue();
To get to the previous key from where you are you do a -1.
like this: GetKey(currentkey -1)->GetValue();
You can also just use the CallCommands for the get previous and get next keys too.
It's a matter of personal choice and what suits your needs the best.
In case you don't know this. Keys on a curve are stored in an array.
So you can get at them using the convention GetKey(0); which gets the first key on the curve. And so on.
Or
You can collect them all with a for loop. Then use that to do a whole bunch of stuff: var track = op->GetFirstCTrack();
var curve = track->GetCurve(CC_CURVE, FALSE); // Get the F-curve R11.5 version
//var curve = track->GetCurve(CCURVE_CURVE, FALSE); // R12 version
var totalkeys = curve->GetKeyCount(); // counts all of the keys in the array
var i;
for(i=0; i<totalkeys; i++)
{
println(i); //prints all the key index values
println(curve->GetKey(i)->GetValue());//prints all the keys values
}
-ScottA
galxcom
10-25-2010, 05:29 PM
Scott, thanx again!
But! :) I need to find value not in key time. It's clear for me (from your previous code examples (thanx for them!)) how to find key's values.
My goal is to find a rate of change (velocity) of track/curve in current time. To do this i need to have two values: in current frame and in frame before.
I can find a value in current frame using code GetValue(currentTime,fps);
Here currentTime is BaseTime object which can be found using code currentTime = GetActiveDocument()->GetTime();
Now i can't figure out how to find value in previous frame (not key). :sad:
Scott Ayers
10-25-2010, 07:15 PM
Sorry about that.
You switched from keys and curves to frames on me and I didn't catch that.
Here's how to get the previous frame: //This Gets the previous frame
var t = doc->GetTime();
var frame = t->GetFrame(doc->GetFps());// The frame the slider is on
var previous = frame -1;// the previous frame
println(previous); // Prints the previous frame
And just in case you want to move the slider to the previous frame at some pont.
Here's how to do that: //Just in case you want to also move the slider to the previous frame
var t = doc->GetTime();
var frame = t->GetFrame(doc->GetFps());// The frame the slider is on
var previous = frame -1;// the previous frame
println(previous); // Prints the previous frame
var bt = doc->GetTime();
bt->SetFrame(previous, 30); // Set the slider to Frame in memory;
doc->SetTime(bt);// Execute the slider change
Lennart posted a script called "Speedo" that gets the velocity of an object in the scripts section.
It might help you with what you're trying to do.
-ScottA
galxcom
10-26-2010, 02:00 AM
Mmm...
It seems i've been misunderstood. I guess it is because of my language barrier :)
But thanx again for answering! You are very patient and thanx for this!!
The trouble for me is that i can't get value in time, not sequence number of the keyframe.
I'll try to explain.
For example, to make realtime convergent oscillation for x position of object I need to find velocity in current frame at first. To find it I need to have two values: x position in current frame and x position in previous frame. And I guess it is necessary find x values without positioning scrubber back. Otherwise it would not be a realtime script.
Then when I found x(this frame) and x(previous frame) values I can find velocity using similar code : velocity_of_x_at_current_frame = (x(this frame) - x(previous frame))*fps
Again. I know how to find x(this frame) : GetValue(GetActiveDocument()->GetTime(), fps) But but i can't figure out how to find x(previous frame).
rustEdge
10-26-2010, 02:57 AM
There are only two ways of getting previous values for this kind of problem. One is a brute-force method of caching the values as you go through the timeline. Better option is to interpret your parameters using calculus to determine the positions/velocities at certain points in time. Unfortunately, AFAIK the math libraries in the API don't have that built in. So yeah, you'll most likely need to have your script bake in keyframes to get what you're after.
Scott Ayers
10-26-2010, 06:13 PM
Mmm...
It seems i've been misunderstood. I guess it is because of my language barrier :).
You're doing fine. The problem is me. I have a hard time figuring out what people are asking for sometimes.
What makes what (I think ) you're asking so difficult is that you're trying to compare key values where the slider is. To the previous key frame's key values.
This is fairly simple if you hand pick the keys yourself like this :
var prevKey = curve->GetKey(0)->GetValue();
var key = curve->GetKey(1)->GetValue();
Then perform the math you want on those two variables.
But comparing frames, slider location, and key values gets pretty tricky. And it requires some more advanced type of programming theory. But it is doable, at least to a point.
Here's what I came up with that compares all of them.
It's only for the first track(the X Position) because the script would get very long if I added support for the other tracks.
Keep in mind I've only got a few months experience with programming. And I'm still a beginner when it comes to solving complicated problems like this. So this might not be the best approach:
//Open the console to see the results of this script
main(doc,op)
{
//Get the slider frame and key value section
var t = doc->GetTime();
var fps= doc->GetFps();
var ft = op->GetFirstCTrack();// This will be the Position . X track
var curve = ft->GetCurve(CC_CURVE, Null);
//var curve = ft->GetCurve(CCURVE_CURVE, false);//R12 version
var KeyTotal = curve->GetKeyCount();
var currentKeyvalue = ft->GetValue(doc, t, fps);
var currentFrame = t->GetFrame(fps);
println("Slider key value= ",currentKeyvalue);
println("Slider value= ",currentFrame);
// Get the value of the last key and the previous keys section
var lastkey;
var prevkey;
var i;
for(i=0; i<KeyTotal; i++)
{
var keytime = curve->GetKey(i)->GetTime()->GetFrame(fps);// get the frame the key is on
var key = curve->GetKey(i)->GetValue();
var prevkeyvalue = curve->GetKey(i-1)->GetValue();
lastkey = key;
prevkey = prevkeyvalue;
}
EventAdd(EVMSG_FCURVECHANGE);//updates the F-Curve timeline changes
println("LastKey's Value = ",lastkey);
println("PreviousKey's Value = ",prevkey);
var velocity = (prevkey - currentKeyvalue)*fps;// <---not sure if this is correct
println ("Velocity = ",velocity);
}
It might not be exactly what you're looking for.
But it's the closest I can get with the amount of programming experience I have.
-ScottA
galxcom
10-26-2010, 06:40 PM
Scott, thanx for answer!
This script is useful to find avarege speed between two keyframes. But not between simple frames (frames with no keys) what I'm looking for.
It seems rustEdge (btw thanx for hint) is right. To implement such action like getting two values of "not keyed" frames we have to bake timeline or store arrays as global variables or in file. I don't know how to implement these ways at the moment.
Now I'm trying to store values using the expresso tag instead of c.o.f.f.e.e. tag to afford storing variables in separate nodes.
galxcom
10-27-2010, 01:17 AM
Problem solved! http://www.helloluxx.com/cinema4d-mograph/springy-camera/
Very easy way, isn't it? ))
Scott Ayers
10-27-2010, 03:33 PM
No fair! That's cheating.;)
-ScottA
CGTalk Moderation
10-27-2010, 03:33 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
vBulletin v3.0.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.