Plugin Cafe Homepage
Forum Home Forum Home > Plugin Cafe > SDK Help
  New Posts New Posts
  FAQ FAQ  Forum Search

Touch(), cache handling and dirty counts

Author
Message
  Topic Search Topic Search
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Topic: Touch(), cache handling and dirty counts
    Posted: 2018 May 05 at 6:46am

User Information:

Cinema 4D Version:   R19 
Platform:   Windows  ;   
Language(s):     C++  ;   

---------

I am having trouble to determine when an input object of my generator changed compared to the
last time that I handled and generated new geometry for it.

When the generator is first invoked, there's no cached state and thus it generates new geometry
for every input object. It saves the dirty count of the input object (DIRTYFLAGS_CACHE | DIRTYFLAGS_DATA)
in a hashmap. After the geometry was generated from the PolygonObject's found in the input object's
cache, it calls Touch() on the object.

However, Touch() increases the DIRTYFLAGS_CACHE dirty count (eg. for Array and Cloner objects,
not for Cube primitives for some reason).

Now the dirty count that I stored for the object is incorrect as it has been modified by Touch().
I need to call Touch() after I computed the geometry however because Touch() will free the cache
of the input object, making it inaccessible for computing the new geometry.

Do I need to compute the dirty count a second time after calling Touch() and store that dirty count
instead?



Now assume the above works properly and I determine that I don't need to recompute geometry
from the input object. I simply call Touch() again in order to hide the input object in the viewport
and reuse the previously generated cache.

Does that mean that the input object's GetVirtualObjects() was called but the result is completely
ignored and deleted again by my Touch() call? Wouldn't that be super unnecessary?

Also, that Touch() call would again increase the dirty count.


Are there some resources that explain how to manage this properly?


On a side-note, I can't use the BaseObject DependenceList stuff because I want to treat all of the
inputs separately. So if one input object changed, I may still re-use the previously generated cache
of another input object.


For the sake of completeness, in case it is of any relevance, this is how I retrieve all the input
objects to my generator:


  inline void CollectGenerators(BaseObject* obj, maxon::BaseArray<BaseObject*>& objects) {
    if (obj->GetDeformCache() || obj->GetCache() || obj->GetType() == Opolygon) {
      objects.Append(obj);
    }
    else {
      for (auto&& child : Utils::IterChildren(obj)) {
        if (!child->GetBit(BIT_CONTROLOBJECT)) {
          CollectGenerators(child, objects);
        }
      }
    }
  }

    // Then in GetVirtualObjects():
    maxon::BaseArray<BaseObject*> generators;
    for (auto&& child : Utils::IterChildren(op)) {
      Utils::CollectGenerators(child, generators);
    }

     for (auto&& generator : generators) {
         // TODO: Determine if the cache of the generator has changed -> Recompute the geometry only in that case

         maxon::BaseArray<PolygonObject*> geos;
         // Here I collect all PolygonObjects using the method described in the
         // BaseObject::GetCache() documentation (checking for BIT_CONTROLOBJECT). 
  
         // Build new geometry from PolygonObjects

         generator->Touch();
     }



Thanks in advance,

Niklas


Edited by NiklasR - 2018 May 07 at 2:10am
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2018 May 07 at 1:58am
Small update: I noticed that calling Touch() on the input object will cause it to NOT generate a new
cache the next time GVO is called. This can be tested easily with a Python Generator:

import c4d
def main():
    array = op.GetDown()
    print(array, array.GetCache())
    array.Touch()
    return c4d.BaseObject(c4d.Ocube)



(<c4d.BaseObject object called 'Array/Array' with ID 5150 at 0x000001F73CB71C50>, <c4d.BaseObject object called 'Array/Null' with ID 5140 at 0x000001F73CB71AD0>)
(<c4d.BaseObject object called 'Array/Array' with ID 5150 at 0x000001F73CB71F50>, None)
(<c4d.BaseObject object called 'Array/Array' with ID 5150 at 0x000001F73CB71AD0>, None)


This resolves one of my previous questions:

Originally posted by NiklasR

Does that mean that the input object's GetVirtualObjects() was called but the result is completely
ignored and deleted again by my Touch() call? Wouldn't that be super unnecessary?


However, what if I actually need the cache but the input object has none because I called Touch()
on it in the previous GVO() pass?


Edited by NiklasR - 2018 May 07 at 1:59am
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2018 May 07 at 2:07am
Another test from the C++ plugin

      auto c = op->GetDown();
      if (c) {
        GePrint(c->GetName() + ": Has Cache? " + String(c->GetCache() ? "yes" : "no"));
        auto cc = c->GetDown();
        if (cc) GePrint("    " + cc->GetName() + ", BIT_CONTROLOBJECT? " + String::IntToString(cc->GetBit(BIT_CONTROLOBJECT)));
        c->Touch();
      }
      return BaseObject::Alloc(Onull);

After I move the Array object under the ObjectData plugin, I get

Array: Has Cache? yes
    Car, BIT_CONTROLOBJECT? 0
Array: Has Cache? no
    Car, BIT_CONTROLOBJECT? 0
Array: Has Cache? no
    Car, BIT_CONTROLOBJECT? 0
Array: Has Cache? no
    Car, BIT_CONTROLOBJECT? 0
Array: Has Cache? no
    Car, BIT_CONTROLOBJECT? 0
Array: Has Cache? no
    Car, BIT_CONTROLOBJECT? 0
Array: Has Cache? no
    Car, BIT_CONTROLOBJECT? 0

Which is the same behaviour as in the Pyhon Generator. I just wanted to make sure that it's not
some kind of issue with the Python Generator, so I tested it in the C++ plugin, too.

Shouldn't the BIT_CONTROLOBJECT be set on the "Car" polygon object? How else would I know
that the object is already being used by the Array object?

Thanks,

Niklas


Edited by NiklasR - 2018 May 07 at 2:08am
Back to Top
knickknack View Drop Down
Forum Moderator
Forum Moderator


Joined: 2016 Jul 01
Location: Italy
Online Status: Offline
Posts: 268
Direct Link To This Post Posted: 2018 May 07 at 2:35am
Hi Niklas, thanks for writing us.

There's a in-depth description on the topic in the BaseObject manual at this link which I think it could be worth reading with regard on how/when to use BaseObject::Touch() .

Let me know if any further help is needed.

Best, Riccardo
MAXON Computer GmbH
SDK Support Team
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2018 May 07 at 3:36am
Thanks Riccardo, that looks like what I was searching for. My bad for not finding it. :-)
I'll give all the info in that page a go and come back with any unanswered questions.

Cheers,
Niklas
Back to Top
NiklasR View Drop Down
Member
Member


Joined: 2010 Dec 13
Location: Germany
Online Status: Offline
Posts: 2575
Direct Link To This Post Posted: 2018 May 07 at 9:14am
Hello Riccardo,

Unfortunately I am not getting very far. I fear my case is a little more complex than what is usually
implemented for generator plugins.
  • GetAndCheckHierarchyClone() seems to always return a clone and tell me that the object is
    dirty. Not very helpful unfortunately. Also, I would like to avoid the "cloning" part and read from
    the cache directly as it will be much more efficient.

  • Using IsDirty() seems to be very useful and does not require me to store dirty counts, great!
    But can I use it for any objects that are not direct children of my generator?

    For example, the Array object only takes its first child as its input. Any other of its child
    objects are ignored and left visible in the Viewport. I want to have my generator take this
    object into account instead (similar to how the Subdivision Surface can handle this case, see
    the image below)

    Also, there appear to be cases where IsDirty() returns true but the object has no cache
    that I can read from. I described this situation before when I wasn't using IsDirty(). How
    would I be able to access the cache when the generator is dirty but it doesn't have a cache?


I noticed that the Subdivision Surface seems to be capable of handling the input objects similar to
the way that I would like to handle them. It sees the objects generated by the array, but also the
second child of the array that is not taken into account by the Array object itself.

Any chance to get some insight on how the Subdivision Surface does all of this? Specifically

  • How does it find all the PolygonObjects to take into account from its child objects?
  • How and when does it call Touch() on these input objects?
  • How does it check if an input object changed?



Thanks in advance,

Niklas



Edited by NiklasR - 2018 May 07 at 9:15am
Back to Top

Forum Jump Forum Permissions View Drop Down

Bulletin Board Software by Web Wiz Forums® version 9.61 [Free Express Edition]
Copyright ©2001-2009 Web Wiz

This page was generated in 0.109 seconds.