AE: Scripting Notes

Just a quick note about a bug and an optimization when scripting After Effects CS4 (and probably earlier versions, too).

addProperty() bug

When adding several effects, each addition invalidates the object variable references to earlier ones. Here’s a code fragment which shows the problem and the solution.

var comp = app.project.activeItem;
var layer = comp.layers.addNull();
layer.name = "null_layer";

var slider1 = layer.Effects.addProperty("Slider Control");
slider1.name = "s1";

var slider2 = layer.Effects.addProperty("Slider Control");
slider2.name = "s2";

// At this point, slider2 is valid, but slider1 is mysteriously not!
// Any action or reference to slider1 will cause an "invalid object" error
//
// What can we do?
//
// Fortunately, they have names by which we can recover them

slider1 = layer.Effects.property("s1");
slider2 = layer.Effects.property("s2");

// Now they're both good to go.

setValueAtTime() Gets very slow!

If you do a whole lot of setValueAtTime() calls to set keyframes, the script will run very slowly. Fortunately, you can just call setValuesAtTimes(), the plural form, to set many at once, which is much more efficient! Makes the minutes seem like seconds, Captain.

// this will be very slow, if myData has more than a few dozen items
for(var i = 0; i < myData.length; i++)
	prop.setValueAtTime(myData[i].t,myData[i].v);

// but if we build up our arrays first...
var timesArray = new Array();
var valuesArray = new Array();
for(var i = 0; i < myData.length; i++)
{
	timesArray.push(myData[i].t);
	valuesArray.push(myData[i].v);
}
// and set them all at once
prop.setValuesAtTime(timesArray,valuesArray);

// it will go lickity-split!

(Thanks creative cow thread!)

Pixel Bender: mod() bug

The mod() Function

In GL Shading Language, and Pixel Bender, the mod function is defined like so: mod(x,y) => x - y * floor(x / y). The result is always positive. We can see quickly that y’s sign goes away. And the floor function pushes towards negative infinity so y * floor(x / y) is always “lower” (not “smaller”) than x.

The mod() Function, GPU vs CPU

Here is a short Pixel Bender kernel.

<languageVersion : 1.0;>
kernel modTest2 <namespace : "x";vendor : "omino.com";version : 1;description : "mod() bug";>
{
    // on PBT build 35, produces different display on GPU/CPU
    parameter float span;
    output pixel4 dst;

    void evaluatePixel()
    {
        float x = outCoord().x / span - 1.0;
        float y = outCoord().y / span - 1.0;
        float m = mod(x,y);
        float t = x - y * floor(x / y); // computed by definition
        dst = float4(m / y,abs(m) / y,t / y,1);
    }

    region generated(){return region(float4(0,0,2.*span,2.*span));}
}

On the GPU, it produces:

And on the CPU:

The blue component is correct on both CPU and GPU because it’s computed according to the formula. The other two color components reveal the nature of the bug. (Probably that x/y is being rounded towards zero.)

As an aside: Notice that some of the CPU rendering’s lines are quite a bit smoother than the GPU rendering’s. Subtly different arithmetics.

The Workaround

A workaround might not be needed, in practice. The mod() function seems to work correctly in Flash, even though it’s on the CPU. (The PBT and Flash implementations are, clearly, if surprisingly, different, since you can turn off the Flash errors & warnings in PBT. Also confirmed in this note by Adobe engineer Tinic Uro.)

On the other hand, After Effects CS4’s CPU-renderer might still have the bug. On the gripping hand, perhaps Adobe will fix it… I’ve had trouble signing up onto the JIRA system to report it, though.

But to get correct render results on the CPU within PBT build 35 on Macintosh, simply use the expanded formula.

float m = mod(x,y); // may have a bug
float m = x - y * floor(x / y); // always works.