python plugin for every frame



  • On 04/12/2017 at 07:46, xxxxxxxx wrote:

    Hi All!

    I'm just new into making Python plugins in C4D and may be you can help me with some doubts I have:

    I was using a python expression in a null object to get info from the scene at each frame, so I could save a file with the information on each frame for the whole animation.

    I am now trying to make this into a plugin, but it's not clear to my what type of plugin should I use to execute this function at every frame. Is it a TagPlugin ?

    Also, I'm having some trouble finding documentation on how to define the GUI with description files. Is there specific documentation for that?

    Thanks a lot!



  • On 05/12/2017 at 02:36, xxxxxxxx wrote:

    Welcome here !
    Tag are executed for each change and baking stage is pretty slow. So I would not recommand you to do this.

    But what you can do is register a CommandData plugin (basicly this is plugin listed into the plugin folder) and when you click on it it will Execute your code. Then in your Execute function you can iterate over the timeline to bake it.

    About GUI, personally if I do a GeDialog I create the dialog from CreateLayout and I don't use ressource file which are limited inside GeDialog. And the nice things you can still refer to some string stored into a ressource file using GeLoadString
    About Ressource file I can only suggest you to read the C++ Ressource File  manual

    More generaly I can suggest you to read Python Introduction into the GUI of Cinema4D, and also the C++ one.

    But if you still have question regarding GUI (And I'm sure you got), I guess you can open another topic since it's not related to your first question, it will be more easy for moderator to track your issue. ;)

    Hope it's helped !



  • On 05/12/2017 at 03:09, xxxxxxxx wrote:

    Hi,

    As gr4ph0s already explained, a tag/expression is not the recommended place to process information from the scene and write a file.

    About evaluating each frame of a scene manually, using BaseDocument.SetTime()/ExecutePasses() is the way to go from a script or CommandData plugin.
    The following script loops through each frame of the active document and prints the position of the active object:

    import c4d
      
    def main() :
        # Backup original time
        originalTime = doc.GetTime()
      
        # Loop through all frames of active document
        fps = doc.GetFps()
        minFrame = doc.GetMinTime().GetFrame(fps)
        maxFrame = doc.GetMaxTime().GetFrame(fps)
        for frame in xrange(minFrame, maxFrame+1) :
            
            # Set current frame
            time = c4d.BaseTime(frame, fps)
            doc.SetTime(time)
            # Animate document at current frame
            doc.ExecutePasses(None, True, True, True, c4d.BUILDFLAGS_INTERNALRENDERER)
            
            # Print active object position at current frame
            if op is not None:
                pos = op.GetMg().off
                print("Frame " + str(frame) + " Active Object Position: " + str(pos.x) + ", " + str(pos.y) + ", " + str(pos.z))
            
            c4d.EventAdd()
        
        # Restore to original time
        doc.SetTime(originalTime)
        doc.ExecutePasses(None, True, True, True, c4d.BUILDFLAGS_INTERNALRENDERER)
        c4d.EventAdd()
      
    if __name__=='__main__':
        main()
    

    The code in main() can be easily transferred to a CommandData.Execute().



  • On 05/12/2017 at 03:17, xxxxxxxx wrote:

    What the difference beetwen, both work, but I used all the time the second code.

    doc.ExecutePasses(None, True, True, True, c4d.BUILDFLAGS_INTERNALRENDERER)
    

    And

    c4d.DrawViews(c4d.DRAWFLAGS_ONLY_ACTIVE_VIEW|c4d.DRAWFLAGS_NO_THREAD|c4d.DRAWFLAGS_NO_REDUCTION|c4d.DRAWFLAGS_STATICBREAK)
    c4d.GeSyncMessage(c4d.EVMSG_TIMECHANGED)
    

    EDIT: looking a bit, it's look like the second can only iterate over the active document while the first code can be executed in any BaseDocument, isn'it?



  • On 05/12/2017 at 07:31, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    What the difference beetwen, both work, but I used all the time the second code.

    It depends on the context and the purpose of the script/plugin.

    From a script/plugin that needs to only process data, it's generally better to use BaseDocument.ExecutePasses() as the function just animates the passed document.

    DrawViews() does more operations and is slower (animates and draws the active document).
    GeSyncMessage(c4d.EVMSG_TIMECHANGED) updates managers/special dialogs and this is not necessary when only retrieving data.



  • On 12/12/2017 at 06:09, xxxxxxxx wrote:

    Hey thanks all, this strategy seems to work fine, it iterates through all the frames!

    This saved me a lot of time. I am really grateful!

    Just a follow up question: while the plugin is processing everything, Cinema4d freezes. I guess it's OK, but I can't see the progress or any indicator of what's happening. I guess it is not ok to do all this in a thread?



  • On 12/12/2017 at 06:23, xxxxxxxx wrote:

    You get two methods,
    First one, don't care but inform user through a progress bar using c4d.StatusSetBar in order to set the bar and do not forget to call c4d.StatusClear to clean the bar after the use.

    Or you can implement it into your dialog, I let you check my code here PivotMaster (sorry for the code quality, I guess it was my second scripts ^^')which use a CustomGUi_ProgressBar, and check methode above of CreateLayout to know how to handle this CustomGui.

    About the second way, as you notice, to not freeze the UI you have to create another thread. For that make a clone of your document, using your document.GetClone(), create a derived class of C4DThread, store your cloned document as a member variable inside this class. Then run your function that will iterate the document from this thread.


Log in to reply