Properly cloning objects multiple times in GVO



  • On 12/03/2018 at 08:51, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R17 
    Platform:    Mac  ;  
    Language(s) :     C++  ;

    ---------
    Greetings to all!

    What is the recommended way to clone an input object multiple times inside GetVirtualObjects()?

    Should GetHierarchyClone() be used each and every time? Or is it better to use GetHierarchyClone() once, and then call GetClone() on the GHC clone for each clone you need after that?

    Also, I've noticed the SDK documents mention that generators with variable object output need to declare themselves as OBJECT_UNIQUEENUMERATION when registering and use SetUniqueIP() on the output objects. The example in the SDK docs simply shows some code applying a sequential IP to each clone returned from GetHierarchyClone(). Do these numbers need to change each time you rebuild the virtual object hierarchy? Or is it sufficient to simply count the number of clones generated, and assign the index number of each clone via SetUniqueIP()?

    Thanks!
    -CMPX



  • On 13/03/2018 at 03:37, xxxxxxxx wrote:

    Hi CMPX,

    in general it's recommended to get the first clone via GetHierarchyClone() (and alike) and then do further cloning via GetClone() of the first one.

    The idea of the unique IP is to make generated clones/objects identifiable over multiple frames (e.g. for motion blur). So actually these numbers should change, only, if the actual clone changed. Hm? Not sure, my explanation makes sense. Lets assume a simple cloner, creating clones with a certain lifespan. For example, it creates an additional clone per frame, each with a lifespan of two frames.

    Could look like so, with frame number at beginning of each line and list of IP following:

    #1 - 1          (created new clone with IP 1)
    #2 - 1 2        (added new clone with IP 2)
    #3 -   2 3      (clone with IP 1 died and a new one added with a _new_ IP 3)
    


  • On 13/03/2018 at 04:30, xxxxxxxx wrote:

    Thanks for the reply!

    I'm already creating my first clone using GetHierarchyClone(), and all subsequent clones using GetClone().

    How should I be managing the unique IPs for these? The manual states that I only need to set the IP for top level object returned from GetHierarchyClone(), but it doesn't say anything about the objects returned from GetClone().

    Do I need to recurse the entire object hierarchy provided by GetClone() and call SetUniqueIP() on each child with a unique integer? Or do I just need to assign a unique IP to each top level object returned from GetClone()?

    Likewise, what about custom objects allocated through BaseObject::Alloc()? Do those need their own IPs as well?

    Last but not least- is it sufficient to just keep around a member variable and increment that every time I need a new IP?

    Cheers,
    -CMPX



  • On 14/03/2018 at 07:22, xxxxxxxx wrote:

    Hi,

    I guess, the answer to all of your questions depends a bit on what you are actually trying to achieve, or what's the actual situation.

    If you need to assign unique IPs to children in your clones hierarchy basically depends on, if the renderer should be able to uniquely identify them from frame to frame independently from clone's root object. In the end the IP is a "string" of IDs through the hierarchy. So if a clone and its children can be viewed as an atomic unit, you probably won't need it.
    Same for allocated objects.

    If a simple counter in a member variable is sufficient, again, highly depends on your use-case. Usually it should be sufficient, but lets take a look at my example above:
    This would be wrong (from a renderer point of view, all clones on all frames would be independent) :

    #1 - 1          (created new clone with IP 1)
    #2 - 2 3        (using counter incremented on last frame)
    #3 -   4 5
    

    Similarly this would not work out as expected:

    #1 - 1          (created new clone with IP 1)
    #2 - 1 2        (using fresh counter on every frame)
    #3 -   1 2      (clones with IP 1 and 2 are not identical to those in the frame before)
    


  • On 14/03/2018 at 23:52, xxxxxxxx wrote:

    Right, that makes sense. So only "new" objects (or new clones) should receive different IPs.

    Last question- sorry if this isn't really the thread to post it in, but it's kinda all related to the plugin I'm trying to develop...

    Is it safe to modify and return an existing object cache within GetVirtualObjects()? It has occurred to me that there's no real reason to recreate the entire clone hierarchy from scratch every time something on the generator changes- for example, if I'm cloning objects in a grid pattern and I want to adjust the dimensions of the grid, then I don't see any reason why I should recreate all the clones- I could just use GetCache() to retrieve the existing hierarchy (if in fact it exists), iterate over that and update the clone positions using SetMl(), then return that same hierarchy from GetVirtualObjects(). Of course you'd still have to rebuild the entire hierarchy if any of the input objects change, but that's trivial to detect using GetHierarchyClone() with a dirty flag and/or CompareDependenceList().

    Is this safe to do?

    If it is, could I take that one step further and actually modify the existing cache (either adding or removing clones as needed) to further speed things up?

    Cheers,
    -CMPX



  • On 16/03/2018 at 09:51, xxxxxxxx wrote:

    Hi,

    my answer will be a bit fuzzy here. I see no actual reason, why it should not work to modify the cache directly. Searching our code base I found no evidence, this is actually done. So either there are reasons not to do it, which didn't occur to me yet, or nobody needed such an optimization yet.



  • On 18/03/2018 at 04:36, xxxxxxxx wrote:

    Thanks for all your help so far!

    I'm not sure if you can answer this, but I figured I might as well ask anyways. How does the Mograph Cloner object handle clones? What I've noticed is that performance is quite poor in GVO if you're returning a large number of objects (for example, a 40x40 object array). The actual cloning completes fairly quickly, but then C4D seems to hang up somewhere in the viewport code for a few seconds while it sorts everything out and draws it on screen.

    This was why I was asking about modifying existing caches- if you're just changing the dimension of the cloned array, then it seems to be far faster to modify the positions of the existing clones rather than creating the clones from scratch (which can cause the viewport to lag while it's processing the new clone hierarchy).

    The Mograph Cloner object doesn't seem to suffer from the same performance issue when changing the dimensions of a grid array, which made me think it was modifying the existing clone cache rather than recreating it from scratch... Is this how the Cloner object does it? Or is there some other optimizations at play which keep the viewport operating relatively smoothly?

    -CMPX



  • On 23/03/2018 at 09:10, xxxxxxxx wrote:

    Hi,

    the MoGraph Cloner is a totally different beast. Without going into too much detail (which I'm not allowed to), the MoGraph Cloner takes quite a few shortcuts only possible internally.
    Not the answer you wanted to hear, I know...



  • On 25/03/2018 at 02:00, xxxxxxxx wrote:

    Hey, no, that's fine. I was half wondering if it was using some sort of internal APIs to achieve that sort of performance, which I assumed you wouldn't be able to talk about. Thank you for confirming that's the case nonetheless, since that in itself is an adequate answer considering what I'm trying to do (it at least confirms that what I'm doing is probably as fast as I'm going to get this thing running).

    Anyways, thanks again for all your help here! I really do appreciate it.

    Cheers,
    -CMPX


Log in to reply