Fast access to geometry objects
-
On 15/12/2015 at 05:45, xxxxxxxx wrote:
User Information:
Cinema 4D Version: R17
Platform: Windows ;
Language(s) : C++ ;---------
Hello, dear PluginCafe!1. Is it possible to access VolumeData data (I need to read RayObjects and their instances and polygon objects with quads, normals, tags, etc) outside of VideoPost plugin type? I also don't need acceleration structures built by Cinema's renderers for RayObjects.
What I want to do is to hit a button in Cinema UI and get all this VolumeData in this convenient way in my custom procedure.
Is there any way to construct VolumeData for document retrieved with GetActiveDocument()?2. BaseObject->isDirty flag determines whether the object is modified since last time it was touched.
How long this value is valuable? When is it updated to false in the Cinema pipeline?
What I want to do is to check the value of VolumeData.RayObject.link->isDirty and decide whether to update my polygons based on new polygons in RayObject or not .Thanks in advance!
-
On 16/12/2015 at 07:25, xxxxxxxx wrote:
Hi and welcome to the Plugin Cafe!
1. VolumeData is only meant to be used in the rendering process from video post, material or shader plugins.
So I'm afraid there's no way to retrieve the VolumeData for a document outside of this context.
See this short thread for more information on RayObjects computation.2. BaseObject::IsDirty() should only be used from within generator object plugins i.e. ObjectData::GetVirtualObjects(). The returned dirty flag may be reset before you check it.
From any other place call BaseList2D::GetDirty() to get the dirty checksum for an object. Compare the returned value with the last stored value to check for changes.
This method is overloaded by many entities (point/polygon objects, materials, shaders, containers, tags, etc.) to support more changes. -
On 16/12/2015 at 10:42, xxxxxxxx wrote:
Hi Yannick,
Thanks for your reply! I become your client for a long time. So, be patient, please!
I have understood about VolumeData. Well, first I have been thinking that it is just a wrapper that simplifies more complex way of Cinema scene traversal. And it also uses data copy of BaseDocument which eats 2x memory. I needed VolumeData to shroten my way of understanding how to get access to the information from scene about unique meshes and their instances based on matrices. Would be glad to find some tutorial explaining the concepts cause the SDK documentation from the first days is more or less a list of class methods descriptions with class inheritance relations without actual info on how these guys communicate with each other.
First of all I need some tutorial with info how to get all geometry meshes like polygonal or if not then converting them to polys. And then how to get the instances. I have seen the function IsInstanceOf(). But it relates to types and used before casting and it is a bit confusing with some object instancing for now.Thanks for info about GetDirty! It is even better than I wanted. How does it work? Is it a hash sum of vertex and other data coordinates for the entire BaseObject (or whatever) ? Or is it just a counter of performed modifications on it? I am just interested whether it is expensive call or not.
-
On 17/12/2015 at 08:48, xxxxxxxx wrote:
Hi,
Originally posted by xxxxxxxx
I have understood about VolumeData. Well, first I have been thinking that it is just a wrapper that simplifies more complex way of Cinema scene traversal. And it also uses data copy of BaseDocument which eats 2x memory.
You can use the flag RENDERFLAGS_NODOCUMENTCLONE to avoid cloning the active document if you run the render by yourself with RenderDocument().
Originally posted by xxxxxxxx
I needed VolumeData to shorten my way of understanding how to get access to the information from scene about unique meshes and their instances based on matrices.
1. You can retrieve this information from a VolumeData in a VideoPostData at render time.
2. If you're interested in Instance Objects you can do that outside a VideoPostData but without the VolumeData information.Originally posted by xxxxxxxx
First of all I need some tutorial with info how to get all geometry meshes like polygonal or if not then converting them to polys.
1. To get geometry information at render time use vps->vd->GetObj(). This returns a RayObject with all the geometry and render information for polygonal object in the scene.
If an object is already a Polygon Object then just cast the object to a PolygonObject*.
To convert a primitive to a polygon mesh call BaseObject::GetCache(). This returns a PolygonObject* if the object actually holds polygon information.Originally posted by xxxxxxxx
And then how to get the instances. I have seen the function IsInstanceOf(). But it relates to types and used before casting and it is a bit confusing with some object instancing for now.
Don't be confused by the name of this function. C4DAtom::IsInstanceOf() just checks if a Cinema 4D atom object is an instance of a certain type. It isn't related to geometry instancing.
1. To access geometry instances at render time use vps->vd->GetObj()->instance. If it's not nullptr then the related is a render instance and points to a RayObjectInstanceData*.
2. If you're interested in Instance Objects check for their type and retrieve the instanced objects from the Reference Object parameter.
Originally posted by xxxxxxxx
Thanks for info about GetDirty! It is even better than I wanted. How does it work? Is it a hash sum of vertex and other data coordinates for the entire BaseObject (or whatever) ? Or is it just a counter of performed modifications on it? I am just interested whether it is expensive call or not.
GetDirty() basically returns a sum counter of modifications. It's not an expensive operation. It only performs flags checks and sum additions.
Originally posted by xxxxxxxx
Would be glad to find some tutorial explaining the concepts cause the SDK documentation from the first days is more or less a list of class methods descriptions with class inheritance relations without actual info on how these guys communicate with each other.
We know the C++ SDK documentation lacks lot of information and we're working on it. If you the search the Plugin Cafe with the name of API classes and functions I'm sure you'll find valuable information.
-
On 18/12/2015 at 12:35, xxxxxxxx wrote:
Hi Yannick!_<_o:_<_o:p_>_o:p>
I have digged out the class Hierarchy which seems to make what I wanted from Volu_<_o:_<_o:p_>_
And it seems that I can use the methods of class Hierarchy such as Do and Run to enumerate all the BaseObjects in sele_<_o:_<_o:p_>_cument.
The documentation _<_o:_<_o:p_>_at Run method:Performs `Do()`[URL-REMOVED] on all objects (virtual and non-virtual) in the hierarchy. Using this class will build all caches for dirty objects for the entire hierarchy, this can be time intensive and should be used carefully. If a polygon object is needed it is generally faster to call `SendModelingCommand()`[URL-REMOVED] with`MCOMMAND_CURRENTSTATETOOBJECT`[URL-REMOVED].
What are the pitfalls there? May I call the Run() method for any document (like GetActiveDocument() ) and do my things at any time? Do I need to copy the document to prevent user changes to the currently edited document by user? How to synchronize user edits and potential automatic creation of c_ <_o:_<_o:p_>_hen I call Run() as described here?
If I do need to make a copy of document for the reasons suggested_<_o:_<_o:p_>_oes the GetDirty() method remain valuable?
Do _<_o:_<_o:p_>_to call SendModelingCommand() right before Run()?
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
On 22/12/2015 at 03:20, xxxxxxxx wrote:
Hi,
Originally posted by xxxxxxxx
May I call the Run() method for any document (like GetActiveDocument() ) and do my things at any time? Do I need to copy the document to prevent user changes to the currently edited document by user? How to synchronize user edits and potential automatic creation of caches when I call Run() as described here?
What do you mean by "do my things at any time"? What's the type of your plugin and in what method are you using a Hierarchy::Run()?
Originally posted by xxxxxxxx
Do I have to call SendModelingCommand() right before Run()?
If you use Hierarchy::Run() you don't have to SendModelingCommand(MCOMMAND_CURRENTSTATETOOBJECT) because the caches for all dirty objects will be built.
-
On 22/12/2015 at 04:00, xxxxxxxx wrote:
This is supposed to be used for interactive renderer that is working in a thread parallel to the main Cinema 4D thread.
I plan to call Hierarchy::Run() using MessageData class for timer callback every 1 or 2 seconds.
And inside Hierarchy::Do() (which is called for every object) I plan to check if the BaseObject has changed using GetDirty() infromation. If the object has changed then geometry will be loaded to the renderer and updated accordingly.I just want to avoid unnecessary re-polygonization of the scene in this process based on Hierarchy::Run() that I plan to implement. For example, if some object with curved surfaces has created the polygon caches once and then it wasn't changed by user then I don't want to trigger the polygon cache rebuild for such objects.
I have renamed the topic title to the broader one than VolumeData only.
-
On 04/01/2016 at 02:27, xxxxxxxx wrote:
Hello,
Cinema 4D does not have an interactive renderer. Therefore the SDK does not include anything that is especially made for building an interactive renderer. Tools like the Hierarchy class are build for different purposes. So everything you build will be experimental and may or may not work.
If you really want to access the caches of "changed" objects you might want to read and iterate the cache manually as shown in GetCache().
Best wishes,
Sebastian -
On 18/01/2016 at 03:57, xxxxxxxx wrote:
Hello Sebastian,
Regarding recursive example of GetCache() the question is: do the functions op->GetCache(nullptr); and op->GetDeformCache(); produce cache data inside them and return the pointers to user? Or alternatively the caches could already be produced somewhere and these functions simply return the pointers.
I am interested in this to know whether I can call these functions for free.For example a strange situation for me is like this that I have noticed:
- I traverse the scene graph using GetCache() recursive example above;
- For Oinstance nodes we get cache (using op->GetCache()) and go down the hierarchy and these cached objects of instance have different address returned by pobj->GetPolygonR(); which seems to be memory vasting if it happens inside C4D even without me calling op->GetCache().
Thanks,
Anton -
On 18/01/2016 at 04:55, xxxxxxxx wrote:
1- the cache is already there, so they are free "if the cache is not there, it will return nullptr"
2- if the instance object got "render instance" checked, its GetCache() will return nullptr. -
On 18/01/2016 at 07:12, xxxxxxxx wrote:
Originally posted by xxxxxxxx
1- the cache is already there, so they are free "if the cache is not there, it will return nullptr"
Ok, why then Cinema does not exploit memory saving geometry instancing?
-
On 19/01/2016 at 01:25, xxxxxxxx wrote:
Hello,
in its default mode an Instance object acts as a generator and rebuilds the referenced object. This way it is possible to deform the instance. If you enable "Render Instance" Cinema will use the geometry of the referenced object directly and it is not longer possible to deform the instance.
Best wishes,
Sebastian -
On 23/01/2016 at 10:45, xxxxxxxx wrote:
Thanks for previous reply, Sebastian!
Are there any events in Cinema that allow to determine which BaseObjects were added or removed from scene hierarchy?
Of course I can compute it on my side by holding the list of old BaseObjects and the list of new BaseObjects and compare the lists. This needs hierarchical brute force checking of all the BaseObjects of BaseDocument. But I would like to look for some optimization.
Thanks,
Anton -
On 25/01/2016 at 01:52, xxxxxxxx wrote:
Hello,
when you add or remove an object you have to call EventAdd() to inform Cinema that something changed. EventAdd() triggers the core message EVMSG_CHANGE. Also when Cinema changes something in the scene it only calls EventAdd(). So the only thing to catch is EVMSG_CHANGE.
Best wishes,
Sebastian -
On 25/01/2016 at 05:44, xxxxxxxx wrote:
Thanks Sebastian, I knew about it EVEMSG_CHANGE already.
Currently, analyze there HDirty flags of BaseDocument with some inverval of time to make sure if I need to retravese Cinema scene graph and either add/remove or change the objects and instances in our rendering system.
UInt32 hdirty_object = doc->GetHDirty(HDIRTYFLAGS_OBJECT); UInt32 hdirty_object_matrix = doc->GetHDirty(HDIRTYFLAGS_OBJECT_MATRIX); UInt32 hdirty_object_hierarchy = doc->GetHDirty(HDIRTYFLAGS_OBJECT_HIERARCHY); UInt32 hdirty_tag = doc->GetHDirty(HDIRTYFLAGS_TAG);
And it is convenient. One remaining issue is with GetCache() and GetDeformCache() which may return nullptr if the cache is not yet built for generator object. They seem to build these caches lasilly and these caches become available some time later. And this is a clever idea for compute amortization!
My routing which periodically checks dirty bits may grab the scene data updates after generator object (like torus) was added but before they have caches built. And it also updates loaded dirty flags. And I also don't know how much time Cinema needs to build the caches.
Questions:
- Is there any way to know that while the cache is not yet built but should be there?
One possible solution is to make a big switch and check all supported generator objects (torus, sphere and others) and assume that they need caches. If no caches done for this time scene traversal then return later and check again. But this may require several scene full traversal until everything is loaded correctly to our renderer.
-
Is there any way to force all caches of BaseDocument to be built? May cause unwanted delays for user.
-
Is there any way to check for the whole BaseDocument using some cheap function whether all the needed caches of scene objects were built or not yet? Such a function can help me to know this kind of hint: "scene changed, but caches are being prepared yet, check later". With this I can save some flops full on scene traversals.
Thanks a lot for info!
Anton -
On 25/01/2016 at 09:39, xxxxxxxx wrote:
Hello,
you can check if an object is a generator by checking the OBJECT_GENERATOR flag. So you don't have to hardcode all generator types. I wouldn't know any way to check if all caches are build but you should be able to build the caches with ExecutePasses().
Best wishes,
Sebastian -
On 25/01/2016 at 11:03, xxxxxxxx wrote:
Hello Sebastian,
Does the function ExecutePasses() rebuild the caches from scratch every time I call it or it basically finish building where needed?
-
On 26/01/2016 at 09:28, xxxxxxxx wrote:
Hello,
ExecutePasses() triggers only the – well- execution of the scene. If a given generator actually does rebuild its cache or not is decided by that generator depending on its dirty state etc.
best wishes,
Sebastian