Objectdata plugin is not refreshed / updated



  • On 26/04/2013 at 08:01, xxxxxxxx wrote:

    This is my first try at a ObjectData plugin, after reading a lot of posts on this subject.

    It generates a simple sweepNURBS, the path spline should be a child of the plugin.
    The contour is created in the plugin.

    Everything seems ok the first time.
    Except, when I replace the path spline, the sweep NURBS it is not refreshed / updated?

    I added my own CheckDirty, but it is never called (no logging).

    What am I missing?
    Here is the complete code.

    import os
    import math
    import sys
    import c4d
      
    from c4d import plugins, utils, bitmaps, gui
      
    PLUGIN_ID = 10252501
      
    class SweepObj(plugins.ObjectData) :
     
        def __init__(self) :
            self.SetOptimizeCache(True)
      
        def CheckDirty(self, op, doc) :
            print "CheckDirty."
            op.SetDirty(c4d.DIRTYFLAGS_DATA)
            
        def GetVirtualObjects(self, op, hierarchyhelp) :
            print "GetVirtualObjects."
            
            # Disabled the following lines because cache flag was set
            # So the cache build is done before this method is called
            #dirty = op.CheckCache(hierarchyhelp) or op.IsDirty(c4d.DIRTY_DATA)
            #if dirty is False: return op.GetCache(hierarchyhelp)
      
            #Get the child / parent object (to use as the sweep spline)
            #Use GetClone so we aren't working on the object itself
            
            source = op.GetDown()               
            if (source is None) : return None
      
            #upsource = op.GetUp()                  
            #if (upsource is None) : return None
            
            source = op.GetDown().GetClone()         
            #source = op.GetUp().GetClone(c4d.COPYFLAGS_NO_HIERARCHY)  
            
            #Create a Circle Primitive to act as the sweep profile
            circle = c4d.BaseObject(c4d.Osplinecircle) 
            #Set the radius
            circle[c4d.PRIM_CIRCLE_RADIUS] = 10.0   
            #Create a new SweepNURBS
            sweep = c4d.BaseObject(c4d.Osweep)
      
            #Insert the source sweep spline under the SweepNURBS
            source.InsertUnder(sweep)
            #Insert the circle profile spline under the SweepNURBS
            circle.InsertUnder(sweep)
      
            #sweep.Message (c4d.MSG_UPDATE)    #update sweep in viewport  
            #c4d.EventAdd()
      
            #Return the SweepNURBS
            return sweep
      
    if __name__ == "__main__":
      
        pluginstr = "SweepObj v01"
        dir, file = os.path.split(__file__)
        icon = bitmaps.BaseBitmap()
        icon.InitWith(os.path.join(dir, "res", "sweepobj.tif"))
        okyn = plugins.RegisterObjectPlugin(id=PLUGIN_ID, str="SweepObj",
                                    g=SweepObj,
                                    description="SweepObj", icon=icon,
                                    info=c4d.OBJECT_GENERATOR)
        if (okyn) : 
            print pluginstr + " initialized."
        else: print "Error initializing " + pluginstr
    


  • On 26/04/2013 at 08:17, xxxxxxxx wrote:

    two things.

    1. checkdirty() is meant to be used by other objects/instances. view it as the counterpart
    implementation to the various isdirty() methods. it is mainly there if you have to implement
    some  custom caching. so that other objects know when they have to rebuild the cache of 
    your object.

    2. your example is not working because the path spline is not part of your plugin class
    instance, but just a GeListNode child. you would have either take another approach and
    make the path spline a member of your plugins bc (a link field for example) or you have to
    overwrite Message() and watch your objects direct children and flag your instance dirty
    if the path spline is dirty and/or has changed.

    edit : to be more correct making the path spline a member of you plugin class won't change
    anything, what i meant with  ' of your plugins bc', is adding a description element to your plugins
    description which is referencing the path spline. your plugin class is attached to a GelistNode
    to which again a BaseContainer is attached which contains your plugins settings. Any changes
    to that container will cause c4d to rebuild the cache for your instance.



  • On 26/04/2013 at 09:01, xxxxxxxx wrote:

    Ok, because the spline is not part of the plugin, it is not updated?

    The first time the plugin works, because I add a spline as child to the plugin?
    After that, changing the spline is not refreshed.
    However changing (e.g. from top to right) refreshed the plugin.

    I will try and use a link.

    Thanks for the support.



  • On 26/04/2013 at 09:29, xxxxxxxx wrote:

    hm,

    something i haven't thought of is that there is a special flag for your scenario. that your
    plugin is updated under certain conditions and under some not might be caused by your 
    current flag. so you might want to try the  _OBJECT_INPUT  _flag, which will kind of 
    automate this desired caching behaviour i guess.

    plugins.RegisterObjectPlugin(...
    `

    • Object Plugin Flags
      _<_t_<__<_t_>_<_<_t_>_lgroup> OBJECT_MODIFIER| Modifier object. Deforms the surrounding object. (E.g. bend.)
      ---|---
      OBJECT_HIERARCHYMODIFIER| Hierarchical modifier. Deforms the surrounding objects together with other instances in a hierarchy chain. Only the top-most instance of the plugin in a chain is called. (E.g. bones.)
      OBJECT_GENERATOR| Generator object. Produces a polygonal or spline representation on its own. (E.g. primitive cube.)
      OBJECT_INPUT| Used in combination with  OBJECT_GENERATOR. Specifies that the generator uses builds a polygon or spline, using its subobjects as input. (E.g. Sweep NURBS, Boolean.)
      OBJECT_PARTICLEMODIFIER| Particle modifier.
      OBJECT_ISSPLINE| The object is a spline.
      OBJECT_USECACHECOLOR| If this flag is specified, the generator object itself controls the objects' colors (the ones that determine the wireframe/shaded color). Normally these are automatically overwritten by the generator objects settings. E.g. if the instance object is set to green, automatically all of its cache objects get the green color. By setting this flag an instance object could individually color objects.
      OBJECT_CAMERADEPENDENT| Camera dependent.
      OBJECT_POINTOBJECT| Point Object.
      OBJECT_POLYGONOBJECT| Polygon object.
      OBJECT_NO_PLA| Objects derived from [PointObject](file:///E:/Misc/SDK%20Help/C4d%20SDK%20Documentation%20Python/help/modules/c4d/C4DAtom/GeListNode/BaseList2D/BaseObject/PointObject/index.html#c4d.PointObject) will not use auto-keyframing (e.g. Joints can contain points and PLA auto-keyframing is not useful for them)
      OBJECT_DONTFREECACHE| Objects' (generators) caches will not be be deleted - users must maintain caches themselves.
      OBJECT_CALL_ADDEXECUTION| Must be set to call [ObjectData.Execute()](file:///E:/Misc/SDK%20Help/C4d%20SDK%20Documentation%20Python/help/modules/c4d.plugins/BaseData/NodeData/ObjectData/index.html#ObjectData.Execute) in the priority pipeline specified by [ObjectData.AddToExecution()](file:///E:/Misc/SDK%20Help/C4d%20SDK%20Documentation%20Python/help/modules/c4d.plugins/BaseData/NodeData/ObjectData/index.html#ObjectData.AddToExecution)._<_t_>_
      `


  • On 26/04/2013 at 10:23, xxxxxxxx wrote:

    Ok, I changed it, but no success.

        okyn = plugins.RegisterObjectPlugin(id=PLUGIN_ID, str="SweepObj",
                                    g=SweepObj,
                                    description="SweepObj", icon=icon,
                                    info=c4d.OBJECT_GENERATOR | c4d.OBJECT_INPUT)
    


  • On 26/04/2013 at 11:07, xxxxxxxx wrote:

    Using a link, it works!
    Thanks.



  • On 26/04/2013 at 13:24, xxxxxxxx wrote:

    i am not sure why the flag is not working. please note, that it was only a guess, it is 
    not guaranteed that the flag is meant to add that special cache rebuilding behaviour.

    alternatively you could follow just the NodeData.Message() approach., if you want to 
    maintain the c4d workflow. simply store a reference to the path object in your class
    and each time Message() receives a change msgid, test if self.reference is node.GetDown()
    and flag your node dirty depending on the result.



  • On 26/04/2013 at 13:56, xxxxxxxx wrote:

    Thanks for all your help and I understand it was only a guess.

    I have to think and of course try out, your other option, the NodeData.message approach.

    I have read some other posts about other simular issues
    I now better understand and it might be an option, something ScottA posted:
    What about triggering a hidden UserData field in your code to update it instead?
    https://plugincafe.maxon.net/topic/6070/6249_getcontour-refresh-or-uncache-how&PID=26194#26194



  • On 26/04/2013 at 14:04, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    alternatively you could follow just the NodeData.Message() approach., if you want to 
    maintain the c4d workflow. simply store a reference to the path object in your class
    and each time Message() receives a change msgid, test if self.reference is node.GetDown()
    and flag your node dirty depending on the result.

    You mean:
    Listen to NodeData.Message( self , node , type , data )
    if self.reference = child object (node.getdown())
        set dirty plugin node

    Not quite sure about it, but I''ll give it a try.



  • On 26/04/2013 at 14:18, xxxxxxxx wrote:

    i think you mean == instead = and i am not sure about the rest of the line, but despite that
    i actually meant the is keyword.

    def __init__(self) :
        self.reference = None
      
    def Message(self, node, type, data) :
        if node.getdown() is not self.reference:
            self.reference = node.GetDown()
            node.SetDirty(0)
    

    practically you could check first if the msg id is the change msg and only then execute the rest.
    you would also have to check the case where the object is the same, but the bc has changed.
    for a parametric spline for example which is animated.



  • On 26/04/2013 at 14:48, xxxxxxxx wrote:

    It is working and more simple then we thought.
    All I did was remove the following lines:

    def __init__(self) :
            self.SetOptimizeCache(True)

    The downside is that Render Active View is not working anymore!
    More testing to do.

      
    import os
    import math
    import sys
    import c4d
      
    from c4d import plugins, utils, bitmaps, gui
      
    PLUGIN_ID = 10252501
      
    class SweepObj(plugins.ObjectData) :
     
        def GetVirtualObjects(self, op, hierarchyhelp) :
            # Disabled the following lines because cache flag was set
            # So the cache build is done before this method is called
            #dirty = op.CheckCache(hierarchyhelp) or op.IsDirty(c4d.DIRTY_DATA)
            #if dirty is False: return op.GetCache(hierarchyhelp)
      
            #Get the child / parent object (to use as the sweep spline)
            #Use GetClone so we aren't working on the object itself
            
            source = op.GetDown()               
            if (source is None) : return None
            source = op.GetDown().GetClone()         
            
            #Create a Circle Primitive to act as the sweep profile
            circle = c4d.BaseObject(c4d.Osplinecircle) 
            #Set the radius
            circle[c4d.PRIM_CIRCLE_RADIUS] = 10.0   
            #Create a new SweepNURBS
            sweep = c4d.BaseObject(c4d.Osweep)
      
            #Insert the source sweep spline under the SweepNURBS
            source.InsertUnder(sweep)
            #Insert the circle profile spline under the SweepNURBS
            circle.InsertUnder(sweep)
      
            sweep.Message (c4d.MSG_UPDATE)    #update sweep in viewport  
            c4d.EventAdd()
      
            #Return the SweepNURBS
            return sweep
      
    if __name__ == "__main__":
      
        pluginstr = "SweepObj TEST v01"
        dir, file = os.path.split(__file__)
        icon = bitmaps.BaseBitmap()
        icon.InitWith(os.path.join(dir, "res", "sweepobj.tif"))
        okyn = plugins.RegisterObjectPlugin(id=PLUGIN_ID, str="SweepObj",
                                    g=SweepObj,
                                    description="SweepObj", icon=icon,
                                    info=c4d.OBJECT_GENERATOR | c4d.OBJECT_INPUT)
        if (okyn) : 
            print pluginstr + " initialized."
        else: print "Error initializing " + pluginstr
    


  • On 26/04/2013 at 14:55, xxxxxxxx wrote:

    because you have disabled caching with that ;) if another node is polling your node the node
    will always recalculate its output instead of returning its cache when the underlying data
    has not changed.



  • On 26/04/2013 at 15:06, xxxxxxxx wrote:

    Yes, the manual tells us:
    The method GetVirtualObjects() is called on each frame and if you don't want to calculate the object on each frame again and again you can still return the cache object with the following code:

    I tried your option, but without any testing.
    That did not solve it. Is 0 the correct flag value  to set?
    How and when is SetDirty used by Cinema 4d?

    def Message(self, node, type, data) :
    
        node.SetDirty(0)
    
        return True
    


  • On 26/04/2013 at 15:57, xxxxxxxx wrote:

    hi,

    i didn't actually test my posted code and assumed that a node is notified each time 
    one of its children changes, however that is not the case (at least not always, 
    messages tend to be confusing ). long story short here is a working version, i have 
    actually tested this time ;) it returns a Null when the object has no child and then 
    alternating a sphere or cube each time the cache is rebuild.

    the cache is rebuild when either the direct child changes or the direct child is dirty.

    import c4d, os, sys
    from c4d import bitmaps, documents, gui, modules,  plugins
      
    class fhOdataTestData(plugins.ObjectData) :
        def __init__(self) :
            self.reference = None
            self.toggle    = False
      
        def GetVirtualObjects(self, node, hierarchyhelp) :
            if node.GetDown() != self.reference:
                self.reference = node.GetDown()
                node.SetDirty(c4d.DIRTY_DATA)
            elif node.GetDown() and node.GetDown().IsDirty(c4d.DIRTY_DATA) :
                node.SetDirty(c4d.DIRTY_DATA)
      
            if not (node.CheckCache(hierarchyhelp) or node.IsDirty(c4d.DIRTY_DATA)) :
                return node.GetCache(hierarchyhelp)
      
            if self.reference:
                if self.toggle:
                    res = c4d.BaseObject(c4d.Ocube)
                else:
                    res = c4d.BaseObject(c4d.Osphere)
                self.toggle = not self.toggle
                return res
            else:
                return None
      
    if __name__ == "__main__":
        path, file = os.path.split(__file__)
        bmp = bitmaps.BaseBitmap()
        bmp.InitWith(os.path.join(path, "res", "fhOdataTest.tif"))
        plugins.RegisterObjectPlugin(id          = 1029736, 
                                     str         = "odatatest",
                                     g           = fhOdataTestData,
                                     description = "odatatest", 
                                     icon        = bmp,
                                     info        = c4d.OBJECT_GENERATOR)
    


  • On 27/04/2013 at 02:22, xxxxxxxx wrote:

    Briljant, thank you very much!
    It works as hoped.


Log in to reply