Python | Force obj to calc b4 another obj



  • On 05/05/2013 at 23:44, xxxxxxxx wrote:

    Hello programming brethren,

    I have an object data plugin that reads data off of other object data plugins to do calculations on itself.  The problem is, it seems that when the other objects are in motion, it highlights how the main object is doing calculations on itself before getting the correctly updated data from the other objects.  So, the net effect is that it is a very glitchy result as sometimes their calculations come in before or sometimes after.  And when I scrub the timeline, the calculations are always a frame off until I let go of the scrubbing and they update and "pop" into place.

    SO.  How can I force the other objectdata plugin objects to do all their computations first before my main plug-in moves itself based on those numbers? Instead of doing calculations that are sometimes based off of how they were a frame ago?

    Is there a priority queue?



  • On 06/05/2013 at 10:34, xxxxxxxx wrote:

    Maybe a better question is:

    What is the best way to pass data from one objectdata plugin to another?

    Right now I am creating hidden descriptions that don't show in the user interface that I store data in for the other plugin to read from.  But it doesn't seem ideal.  And it may be why I am at the mercy of the timing of it to update.

    Is there a way to read instance variables straight from the other plugin or a way to call a method on another plugin to get a result from it in the order I want?



  • On 06/05/2013 at 11:12, xxxxxxxx wrote:

    You can use Cinema's messaging interface. You can pass almost any python object via the
    message system and react on that in your ObjectData.

    MSG_RETRIEVE_OBJECTDATA_INSTANCE = # Your UNIQUE ID from the plugincafe.
      
    class MyObjectPlugin(c4d.plugins.ObjectData) :
      
        def __init__(self) :
            super(MyObjectPlugin, self).__init__()
            self.internal_data = ['super', 'internal', 42, 'data']
      
        def Message(self, op, id, data) :
            if id == MSG_RETRIEVE_OBJECTDATA_INSTANCE:
                data['obj'] = self
                return True
      
            # ... other message handling
      
    def in_some_arbitrary_context(op) :
        if op.CheckType(Omyobjectplugin) :
            data = {'obj': None}
            op.Message(MSG_RETRIEVE_OBJECTDATA_INSTANCE, data)
            if data['obj']:
                print obj.internal_data
    

    But be aware of the fact that only "non-native" types can be passed to the second argument of
    Message(). If you pass an integer instead of the dictionary (just for instance), you will get the
    message in the ObjectData plugin, but the "data" parameter will have a None value instead of
    the integer.

    Best,
    -Niklas



  • On 06/05/2013 at 11:19, xxxxxxxx wrote:

    Thanks a ton for this, Niklas.  In your opinion if I pass data this way that I may not have the time lag that I'm having now having it read hidden descriptions?  Meaning in this method, is it causing the other object to calculate in advance or am I still at mercy to when the data in my other plugin is updated for that frame?



  • On 06/05/2013 at 11:27, xxxxxxxx wrote:

    hi,

    i can only answer partially:

    1. Communication between two nodes can be a difficult task in c4d. Hidden descriptions are one 
    way to do it. For more complex data sets you have to use messages directly from node to node 
    or with a core message plugin in the middle. Also there is GetListNode.GetNodeData()which is 
    not documented for python for whatever reason, which lets you grab the your NodeData plugin 
    class implementation instance attached to a GeListNode and therefore would let you access all 
    its members.

    2. For the update cycle - It is a quite complicated topic I also do not fully understand and have 
    not fully explored yet. There are several methods like BaseObject.Touch() and BaseObject.
    SetDirty()which allow you to force an object to rebuild its cache. This however won't solve your
    problem of priority directly.
    But as all NodeData direct 'execution' methods are threaded it should  be theoretically possible 
    for your ObjectData plugin to wait with its output until all data it is based on have been updated
    for the current pass (are not dirty anymore). But that is pure theory as I said, I could always get 
    away with tag plugins until now, which do offer a manual priority system (the tag execution order).



  • On 06/05/2013 at 12:37, xxxxxxxx wrote:

    Thankyou for that, Ferdinand (did I get your name correct?),

    For GetNodeData(), I can ask for this and it returns what?  a Dict with all the Ivars or method names? Or basically pass the "op"?  So can I call functions on another plugin with this? If so, it would force the order perfectly as it would make it run the function and get the result before moving on which would be perfect.



  • On 06/05/2013 at 13:00, xxxxxxxx wrote:

    Hi Chris,

    no this will not change the order of execution. But it allows you to change any data (such as
    the values of instance variables that are not shown in the description of an object). You have to
    get the priorities right. Cinema executes generators sequentially, so if you want the one generator
    to be finished before the other, you have to put the one in a higher position than the other.

    Another way would be to calculate the data in an Execute() pass and retrieve this information
    in GetVirtualObjects(). But I think this would disallow you to use objects as input (children, like
    the HyperNURBS for example uses it's child-object as input). If this is a requirement, it will probably
    not be an option.

    Best,
    -Niklas



  • On 06/05/2013 at 13:09, xxxxxxxx wrote:

    hi,

    yes my name is Ferdinand. GetNodeData() returns the NodeData implentation attached to a 
    GeListNode. If you have an ObjectData plugin which implements something like that :

    ObjectDataData(plugins.ObjectData) :
    	def __init__(self) :
    		self.someFancyList = []
    	[...]
    	def GetVirtualObjects(self, node, hh) :
    	[...]
    

    it will return an instance of ObjectDataData attached to that GeListNode so that you acccess
    ObjectDataData.someFancyList attached to that GeListNode. For native c4d objects (that are 
    not implemented as a plugin) it will return None. You can try it from the console, simply drag a 
    plugin node into it and put a .GetNodedata() behind it.



  • On 06/05/2013 at 13:14, xxxxxxxx wrote:

    Ferdinand, I don't have a Python Object Plugin to hand, but using GetNodeData() on a non-Python
    plugin object doesn't work (it returns None). I didn't test it for Python object plugins, but I assume it
    only works for those (would be pretty much senseless if it would always return None).

    Anyway, good catch that this method is actually available, didn't see it until now.

    Best,
    -Niklas



  • On 06/05/2013 at 13:19, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    You have to get the priorities right. Cinema executes generators sequentially ...

    are you sure about that ? c4d obviously passes the objects sequentially, or in other
    words works its way down the object manager, but as i already wrote above, as
    BaseObject.GVO(), BaseTag.Execute() and so on are all threaded, should it not be 
    possible to 'wait' for other objects to be updated ?

    edit: is does work, i am using it. not sure why it is not documented. as i am exclusively
    python right now, except from our polylight cpp excursion, i haven't bothered testing it
    cross language yet, but you are right it does not work (which is not really surprising IMHO).

    [OK] fhArraySampleObject.GetNodeData()
    <__main__.fhArraySampleObjectData object at 0x000000000FA193F0>
    [OK] hook = fhVertexmapShade.GetNodeData()
    [OK] dir(hook)
    ['Draw', 'Execute', 'FakeMaterial', 'GetDEnabling', 'Init', 'InitAttr', 'IsInit', 'IsInstanceOf', 'IsRenderDirty', 'Message', 'Sampler', 'ShaderContainerCache', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'initHost', 'isDirty']
    [OK] print hook.IsRenderDirty
    False
    

    edit2: first one is an ObjectData, second one is a TagData. i did extend the example a bit,
    mainly for the op.



  • On 06/05/2013 at 13:43, xxxxxxxx wrote:

    This is all pure gold.  Thank you both.  I appreciate both of your assistance greatly.  I'll reply with any succsesses.



  • On 06/05/2013 at 13:56, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Originally posted by xxxxxxxx

    You have to get the priorities right. Cinema executes generators sequentially ...

    are you sure about that ? c4d obviously passes the objects sequentially, or in other
    words works its way down the object manager, but as i already wrote above, as
    BaseObject.GVO(), BaseTag.Execute() and so on are all threaded, should it not be 
    possible to 'wait' for other objects to be updated ?

    I'm also thinking about this. I'm not 100% sure about this, but it would be the only explanation
    to the behavior I have experienced so far.

    Even if it would be possible for you to wait until another object has been updated (assuming
    it is being updated from another thread), you would block the thread of your object. 🙂

    And to extend your example a bit more :)

    [OK] print Cube.GetNodeData()
    None
    

    Best,
    -Niklas



  • On 06/05/2013 at 17:17, xxxxxxxx wrote:

    I AM BLOWN AWAY

    Works flawlessly.  Instead of storing my variable as a hidden description, I wrote a "getter" method on my other plugins.  On my main plugin when I needed the data I asked for the NodeData and called that function that did the calculation on the other plugin on demand and all of the frame lag and glitching is completely gone.  Works FLAWLESSLY.

    I am blown away it works so well.  I'm also excited as I've had other "glitchy" bugs all related to passing values through Descriptions.  And when I switch them to "getter" methods through NodeData, I'm confident it will solve those as well.

    Again, thank you both for this help.  Priceless.



  • On 06/05/2013 at 17:46, xxxxxxxx wrote:

    ^Can you please post an example how you are getting your other plugin?

    To grab tag plugins I use something like this: tag = obj.GetTag(TAG_PLUGIN_ID);
    I don't make ObjectData plugins very often. So I'm wondering if grabbing an ObjectData plugin is done differently than getting a tag plugin from within another plugin?

    -ScottA



  • On 06/05/2013 at 17:58, xxxxxxxx wrote:

    Sure.  This isn't from my plug but a simple example of what I just did:

    My Camera Waypoints are their own object and they are created by my main plugin.  As they are created they are populated into an incl/exl list on the main plugin.  Each Waypoint has a target position in space based on their Z direction * a depth parameter.  I was calculating this global position for depth on the Waypoint and setting a hidden description with this value which I would read on the main plugin.  However since the 2 objects are in different threads, this was often calculated AFTER the main plugin causing lag and glitching.  SO, on the main plugin instead of getting the waypoint as an object from the list and doing a :

    tpos = cam[c4d.TARGETPOS]
    

    I moved my code on the waypoint into a functioncalled "getTPos()" that gathers all the info needed there and returned the value from the method itself.  SO, on my main plugin I did:

    camdata = cam.GetNodeData()
    tpos = camdata.getTPos()
    

    Which forced the waypoint to collect all the data that moment before my main plug carried on further.  Insuring the data would always be calculated when asked.  Before when it was calculated on it's own in it's own thread and placed in a description it was usually behind or on occasion ahead which create a glitch or lag.



  • On 06/05/2013 at 18:03, xxxxxxxx wrote:

    Looking at your question again, Scott, you may have been asking how to reference your other plugin as opposed to the GetNodeData part.  Mine are always in a exl list so I just loop through that getting data on each.  Although I suppose you can just do the usual way of referencing other objects but for type you use your plugin ID.  Not sure if that's what you mean.



  • On 06/05/2013 at 18:33, xxxxxxxx wrote:

    Yeah.
    I meant getting an ObjectData plugin from inside a different plugin. So I could then get at all of the ObjectData's members.
    It's handy to be able to have two plugins talk to each other internally like that.
    I've done this with tag and Gedialog plugins. But not ObjectData plugins yet.

    I thought that's what you were dong. But I guess not.

    Thanks anyway.
    -ScottA


Log in to reply