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 13/08/2017 at 05:15, xxxxxxxx wrote:
Why do you need that?
On 14/08/2017 at 00:36, xxxxxxxx wrote:
Hi Aaron,
Thanks for your reply.
Yes, tacking objects across different rendering calls and also for animation. Frozen Tarzan was a team member who started doing this a while back.
No, not for a shader. Hashing the path/names is also no option as object can have the same names. Hashing the path would be vulnerable to changed object order in one single hierarchy. So you would have to track position within hierarchy as well. Again, this is very vulnerable.
I am generating IDs on each render pass for objects that have not been identified already. The problem is, in Cinema 4D most objects that are visible to the renderer do not exist in the "normal" hierarchy but are part of caches or deform caches. For example, if you put a character into a Subdivision Object, none of the original objects will make it to the renderer. They are all marked as control objects. Other than with other control objects, their resulting geometry is not found in their own caches though but in the Subdivsion Object. Unfortunately, the Subdivision Object does not create unique IPs as other generators. So there is no way to identify the created geometry in a persistent way.
To use an object tag (hidden and automatically managed) was one option I was thinking about but using the AddUniqueID() method is easier. This is also usable for objects that are not able to have Tags.
My problem is really to identify objects in some edge cases. So far I have it working for most situations. Even for instances of complete hierarchies. Objects like the Subdivision Object seem to work quite differently and in contrast to what the documentation recommends that third party developers should do.
As for the waste of time: We have render times of a few milliseconds, so each fraction of a second I can save transferring scene data is worth it in our opinion. In the end, the goal is to render the scene live
Thanks for your help. Martin
On 14/08/2017 at 06:16, xxxxxxxx wrote:
Hi Martin, first of all thanks for writing us.
I strongly apologize for getting later than expect to you with reference to uniquely identifying objects in Cinema but it's taking me longer than expected to formulate a comprehensive answer (touching different cases) to prove that it's indeed possible and consistent with how Cinema is internally designed.
I kindly ask you for a little more patience in order to reorder ideas and get back once and (maybe) for all on the topic.
Best, Riccardo
On 14/08/2017 at 13:06, xxxxxxxx wrote:
Hi Assoc,
Btw, have a look at a problem as in this thread: https://plugincafe.maxon.net/topic/580/13734_posemorph-tag-always-increases-getdirty-counter
What is relevant for your case is that in some cases the object generated caches/addresses may change very frequently. Without even any user change. The object IDs may break rapidly.
Yes, using hierarchical name hashing has dissadvantage that names on the same level can be same and object ID change if we move the object up/down through the hierarchy. But good thing is that by default Cinema makes them different
One more idea to think about for you is to assign object IDs with some clever method until certain level (until a generator like subdiv or cloner). And starting from the generator and deeper (when we work with caches and objects that depend on scattering parameters) we may assign depth-first order enumeration for children.
I look after this topic too to get more options for my goals. Btw, I have a problem. Not very huge however. It's simply impossible to get any object ID in a method
Bool NodeData::InitGLImage(BaseMaterial* mat, BaseDocument* doc, BaseThread* th, BaseBitmap* bmp, Int32 doccolorspace, Bool linearworkflow) {}
This method is called to get a texture 256x256 for object color in the viewport. So the custom Variation shaders look the same in the Viewport. And if you look for what objects this material is assigned too then there can be several.
On 15/08/2017 at 07:43, xxxxxxxx wrote:
funny that this thread is active.. i was just looking for smth like this...
im trying to store an object using a string, to find it again later obviously the name is not enough, there can be multiple objects with the same name so is there a possibilty to get an id or index for an object? GetGUID() gives me a number, but how can i select this object again if i have the GUID? note: im using this only for real objects (no generators, cache or whatever) is GUID safe for that?
and... if you str(object) you get a number.... 0x.... is this usable or just a memory position?
On 15/08/2017 at 23:44, xxxxxxxx wrote:
in InitGlImage you don't have an object reference AFAIK. I was briefly looking into that. My conclusion was, I needed to overwrite GLDraw() which is not documented. From what I saw you need to build your own GLSL shaders with some undocumented methods. There are some GL examples in the SDK that can get you a step or two further but ultimately without documentation how GLSL viewport shaders are build by Cinema 4D this needs quite some effort in reverse engineering
Cheers, Martin
On 15/08/2017 at 23:48, xxxxxxxx wrote:
Hi Zeorge,
str(object) will give you undefined behavious. It's converting the memory content of the object to a string. Just don't do that!
I would try GetGUID(). I have found cases were objects receive the same GUID (see above) but that seems to be restricted to caches. The method suggested in the SDK is GetMarker(). This will be unique for each object. It will change frequently though. If you need the IDs for only one pass parsing through the scene and have no requirement for them to be persistent, Marker should work fine.
On 15/08/2017 at 23:52, xxxxxxxx wrote:
BTW: I am assigning IDs to objects in a first parsing pass and build my own cache of IDs for all objects in caches based on their Control Object parent and their IPs. Where this is not possible, I try GUID and where that fails I resort to Marker to differtiate between objects (though, no longer can identify them).
It seems that different generators behave differently. Putting a character in a subdivision objects yields quite different structural results than using ie. Cloners. Using deformers again has a different structure.
And I haven't even started to talk about instances of hierarchies. They pose their own set of challenge that I am about to finish.
Thanks Riccardo.
On 18/08/2017 at 04:25, xxxxxxxx wrote:
Hi Martin, first and foremost thansk for your patience.
This week has been busy not to have been cope with reordering my findings in a short time and I also wanted to extend my research to reduce the "corner case" risk.
Cinema 4D API offers basically two means to uniquely identify objects: BaseObject::GetGUID() and BaseList2D::GetMarker() which respectively access the object's unique ID and object's GeMarker.
The intent of my research has been to demonstrate that GUID(s) is indeed a reliable way to track objects in Cinema across different user interaction scenarios and aided by GeMarker(s) they offer a complete way to track objects uniquely around Cinema.
Scanning a very simple scene and printing the IDs and GeMarkers info belonging to the objects present provides the following table:
=================================================================================================== [ { GeMarker CRC / GetMarkerStampEx } | GetGUID ] [ { 000459316928 / 0014570499 # 07496 } ] TraversalTest01.c4d [ { 003110699520 / 0014622782 # 07807 } | 0x739249CE036A085C ][0]Cube.A : Cube, 5159-0/0/0/0-0/1 [ { 000611561600 / 0002058671 # 09006 } | 0x4B1F714B50E82730 ][0][C]Cube.A : Polygon, 5100-0/0/0/0-1/1 [ { 003620385536 / 0063246992 # 13019 } | 0x29738F0DAFFED8E7 ][1]+Sphere.A : Sphere, 5160-0/0/0/0-0/1 [ { 002984169472 / 0002058671 # 09014 } | 0x51A98A6F2EF6E7EF ][1]+[C]Sphere.A : Polygon, 5100-0/0/0/0-1/1 [ { 003436609536 / 0002058671 # 09023 } | 0xD1A95C51EE8F50E6 ][1]+[C][D]Sphere.A : Polygon, 5100-0/0/0/0-1/1 [ { 000174035360 / 0088455968 # 08560 } | 0x05B40FA55FC2F5DF ][2]++Bulge : Bulge, 5129-0/0/0/0-0/1 [ { 003097937408 / 0063290296 # 13696 } | 0x4EE5F93031C05A1A ][0]Sphere.C : Sphere, 5160-0/0/0/0-0/1 [ { 002138721536 / 0002058672 # 09030 } | 0x51A98A6F2EF6E7EF ][0][C]Sphere.C : Polygon, 5100-0/0/0/0-1/1 [ { 003100995840 / 0014593669 # 07682 } | 0x2879AC49124B66C3 ][1]+Cube.C : Cube, 5159-0/0/0/0-0/1 [ { 000041026420 / 0002058672 # 09039 } | 0x4B1F714B50E82730 ][1]+[C]Cube.C : Polygon, 5100-0/0/0/0-1/1 [ { 002939917824 / 0014594104 # 07733 } | 0x06B497DDDB536CC5 ][0]Cube.D : Cube, 5159-0/0/0/0-0/1 [ { 002548005888 / 0002058672 # 09047 } | 0x646E97C2D42A60BE ][0][C]Cube.D : Polygon, 5100-0/0/0/0-1/1 [ { 001481651072 / 0002058672 # 09085 } | 0xE46E41FC1453D7B7 ][0][C][D]Cube.D : Polygon, 5100-0/0/0/0-1/1 [ { 000965783296 / 0088388848 # 08324 } | 0x062A5B18C994B055 ][1]+Bend : Bend, 5128-0/0/0/0-0/1 [ { 004036197120 / 0014594509 # 07747 } | 0x42F618087566F85F ][1]+Cube.E : Cube, 5159-0/0/0/0-0/1 [ { 001382765824 / 0002058672 # 09055 } | 0x646E97C2D42A60BE ][1]+[C]Cube.E : Polygon, 5100-0/0/0/0-1/1 [ { 002404496384 / 0002058672 # 09078 } | 0xE46E41FC1453D7B7 ][1]+[C][D]Cube.E : Polygon, 5100-0/0/0/0-1/1 [ { 002284037888 / 0088342184 # 08167 } | 0x7E14190FED9A5DE4 ][2]++Twist : Twist, 5134-0/0/0/0-0/1 [ { 001033116672 / 0014594922 # 07761 } | 0x1ACC3DCCC83D6EE4 ][2]++Cube.F : Cube, 5159-0/0/0/0-0/1 [ { 001744077952 / 0002058672 # 09063 } | 0x4B1F714B50E82730 ][2]++[C]Cube.F : Polygon, 5100-0/0/0/0-1/1 [ { 002722147328 / 0002058672 # 09071 } | 0xCB1FA77590919039 ][2]++[C][D]Cube.F : Polygon, 5100-0/0/0/0-1/1
In the table above the info after GUID is as follows: [ n]+++[C][D]<name> : <type name>, <type>-<other status flags>
The test scenes are available here:
... bl->GetMarker().GetMemory(data, size); const UInt32 crc32 = ZipFile::CalcCRC32(data, size); markerString = String::FloatToString(Float32(crc32), 12, 0) ...
_ NOTE: The value for the GUID is obtained by using String::HexToString conversion on the returned value from GetGUID. Although the value returned looks like a memory address it is not._
... const UInt64 guid = obj->GetGUID(); const String guidString = String::HexToString(guid); ...
Opening the file TraversalTest_01.c4d leads to the following results:
Rendering View (Ctrl-R) leads to the following results: _ NOTE: _During a rendering process only object caches representing the final geometry of the objects are provided in the VolumeData() instance which means the ID data is extracted from the objects actually used by the Renderer. To retrieve the initial generators it's required to implement a recursive function using BaseObject::GetCacheParent() to iterate up to the parent generator. Objects listed as "CacheP" are the root-cache parent of a cache object.
_ _ Rendering via IRR (Alt-R) leads to the following results: NOTE: During a rendering process only object caches representing the final geometry of the objects are provided in the VolumeData() instance which means the ID data is extracted from the objects actually used by the Renderer. _To retrieve the initial generators it's required to implement a recursive function using BaseObject::GetCacheParent() to iterate up to the parent generator. _ Objects listed as "CacheP" are the root-cache parent of a cache object. IRR clones the active scene to run the interactive rendering in a separate thread and leave Cinema 4D free to interact with the user.
Rendering in PV (Ctrl-R) leads to the following results: _ NOTE: During a rendering process only object caches representing the final geometry of the objects are provided in the VolumeData() instance which means the ID data is extracted from the objects actually used by the Renderer. _ _To retrieve the initial generators it's required to implement a recursive function using BaseObject::GetCacheParent() to iterate up to the parent generator. _ Objects listed as "CacheP" are the root-cache parent of a cache object. Render to PV clones the active scene to run the rendering in a separate thread and leave Cinema 4D free to interact with the user.
Cloning the scene (via simple Python script) leads to the following results:
_ _ Copy &pasting active document's objects into a new document leads to the same results brought by Cloning the scene;
Swapping from cloned scene to source scene leads to the following results:
Changing a parameter affecting geometry representation (Fillet "ON" in Cube.C) leads to the following results:
Undoing the parameter change affecting geometry representation leads to the following results:
Saving the scene (overwriting existing file) leads to the following results:
Saving the scene to a new file leads to the following results:
Closing Cinema 4D and opening the new saved scene leads to the following results:
Re-opening the initial scene leads to the following results:
In the end:
Looking forward your thought or comments (or even better corner case), give best. Riccardo
On 18/08/2017 at 05:30, xxxxxxxx wrote:
Wow, thanks Riccardo! Please pin this topic somewhere.
On 18/08/2017 at 06:23, xxxxxxxx wrote:
Actually the plan is to add the info to the SDK docs.
On 18/08/2017 at 06:49, xxxxxxxx wrote:
Hi Riccardo,
thank you for your extensive research.
Unfortunately, your findings confirm my own research:
GUIDs are not unique as duplicated of the same GUID can appear in caches
GeMarker change when the generator updates objects. Thus loosing tracking between different render calls
Objects that are repesented in the scene work fine. Unfortunately, a typical Cinema 4D scene's geometry that needs to be rendered is mostly found in caches and deform caches. Generators and deformers cannot be rendered directly.
The generated geometry cannot be reliably tracked/identified. Identical GUIDs are used for different objects in caches. GeMarker change when the geometry is changed.
Generators should make use of unique IPs so that objects can be reliably tracked across different frames. Unfortunately, for example the Subdivsion Object does not generate IPs for its generated objects in the Cache. Putting a hierarchy of objects into a Subdivision object has two results: all objects are marked as control object. Control objects themselves indicate that they are not to be used directly but that they generate the data that should be used. Typically, this data is found in the cache of the object itself. The Subdivision Object exhibits different behaviour. All the generated geometry is put in it's own cache, regardless where the control object for that geometry. This way, there is no reliable way to deduct the control object that originally was responsible for creating the data.
The problem I have is not getting to the actual geometry to render but to keep track of which object generated what and to only update the changes. With rendering times of a few milliseconds it ridiculous to spend dozens of secoonds or even a few minutes to prepare the frame.
In conclusion: Ultimately, GUIDs and GeMarker can be used to differentiate between objects. Identification is not possible though. Also, UniqueIPs - a proposed method to track/identify genearated objects during animation - is not consistenly used by generators.
The same GUID is used for different objects in caches. Visible geometry for many scenes can be found exclusively in caches.
GeMarker changes when the geometry for generated objects change, making it impossible to identify the object between two consecutive renderings.
Cloning documents changes GeMarkers. Cloning is happening frequently when rendering.
The proposed mmethod to track generated object by using their UniqueIP fails as generators like the Subdivision Object does not assign UniqueIPs
BTW: I need to identify objects not only between consecutive renderings but I also track changes "LIVE" to immediately update the realtime rendering. I am not using VolumeData in the VideoPost Plugin for this very reason.
I am using Hierarchy class derived classes to traverse the scene and also handle instances myself.
Here is a scene that is showing some of the duplicate GUID problems and also shows how Subdivision Object is handling caches and UniqueIPs differently: https://drive.google.com/file/d/0BwFHFNK-2yFAMGpIVkRodDlxbzg/view?usp=sharing
Image of resulting test-rendering here: https://drive.google.com/file/d/0BwFHFNK-2yFAcjFlaGJ1NG5wb28/view?usp=sharing
Edit: Here is my log of the scene: https://drive.google.com/file/d/0BwFHFNK-2yFAUVhtU2ZOT21LV1E/view?usp=sharing For IP I print the cache parent ID and the IP. ID in general is the assigned ID I created. (cached) has a CacheParent is not part of the scene the user sees in the object manager.
I am having troubles with identification in this scene mostly due to the nesting of instances and putting everything into a Subdivision Object. One Holder_01 "instance of an instance of an instance" and a couple of Holder_02 objects are eluding my efforts to identify them.
Thanks, Martin
BTW: I am out of the office next week. So I will not be able to respond in a timely fashion.
On 18/08/2017 at 14:06, xxxxxxxx wrote:
Martin, attach few expression tags and you will get always changing addresses/pointers of objects. Same object remains untouched, but you make a mouse click and it changes and it will report to dirty flags, and most likely to ID assigning staff that you must remove/add things (not just update them slightly). Welcome to Cinema 4D >:E :)) Let's ask Maxon to solve this ID issue and update the API for recent couple of major Cinema versions.
On 18/08/2017 at 15:56, xxxxxxxx wrote:
i also would like is to be able to identify objects by using an unique object id. as stated here BaseObject.GetGUID() is not unique (?) and even if it is, there is no method to get the BaseObject if you just have the GUID. (is there one?)
btw it would be nice if it wouldnt be just an in-memory id, but would stay the same when quitting the app and reopening the file.
ps if you str() an object you get <c4d.BaseObject object called 'Null/Null' with ID 5140 at 0x1412bd6d0> is this hex address usable for indentification?
On 28/08/2017 at 01:31, xxxxxxxx wrote:
I have found that the best way to identify an object that is not in a cache is to use AddUniqueID(). This should be serialized to the file as well. It has been working fine for me.
If you don't care about changed IDs with cloned scenes or re-created objects, you can always use GeMarker().
My problem is that I actually have scenes where ALL visible geometry is exclusively coming from object caches. This is where my approaches start to fall appart. Most of the time I can rely on FindUniqueID() of the CacheParent() and the UniqueIP() to reconstruct a unique identification. Unfortunately, this breaks with ojects like the subdivision surface object who do not assign uniqueIPs and also do not make the generator source objects identifiable.
@zeorge: having the GUID, traversing the document to get the BaseObject should not be that time consuming. Even with several thousand objects.
On 28/08/2017 at 03:56, xxxxxxxx wrote:
If I may drop some insight here on why GUIDs might not always be unique: It's likely due to the use of crc32. In scenes with many objects, especially many generators used, there will be collisions.
I ended up calculating GUIDs myself, only using UniqueIPs, hasing them with xxHash (https://github.com/Cyan4973/xxHash), fixing some of the obvious shortcomings by adding dummy IPs for generators - or something like that, it's been a while.
Works pretty well for uniqely identifying objects across frames of an animation - unfortunately it's useless for identifying objects across insertions/deletions in the scene.
Originally posted by xxxxxxxx Actually the plan is to add the info to the SDK docs.
Originally posted by xxxxxxxx
Please add "not using crc32" to said plan.
On 28/08/2017 at 04:29, xxxxxxxx wrote:
From what I've experienced, the identical GUIDs were not due to hash clashing but actually objects that where generated from the same source object, thus being "identical" to them. Unfortunately, that makes it impossible to identify the generated objects
I also use hashing throughout my parsing and use 64-bit SipHash (https://131002.net/siphash/).
On 01/09/2017 at 09:44, xxxxxxxx wrote:
Thanks both Yves and Martin for keeping your insights to come.
@fused: actually we internally use CRC64 which make the chance to get GUID collisions really unlikely; the reason because there might be equal GUIDs in the scene is mostly related to how GUIDs are currently generated. @assoc: to reduce (not to exclude) the chance to avoid GUIDs duplication you can enumerate you objects in the scene by setting their IP (BaseObject::SetUniqueIP()) using something like:
const Bool SetObjectsIP(BaseObject *op) { Int32 ipnum = 1; while (op) { if (!SetObjectsIP(op->GetDown())) return false; op->SetUniqueIP(ipnum++); op = op->GetNext(); } return true; }
As I've described in my previous post, we are aware of the fact that caches might end up in sharing the same UID, and we are also aware that unique identification is a pretty relevant topic for interactive rendering purposes. That said I'll try to rise up in the future an internal discussion on the topic to see if and how the desired behavior could be addressed.
Best, Riccardo.
On 04/09/2017 at 00:13, xxxxxxxx wrote:
Thanks Ricardo.
Yes, using SetUniqueIP() is what I would expect to be used by generators that have several objects in their caches. As I am parsing the scene - not generating objects myself - I have no influence how other generators are handling UniqueIPs.
In this regard, I noticed that the Subdivision object does not use UniqueIPs. So I am having a hard time to identify the generated objects between scene changes because they don't have UniqueIPs. So I was trying to use their GUID as fallback but then noticed that the Subdivision object had objects with the same GUID in its cache as well. The GeMarker unfortunately does not help either as it changes frequently.
So I have this case where all methods of the SDK to identify generated objects in a cache fails.