Skip navigation
Currently Being Moderated

Parameter groups and Debug to Release

Jan 20, 2010 1:08 PM

Hello,

 

I have created a plugin and I am facing two issues right now. Firstly, my UI is a bit cluttered. I read all the SDK but couldn't find enough information on how to create Parameter Groups.  Secondly, can anyone tell me how to convert the plugin from Debug to Release in Xcode and on Windows? Also everytime I change the Outflags, AE throws an error saying that theere is a mismatch with PiPL. How is that PiPL number calculated? Is that just a hex number?

 

Thank you very much in advance.

 
Replies
  • Currently Being Moderated
    Jan 20, 2010 1:38 PM   in reply to gutsblow

    Use PF_ADD_TOPIC and PF_END_TOPIC

     

    I have a parameter group set up like this...

     

    in my .h file...

    enum

    {

    LUMINANCE_DISK_ID = 1

     

     

    ,TOPIC_FEEDBACK_ID

    ,SHOW_HISTOGRAM_DISK_ID

    ,HIST_CORNER_ID

    ,HIST_SIZE_ID

    ,END_TOPIC_FEEDBACK_ID

     

    ,My_NUM_PARAMS

    };

     

    and in my .cpp file...

     

     

    //

     

     

    // ---------- Histogram group

     

     

    //

    AEFX_CLR_STRUCT(def);

    PF_ADD_TOPIC(

     

    "Histogram Overlay", TOPIC_FEEDBACK_ID);

    AEFX_CLR_STRUCT(def);

    def.flags = PF_ParamFlag_CANNOT_TIME_VARY;

    PF_ADD_CHECKBOX(STR(StrID_ShowHist_Name), STR(StrID_ShowHist_Description), FALSE, 0, SHOW_HISTOGRAM_DISK_ID);

    AEFX_CLR_STRUCT(def);

    def.flags = PF_ParamFlag_SUPERVISE;

    PF_ADD_POINT(

    "Histogram location", HIST_LOCATION_X, HIST_LOCATION_Y, RESTRICT_BOUNDS, HIST_CORNER_ID);

    AEFX_CLR_STRUCT(def);

    def.flags = PF_ParamFlag_SUPERVISE;

    PF_ADD_PERCENT(

    "Histogram width", HIST_PCT_DFLT, HIST_SIZE_ID);

     

    //PF_ADD_FIXED("Histogram width", HIST_SIZE_MIN, HIST_SIZE_MAX, HIST_SIZE_MIN, HIST_SIZE_MAX, HIST_SIZE_DFLT, 0,0,0, HIST_SIZE_ID);

    AEFX_CLR_STRUCT(def);

    PF_END_TOPIC(END_TOPIC_FEEDBACK_ID);

     

     

    Regarding the Debug/Release question:

    In XCODE I just duplicated the Debug configuration and named it Release. Then I removed the preprocessor macro that defined "_DEBUG" in my debug verison. I also checked "Strip debug symbols during copy". There may be other changes to make the release version smaller.

     

    Regarding the PiPL mismatch. I just change the PiPL setting to match what AE says in the error, but it is possible to figure it out.

     

    -Mike

     

     

     

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 21, 2010 12:59 AM   in reply to gutsblow

    i doubt this would make an improvement on your quality of life,

    but here's some info regarding the resource version.

     

    the resource version is actually calculated using a macro defined in the SDK.

    you can see it in the global setup function of your plug-in:

    out_data->my_version = PF_VERSION( MAJOR_VERSION,

    MINOR_VERSION,

    BUG_VERSION,

    STAGE_VERSION,

    BUILD_VERSION);

    it uses bit shifting to calculate the value.

    however, translated to human it reads:

    RESOURCE_VERSION =

    MAJOR_VERSION * 524288 +

    MINOR_VERSION * 32768 +

    BUG_VERSION * 2048 +

    STAGE_VERSION * 512 +

    BUILD_VERSION

    i tried to force this calculation as an enum, to make the resource version update automatically,
    and it worked on visual studio, but failed on xcode...
    alas, on visual studio, if you hover over  the enum of the RESOURCE_VERSION it will tell you it's value, and you can copy it to the resource file, without manually calculating it.
    and don't forget that on windows you have to delete the .rc file or the build will not generate a new one even if changes were made in the resource file.
    :-)

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 21, 2010 1:06 PM   in reply to gutsblow

    dude, you're not bugging me. :-)

    i help when i can.

     

    anyways,

    the version number has nothing to do with non-developer-machine compatibility.

    you can set it on PF_STAGE_ALPHA, and all will be well.

    on windows the compatibility is a dependency issue.

    different versions of windows have different CRT dlls installed.

    the host machine must have EXACTLY the same dll versions as your plug-in is dependent on.

    and of course, no normal machine has debug dlls on it. so you must use either /MD or /MT. and NOT /MDd or /MTd.

     

    now you're left with two options:

    /MD and /MT.

    what do they mean?

     

    /MD means your plug-in will be dependent on other dlls for the use of some functions.

    up side:

    smaller code. in my opinion, meaningless. plug-ins weigh 500k at most, so what's the save here?

    ms claim that you can enjoy the benefit of updated dlls for increased security and stability. that's practically a lie, as your plug won't use newer versions of the dll, unless compiled do be dependent on the newer version.

    down side:

    welcome to dll hell.

    windows xp sp2 comes with CRT dlls version 7.

    vc2005 creates plug-ins dependent on version 8.

    that means you must distribute your plug-in along with the correct dlls, and put them into place.

    in my opinion. avoid at all costs.

     

    /MT means that you include the code from external libraries in the code of your plug-in.

    up side:

    resolves 99% of dependency issues, and makes your plug-in good to go with no deployment on the host machine.

    down side:

    much longer build times, and larger code. in my opinion, totally worth it.

     

     

    i personally go with the /MT option, as it's benefits by far outweigh the drawbacks.

    to make sure your plug isn't dependent on anything it shouldn't throw your plug into "dependency walker".

    some dependencies are unavoidable, such as kernel.dll.

    the things you don't want to see there are MSVCR80.DLL or heaven forbid MSVCR80_DEBUG.DLL.

    if you still get debug dlls in your dependency list make sure you don't have any _DEBUG symbols in your project settings.

    you can put "dependency walker" on a non-developer machine and throw your plug-in into it.

    you'll immediately see if your plug-in gets all it needs.

     

    if you choose to go with the /MD option (why???) then you should expect to see MSVCR80.DLL in your dependency list.

    in such a case you'll have to get the MS redistributable dlls package and ship it along with your plug-in for any xp machine. yuk.

     

    throw some other plug-ins, by adobe or other vendors into dependency walker.

    see what they're dependent on.

     

    besides the dependency issue,

    when compiling release you can set some options that are unavailable when debugging.

    you can ask for performance optimization. that speeded up some plug-ins by 20% for me.

    you can also ask for fast floating point model rather than precise. that gave me an additional 10% in speed.

    (these things very much depend on the inner works of your plug-in)

     

    that's all i can think of at the moment.

    it's been a while since i dealt with these issues. (now days i just work with my ready made settings)

    i hope this answers your question.

    :-)

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 22, 2010 4:28 AM   in reply to gutsblow

    Thank you in advance Shashar! (I also noticed I have misspelled your name in

    all my previous posts.)

    and you have misspelled it again.

    if you insist on writing my name, consider using copy and paste. (ctrl + c / v on windows, cmd + c / v on mac)

    :-)

     

    as for your question,

    it's actually a compositing problem.

    you're compositing a fully opaque solid over an image with a semi transparent alpha.

    just like in a composition, it will fill the empty pixels with red, regardless of the "multiply" transfer mode.

    instead of using composite_rect try using transfer_rect.

    it has an optional mask_world. it composites the input over the output through the alpha of the mask world.

    you should try the following steps:

    1. fill the output world with black using the fill function.

    2. create a temp world and fill it with white (or red or whatever you want).

    3. composite the temp white world over the black-filled output world using transfer_rect, utilizing the mask_world for the alpha.

     

    you'll get a white image in the shape of your alpha over an opaque black background.

    it calls for one extra compositing operation, more than just multiplying the output with red,

    so check if it's eventually faster than using the iteration suite.

    i guessing it's faster, but who knows.

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 23, 2010 1:30 AM   in reply to gutsblow

    i struggled with the same problem myself a while ago,

    and for the same reason as well. using the fast blur function.

     

    b.t.w as opposed to it's name, it's actually a gaussian blur that works fast.

    it's exactly as fast as the gaussian blur adobe uses. (very very fast)

    to be precise, when used in high quality, it's gaussian, and when used in low quality, it's linear.

    if you really want to dive into detail, then it's not a real gaussian blur.

    it's actually a box blur, either repeated to resemble gaussian distribution (3 repeats of box, come as close as 3% difference from gaussian), or simply one box warped to a gaussian curve. (which is what i think happens in the fast blur function)

     

    back to our issue.

    as hard as i tried i couldn't find a way to convert an effectWorld into a world.

    only the opposite is possible via the function you mentioned.

    the practical meaning of that is that you can't fast blur the input and output worlds directly, as they are effectWolrds.

     

    however, it's not all bad news.

    all other intermediate worlds can be be blurred.

    the function that converts a world into an effectWorld is actually a wrapper. (much like a pointer in c++)

    so instead of creating a new effectWorld, you can create a world instead and wrap it into an effectWorld.

    the processing time for that is negligible, as it only creates a pointer to the allocated world,

    so whatever you put in the one, also appears in the other.

    in other words, working with worlds wrapped into effectWorlds allows you to enjoy the best of all... well... worlds. and at almost 0 cost.

    so while you can't blur the input or the output directly, with one extra copy operation you can solve that problem,

    and if your effect uses new effectWrolds anyways, then there's no waste at all, as you can use warped worlds instead,

    blur the world, and get a blurred effectWorld.

     

     

    to copy from an effectWorld into a world you just need to convert the world into an effectWorld,

    and use PF_COPY between the effectWorlds. (ant NOT the iteration suite)

    the wrapped world is actually the one that will get copied to, via the effectWorld wrapper.

     

     

    there are alternatives.

    you can use the convolve function to blur your image.

    you also have a function in the sdk to create a gaussian kernel for you.

    if you use a 2D kernel, it will render as slow as the gaussian blur on AE 3.1

    if you use two passes of a 1D kernel (one for vertical and one for horizontal), it will render faster. as fast as AE 5.5 did. (exponentially faster than 3.1)

    both options are much slower then using the fast blur function, and the difference will grow exponentially, as the blur size grows.

    the fast blur function renders the blur very fast, dependent only on image, and regardless of blur size.

    the upside of using convolve: it works directly on effectWorlds, so you can save one copy operation.

    the downside: it's much slower than the fast blur function.

    bottom line: don't use the convolve for blurring. it's not worth it.

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 23, 2010 3:24 AM   in reply to gutsblow

    that's no biggie.

    here are the steps:

     

    //first you create handles to a new world and a new effectWorld

    //no memory is allocated yet. you're just declaring the handles.

    AEGP_WorldH worldH = NULL;

    PF_EffectWorld effectWorld;

     

    //now you allocate the memory for the world

    ERR(suites.WorldSuite2()->AEGP_New( NULL, AEGP_WorldType_8, width, height, &worldH));

     

    //wrapping the world handle into an effect world.

    //what does wrapping mean?

    //it creates the minimal data needed to describe an effectWorld,

    //but for the content of that effectWorld, it points to the memory chunk where the world stores it's pixels.

    //this way the world and effectWorld share the same image buffer.

    ERR(suites.WorldSuite2()->AEGP_FillOutPFEffectWorld( worldH, &effectWorld));

     

    //if you need to, that's how you copy into the wrapped world

    ERR(PF_COPY(inputP, &effectWorld, NULL, NULL));

     

    //we can now blur the world.

    ERR(suites.WorldSuite2()->AEGP_FastBlur( amount,//blur radius in pixels

    PF_MF_Alpha_STRAIGHT,

    PF_Quality_HI,//high is gaussian, low if linear.

    worldH));

     

    //if you need to, that's how you copy from the wrapped world.

    ERR(PF_COPY(&effectWorld, output, NULL, NULL));

     

    //you dispose ONLY of the world. NOT the effectWorld.

    //remember. the allocated memory belongs to the world.

    //the effectWorld just points to that address in memory, but doesn't own it.

    ERR(suites.WorldSuite2()->AEGP_Dispose( worldH));

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 25, 2010 2:45 AM   in reply to gutsblow

    once upon a time, there was an API call ADM (adobe dialog manager).

    it allowed plug-in programmers to create windows with various controls, including text boxes.

    i don't know why, but that is no longer supported.

     

    anyways,

    since you mentioned using interface builder, i'm guessing we're talking mac.

    i must tell you my friend, you've hit the spot with this question.

    there are many small problems in this process that have left me frustrated for days on end.

     

    so now you have a .nib file and you wonder, "now what?"

    first, open the .nib file in interface builder.

    set the window's class type to "movable modal".

    this will ensure the window being in front of AE, and prevent interactions with AE until the window is released.

     

    the second step would be to add the .nib file to the xcode project.

    obviously, you add the file like any other file, and it appears in the project.

    you should see that it also appears on the last folder on the project, called "NIB Files".

    we're not done adding.

    now twirl down the "targets" category, and twirl down your plug-in's name.

    you can now see the build stages.

    now add a new build stage, "copy bundle resources", and add the .nib file into it as well.

    now compile your plug-in, and go to the generated file.

    open the package and check that the nib file is indeed there.

    included successfully?

    we're still adding stuff.

    now add two objects of "existing frameworks" type.

    "IOKit.framework" and "CoreFoundation.framework"

    we move on.

     

    go to the "mac" folder inside the project folder and locate a file called "YourPlugName.plugin-info.plist".

    open it with property list editor.

    we're interested mostly in the CFBundleIdentifier string.

    it's most probably "com.adobe.AfterEffects.YourPlugName"

    you can keep it like that, or change it to whatever you want.

    just make sure it's unique, so no other bundle in the world would use the same name.

    copy that name. we'll need it for later.

     

    and this is how it's all implemented:

     

    //some declarations

    CFBundleRef bundleRef = NULL;

    IBNibRef theNibRef = NULL;

    OSStatus theErr = NULL;

    const EventTypeSpec kEvents[] = { { kEventClassCommand, kEventCommandProcess } };

    WindowRef ActivationWindow = NULL;

     

    // Create a nib reference

    bundleRef = CFBundleGetBundleWithIdentifier( CFSTR("replace this text with the bindle name from earlier"));

    theErr = CreateNibReferenceWithCFBundle( bundleRef, CFSTR("replace this text with the .nib file name, without the .nib extension"), &theNibRef );

     

    //an optioanl check for errors.

    //you should put this after almost every step.

    //it's up to you.

    //require_noerr( theErr, CantGetNibRef );

     

    // Create a window.

    //it's still not visible at this stage.

    theErr = CreateWindowFromNib( theNibRef, CFSTR( "Window" ), &ActivationWindow );

     

     

    // We don't need the nib reference anymore.

    DisposeNibReference( theNibRef );

     

    // making sure the window will show somewhere on screen.

    RepositionWindow( ActivationWindow, NULL, kWindowCascadeOnMainScreen );

     

     

    // The window was created hidden so show it.

    ShowWindow( ActivationWindow );

     

    // Install event handlers

    theErr = InstallWindowEventHandler( ActivationWindow, EventHandlerFunction,

    GetEventTypeCount( kEvents ), kEvents, (void*)ActivationWindow, NULL );

     

     

    // Call the event loop

    RunApplicationEventLoop();

    this is it. you're now stuck in the event loop, until you terminate it.
    here's the basic code for event handling:
    #define OkCommand 'ok  '
    #define CancelCommand 'not!'
    OSStatus ActivationFormEventHandler(
    EventHandlerCallRef inHandlerCallRef,
    EventRef inEvent,
    void *inData )
    {
    OSStatus theErr = eventNotHandledErr;
    HICommand theHICommand;
    WindowRef theWindow = ( WindowRef ) inData;
    GetEventParameter(
    inEvent, kEventParamDirectObject, typeHICommand, NULL,
    sizeof( HICommand ), NULL, &theHICommand );
    switch ( theHICommand.commandID )
    {
    case OkCommand:
    //do whatever you want to do when the user presses "ok"
    //terminating the window, and releasing the process back to the effect.
    DisposeWindow( theWindow );
    QuitApplicationEventLoop();
    break;
    case CancelCommand:
    //and the same for when the user clicks "cancel"
    QuitApplicationEventLoop();
    theErr = noErr;
    break;
    }
    return theErr;
    }
    that's it.
    this should get you up and running.
    :-)

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 27, 2010 7:22 AM   in reply to gutsblow

    there's a reason why i wrote the detailed steps.

    no amount of general advise would give the complete picture.

    finding out what makes that process fail is like solving a quest, like "myst" or "monkey island".

    you must get the ref from a function that requires a handle that can only be obtained via a system call valid only during some events...

    you get the point.

    sorting this out was an uphill battle that left me banging my head on the keyboard numerous times.

    i won't be surprised if my forehead showed faint signs of inverted qwerty...

     

    anyways,

    how do you save that data?

    i personally write licensing data to a separate file, and not the effect itself.

    a simple fopen() and fclose() do the job.

    on osX the plug-ins are actually packages, so you can add a new file to the package.

    on windows you don't have that option.

    i never tried changing the effect file from within it's own process.

    i think that's possible, but you may encounter problems such as the file being locked because it's in use.

    no idea. never tried it.

     

    another reason to avoid changing the file itself is that the user ends up with an unlocked plug-in he can send to anyone.

    to avoid that, you must also include a unique identifier of the computer on which the serialization took place.

    thinking of it, you'd have to do the same for an external file, or someone might distribute your licence file for the world to use.

     

    in my opinion, it's better to do with an external file.

    this way when the user downloads an update for your plug-in, he won't have to re-serialize it, as the licence file remains the same.

     

    as for UIs being a pain to develop, i sadly agree.

    adobe used to have such support (ADM) but it no longer exists.

     
    |
    Mark as:
  • Currently Being Moderated
    Jan 31, 2010 11:35 PM   in reply to gutsblow

    PR_RenderContextH... that's a tough one.

    only AEGPs of type artisan get that parameter.

    artisans are the plug-ins that handle composition rendering. (such as "advanced 3D")

    so when AE wants to render a comp, and your artisan is the one selected to render that comp, AE will hand you a valid PR_RenderContextH.

    there's no other way to obtain it, and all functions who require an PR_RenderContextH, woun't work without a valid one.

    nor should they, by the way, because the PR_RenderContextH refers to a specific set of layers (not all layers) in a specific comp, and not just any layer in any comp.

     

    most of the functions requiring PR_RenderContextH, are actually useless for purposes other then artisans, and those who aren't useless, have alternatives.

    to obtain the camera affecting your effect, use AEGP_GetEffectCamera and AEGP_GetEffectCameraMatrix instead.

     
    |
    Mark as:
  • Currently Being Moderated
    Feb 1, 2010 9:38 AM   in reply to gutsblow

    you asked two simple questions.

    unfortunately, you stumbled upon some tough issues there.

    let's start with the complex, and move on to the very complex.

     

    keyboard shortcuts:

    i don't know of a direct and simple way of creating keyboard shortcuts.

    maybe there is a simple way, but i sure as hell couldn't find it.

    there are however, indirect and complex ways.

    a. if you're using a palette, then as long as it's visible (fully shown or just tab peeking), you can get notified of all keys being pressed, and decide on your own whether the correct combination was hit.

    b. if you're an effect, you can add a custom param which if visible (twirled down in an open panel) will allow you to get the same notifications i mentioned before.

    c. if you're a simple menu command AEGP, you can leech off of existing shortcuts. find out the command number for "turn off video for all other layers" (ctrl + alt + shift + V), and register it in the command hook. when you get that call you decide whether the user meant for your action to take place, or the improbable command for turning all other layers off (who uses that???).

    d. you can try adding new entries to the shortcut prefs text document to which AE complies. i don't think you can define new shortcuts, rather than just changing existing ones. but hey, if you're willing to experiment, i'd love to hear what you came up with.

     

     

    getting the camera:

    you're describing a not-so-normal situation, in which you need to get the comp camera or view, from an AEGP, and not an effect in that comp.

    you could scan the currently active comp (AEGP_GetMostRecentlyUsedComp), and hit the relevant camera that way,

    but that won't give you the custom view.

    the only way i know of for getting some info about the custom view is by using AEGP_GetLayerToWorldXformFromView.

    to use that you must have a layer in it's reset position in the comp, so you'd get a matrix that refers to the true orientation of the scene.

    but what if you don't have such a layer?

    luckily the process you describe is not continuous and only happens once per user request.

    so when the user triggers this command, you can add a new layer, get it's LayerToWorldXformMatrix, and delete it, without the user noticing.

     

    you're not out of the woods yet.

    now you have a matrix and you need to decompose it. (welcome to hell)

    matrices have more than one possible solution, but at least you have some given constants when dealing with custom views. (size is always 1 for xyz)

     

    don't despair!

    it's all solvable, and most likely there are simpler solutions that i don't know of.

    :-)

     
    |
    Mark as:
  • Currently Being Moderated
    Feb 2, 2010 12:47 AM   in reply to gutsblow

    before you try all of this, try to check if GetEffectCamera really does return null when in custom view.

    who knows? the documentation says it will return a null if no camera is active. but maybe it considers the custom view a camera...

     

    as for the constraints on the custom view camera,

    yes, by size i meant scale.

    another important constraint is that the camera is always leveled. it never banks.

    and as you said, the zoom is always 1. (or at least, it's always in the same ratio to the comp dimension)

     

    i'm not sure you'll have to invert the matrix you get from AEGP_GetLayerToWorldXformFromView...

    but that's no mystery. the moment you try to apply the data you retrieve from the matrix, you'll know.

     
    |
    Mark as:
  • Currently Being Moderated
    Feb 2, 2010 1:32 AM   in reply to gutsblow

    i forgot something important.

    matrices in AE is row based, while openGL is column based.

    that means that what you expect to go to [2,3] in the matrix actually goes to [3,2]

    this ought to save you a couple of nights worth of sleep.

     
    |
    Mark as:
  • Currently Being Moderated
    Feb 2, 2010 1:52 AM   in reply to gutsblow

    sorry... i have mislead you.

    GetEffectCamera can not work because it returns a layer handle, and not camera properties, so if there's no camera layer, it's useless.   :-(

     

    as for GetEffectCameraMatrix,

    are you sure it gives you the custom view and not just a camera layer in the comp?

    to use it from an AEGP use the same trick i mentioned before.

    when the user triggers you command, put a new layer in the comp, apply a special made effect (that does nothing but ask fro the camera and send that info to the AEGP) to that layer, and remove the layer from the comp.

     

    HOWEVER, i see no advantage over using AEGP_GetLayerToWorldXformFromView, as this method saves you the steps of adding (and also programming) a new effect to the layer to ask for the transformation.

    this way you can get the same matrix directly from the newly added layer.

    it also allows you to finish the process without exiting you AEGP, so the effect could to it's thing.

     
    |
    Mark as:
  • Currently Being Moderated
    Feb 24, 2010 3:17 AM   in reply to gutsblow

    you are crazy guys.... as qutsblow wrote, you should write a book or some articles, there are so much information. I am just abusing this post, but i had to say that, its really informative.

     

    thanks

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 13, 2010 12:17 PM   in reply to gutsblow

    Hi gutsblow,

     

    I understand you know how to implement a dialog on the Mac platform of the AE CS5 SDK? I am a newbie with OSX programming and I simply want to port my Windows plugin which implements a dialog when the user clicks the Options button. I was wondering if you wouldn't mind posting a description on how to do this or point me in the right direction.

     

    Thanks!

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 13, 2010 1:51 PM   in reply to gutsblow

    Thanks so much, I will take a look. It's good to know there are those willing to share!

     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (1)

Answers + Points = Status

  • 10 points awarded for Correct Answers
  • 5 points awarded for Helpful Answers
  • 10,000+ points
  • 1,001-10,000 points
  • 501-1,000 points
  • 5-500 points