Your browser does not seem to support JavaScript. As a result, your viewing experience will be diminished, and you have been placed in read-only mode.
Please download a browser that supports JavaScript, or enable it if it's disabled (i.e. NoScript).
On 05/05/2018 at 06:46, xxxxxxxx wrote:
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
On 07/05/2018 at 01:58, xxxxxxxx wrote:
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 xxxxxxxx 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?
Originally posted by xxxxxxxx
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?
On 07/05/2018 at 02:07, xxxxxxxx wrote:
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,
On 07/05/2018 at 02:35, xxxxxxxx wrote:
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
On 07/05/2018 at 03:36, xxxxxxxx wrote:
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
On 07/05/2018 at 09:14, xxxxxxxx wrote:
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
Thanks in advance, Niklas