Plugin not updating with frame advance rendering



  • On 28/10/2014 at 15:50, xxxxxxxx wrote:

    Hi
    The plugin I'm working on generates new, random geometry every frame in the scene - it works perfectly when I scrub through the timeline.  But when I go to render some frames, it's stuck on the first frame, obviously not updating / refreshing / communicating with the plugin.  Apologies for being a noob - the stripped down code is below - I've commented where my main 'update' code is.

    > import c4d
    >
    > from c4d import gui, plugins, bitmaps
    >
    > import time, sys, os, subprocess
    >
    > import logging, logging.handlers
    >
    > import math, random
    >
    > from c4d.utils.noise import C4DNoise
    >
    > from c4d.utils import SinCos
    >
    > import c4d.utils
    >
    >
    >
    >
    >
    >
    >
    > __plugin_id__ = 1000002
    >
    > __version__ = "1.0"
    >
    > __plugin_title__ = "Vectorfield"
    >
    >
    >
    >
    > DUMMY = 1000
    >
    > BTN_ABOUT = 1001
    >
    >
    >
    >
    > global lastFrame
    >
    > doc = c4d.documents.GetActiveDocument()
    >
    > cF = doc.GetTime().GetFrame(doc.GetFps())
    >
    > lastFrame = cF
    >
    >
    >
    >
    > # String shown in status bar
    >
    > HELP_TEXT = "Vectorfield"
    >
    >
    >
    >
    > class MainDialog(gui.GeDialog) :
    >
    >
    >
    >
    >     def updateScene(self) :
    >
    >         
    >
    >         """
    >
    >
    >
    >
    >         MY CODE GENERATING NEW GEOMETRY EVERY FRAME HERE
    >
    >
    >
    >
    >         """
    >
    >
    >
    >
    >         return True
    >
    >
    >
    >
    >
    >
    >
    >     def InitValues(self) :
    >
    >
    >
    >
    >         return True
    >
    >
    >
    >
    >     def CreateLayout(self) :
    >
    >
    >
    >
    >
    >
    >
    >         self.SetTitle(__plugin_title__)
    >
    >
    >
    >
    >         # Create the menu
    >
    >         self.MenuFlushAll()
    >
    >
    >
    >
    >         # About/Help menu
    >
    >         self.MenuSubBegin("Vectorfield")
    >
    >         self.MenuAddString(BTN_ABOUT, "About")
    >
    >         #self.MenuAddSeparator()
    >
    >         self.MenuSubEnd()
    >
    >
    >
    >
    >         self.MenuFinished()
    >
    >
    >
    >
    >         return True
    >
    >
    >
    >
    >     def about_C4DPluginTemplate(self) :
    >
    >
    >
    >
    >         gui.MessageDialog("{0}\nblah blah\nblah blah\n\nblah blah\n".format(__plugin_title__), c4d.GEMB_OK)
    >
    >
    >
    >
    >     def Message(self, msg, result) :
    >
    >
    >
    >
    >         self.updateScene()
    >
    >
    >
    >
    >         return c4d.gui.GeDialog.Message(self, msg, result)
    >
    >
    >
    >
    >     def CoreMessage(self, id, msg) :
    >
    >
    >
    >
    >         return c4d.gui.GeDialog.CoreMessage(self, id, msg)
    >
    >
    >
    >
    >     def Command(self, id, msg) :
    >
    >
    >
    >
    >
    >
    >
    >         if id == BTN_ABOUT:
    >
    >             self.about_C4DPluginTemplate()
    >
    >
    >
    >
    >         return True
    >
    >
    >
    >
    >
    >
    >
    >
    >
    >
    > class Vectorfield(plugins.CommandData) :
    >
    >
    >
    >
    >     dialog = None
    >
    >
    >
    >
    >     def Execute(self,doc) :
    >
    >         if self.dialog is None:
    >
    >             self.dialog = MainDialog()
    >
    >
    >
    >
    >         return self.dialog.Open(dlgtype = c4d.DLG_TYPE_ASYNC,
    >
    >                                 pluginid = __plugin_id__,
    >
    >                                 defaultw = 300,
    >
    >                                 defaulth = 400)
    >
    >
    >
    >
    >     def RestoreLayout(self, sec_ref) :
    >
    >         if self.dialog is None:
    >
    >             self.dialog = MainDialog()
    >
    >
    >
    >
    >         return self.dialog.Restore(pluginid = __plugin_id__, secret = sec_ref)
    >
    >
    >
    >
    >
    >
    >
    >
    >
    >
    > if __name__ == "__main__":
    >
    >
    >
    >
    >     icon = bitmaps.BaseBitmap()
    >
    >     dir, file = os.path.split(__file__)
    >
    >     iconPath = os.path.join(dir, "res", "icon.tif")
    >
    >     icon.InitWith(iconPath)
    >
    >
    >
    >
    >     plugins.RegisterCommandPlugin(id = __plugin_id__,
    >
    >                                   str = __plugin_title__,
    >
    >                                   info = 0, 
    >
    >                                   help = HELP_TEXT,
    >
    >                                   dat = Vectorfield(),
    >
    >                                   icon = icon)



  • On 29/10/2014 at 01:24, xxxxxxxx wrote:

    Hello,

    you are changing the document from the Message function of a Dialog. A dialog is part of Cinema's GUI, not part of a document. So when you access GetActiveDocument() you get the document that is currently open in Cinema 4D.

    But when you render the document in the external renderer, Cinema will create a copy of your document and render that copy (so you can keep working on the active document). The dialog cannot know about this document copy so it can't change it.

    For what you are trying to to I suggest to create a ObjectData plugin that creates new objects in it's GetVirtualObjects() function.

    best wishes,
    Sebastian



  • On 29/10/2014 at 01:33, xxxxxxxx wrote:

    Yeah I kinda knew that was the problem - I just started with a basic plugin template I found.  Thanks for the advice, looking into this now,
    G.



  • On 29/10/2014 at 03:10, xxxxxxxx wrote:

    quick follow up question,

    I'm looking at a lot of example ObjectData python plugins, particularly the examples that come with the documentation.  But they all have .h and .res files.  I thought I could write python plugins without having to compile c++ files ?  Am I wrong on this ?
    Can I get away with writing plugins without needing .res files ?  Are there any examples / templates around like this?

    Thanks,
    G.



  • On 29/10/2014 at 03:46, xxxxxxxx wrote:

    Hello,

    The .h and .res files are used by both C++ plugins and Python plugins (but of course you don't have to compile Python plugins). .h files let you define constants in your plugin using an enumeration, .res files define parameters and dialogs. You may not use .h and .res files but then your plugin won't have parameters.

    For more information on this topics please take a look at:

    best wishes,
    Sebastian



  • On 29/10/2014 at 03:57, xxxxxxxx wrote:

    aaah I just realised that the .res files are just text files - I thought they were precompiled code.   That helps a lot 🙂

    Thanks again the help, much appreciated..
    G.



  • On 29/10/2014 at 06:24, xxxxxxxx wrote:

    Hi - I still need a bit of help.  I've worked out creating an ObjectData plugin - but not sure where to place my code within it.  I have a method which creates random geometry to the scene each frame 'updateScene' - you can see where I've put it below - but not sure where to call it from, and how it connects to getvirtualobjects to update.

    > import c4d
    >
    > from c4d import gui, plugins, bitmaps
    >
    > import time, sys, os, subprocess
    >
    > import logging, logging.handlers
    >
    > import math, random
    >
    > from c4d.utils.noise import C4DNoise
    >
    > from c4d.utils import SinCos
    >
    > import c4d.utils
    >
    >
    >
    >
    >
    >
    >
    > PLUGIN_ID = 100000
    >
    >
    >
    >
    > class Test(c4d.plugins.ObjectData) :
    >
    >
    >
    >
    >     def GetVirtualObjects(self, op, hierarchyhelp) :
    >
    >         dirty = op.CheckCache(hierarchyhelp) or op.IsDirty(c4d.DIRTY_DATA)
    >
    >         if dirty is False:
    >
    >             return op.GetCache(hierarchyhelp)
    >
    >
    >
    >
    >     def __init__(self) :
    >
    >         self.SetOptimizeCache(True)
    >
    >
    >
    >
    >
    >
    >
    >     def Execute(self, op, doc, bt, priority, flags) :
    >
    >         return True
    >
    >
    >
    >
    >     def Message(self, node, type, data) :
    >
    >
    >
    >
    >         return True
    >
    >
    >
    >
    >     def Draw(self, op, drawpass, bd, bh) :
    >
    >
    >
    >
    >         return c4d.DRAWRESULT_OK
    >
    >
    >
    >
    >     def updateScene(self) :
    >
    >        
    >
    >         """
    >
    >         CREATE RANDOM GEOMETRY EVERY FRAME
    >
    >
    >
    >
    >         """
    >
    >         return True
    >
    >
    >
    >
    >
    >
    >
    > if __name__ == '__main__':
    >
    >     #main()
    >
    >     bmp = c4d.bitmaps.BaseBitmap()
    >
    >     dir, file = os.path.split(__file__)
    >
    >     fn = os.path.join(dir, "res", "Icon.tif")
    >
    >     bmp.InitWith(fn)
    >
    >     result = plugins.RegisterObjectPlugin(
    >
    >         id=PLUGIN_ID,
    >
    >         str="Controller",
    >
    >         g=Test,
    >
    >         description="Otest",
    >
    >         info=c4d.OBJECT_GENERATOR,
    >
    >         icon=bmp
    >
    >     )



  • On 29/10/2014 at 06:45, xxxxxxxx wrote:

    p.s. my 'updateScene' function deletes all old objects and creates new objects every frame - so in every frame the scene is completely rebuilt - if that's of any help.



  • On 29/10/2014 at 09:52, xxxxxxxx wrote:

    Actually just discovered that a Python Generator will do pretty much everything I need.  Will pursue this and see what happens, seems much simpler than trying to write a full plugin.



  • On 29/10/2014 at 12:05, xxxxxxxx wrote:

    Might have to go back to the plugin route - having a problem with the python generator - any advice on this...

    Basically my script inside the generator creates geometry every frame - but also uses a CallCommand to make editable the objects.  For some reason the CallCommand only seems to work once when first executed, but not again thereafter.  Have no idea why!

    If I can get the generator to work, or understand how getvirtualobjects works in a plugin, i'll be happy..



  • On 29/10/2014 at 16:45, xxxxxxxx wrote:

    Okay - I've learned that SendModelingCommand is much better for me than CallCommand.  So things are going well..



  • On 29/10/2014 at 17:25, xxxxxxxx wrote:

    Ooh another little problem - I also need to use the 'Connect Objects' command - but it's not available with SendModellingCommand.  It can be called with CallCommand - but as I said - I can only get CallCommands to execute once when using within a generator.
    How would I go about merging lots of objects into a single mesh otherwise?
    Thanks..



  • On 30/10/2014 at 02:20, xxxxxxxx wrote:

    Hello,

    A generator works by creating objects and returning them in GetVirtualObjects (). To return multiple objects simple create a parent object and make the other objects children of that parent. These objects are not permanently added to the scene; for every call of GetVirtualObjects() all objects have to be re-created.

    Since GetVirtualObjects() works with virtual objects and is called in a thread context, CallCommand and SendModellingCommand may not work correctly or may not be available. The Python generator also creates it's geometry in a GetVirtualObjects() functions, so there in no difference between the generator and a plugin.

    For an example how to use GetVirtualObjects() and how to handle rough polygon data please take a look at the RoundedTube example. You may also have a look at the "Atom" example in the C++ SDK.

    best wishes,
    Sebastian



  • On 30/10/2014 at 02:39, xxxxxxxx wrote:

    Thanks very much Sebastian for the help as usual,
    Last night I managed to get GetVirtualObjects to create all my hierarchy of objects - no problem - but for example if I want the positions of the objects to randomise every frame when rendering - It's stuck on the first frame - so I need to know how the plugin can make a call every frame somewhere to update the positions of the objects.

    p.s. I discovered MCOMMAND_JOIN which will allow me to connect objects (not related to above problem)



  • On 30/10/2014 at 09:31, xxxxxxxx wrote:

    Hello,

    GetVirtualObjects() is called very often so there are mechanisms that make sure your generator only re-creates it's result when something (like a parameter) changed. You can use SetOptimizeCache() to let the plugin itself decide if the generator should generate new geometry.

    Another way is to manage it yourself with the "old" method of checking the cache and the dirty state. There you can also check if the frame changed (look at this example). If the frame changed, you can rebuild your geometry.

    best wishes,
    Sebastian



  • On 30/10/2014 at 10:44, xxxxxxxx wrote:

    Thanks for that, useful to know - at the minute though it looks like I can get a good old python tag to do everything I need.  Workflow is much simpler this way.
    Appreciate all the help on these forums,
    Here's what I'm working on if it's of interest - I'm a 'generative artist'..
    http://glennmarshall.wordpress.com/2014/10/23/python-cinema-4d/

    G.



  • On 31/10/2014 at 01:01, xxxxxxxx wrote:

    Hello Glenn,

    very impressive work. Just one thing: Cinema introduced Python support in Release 12 four years ago, not just "recently".

    For your purposes you may also take a look at the Python Mograph Effector.

    best wishes,
    Sebastian



  • On 31/10/2014 at 01:38, xxxxxxxx wrote:

    haha - I've been out of the '3d' game for too long - I'm just catching up with how everything has progressed!


Log in to reply