Create Hidden Cloner Object

On 04/02/2015 at 01:45, xxxxxxxx wrote:

User Information:
Cinema 4D Version:   14,15,16 
Language(s) :     C++  ;

I am trying to create a (hidden) cloner object so I can use its surface distribution properties, like retrieving global positions and normals of individual clones.

However, I am getting stuck at the very beginning.
Here is some code:

BaseDocument* doc = node->GetDocument();
BaseObject *clonerObj = BaseObject::Alloc(1018544); // Create a cloner object
if (!clonerObj) return true;
BaseTag *tag = clonerObj->GetTag(ID_MOTAGDATA);
if (!tag) return true; // The tag is not found so the rest is not executed
GetMoDataMessage msg_data;
tag->Message(MSG_GET_MODATA, &msg_data); 
LONG clCount = msg_data.modata->GetCount();
GePrint(LongToString(clCount ));

The only situation that I got working is to search for a cloner object from the scene, but I want to create my own cloner and hide it from the user (if possible).
I tried inserting the cloner into the document and even inserting an object under the cloner, the tag is still not found unless done manually and not programamtically.

Any help would be greatly appreciated.

On 04/02/2015 at 06:36, xxxxxxxx wrote:


there is nothing wrong. When you create the cloner object you do just that – you create the cloner object, nothing else. The cloner hasn't done anything yet. The cloner will only create the MoData tag when it is used inside a scene and it is necessary to create the tag.

If you want to force the cloner to be in such a state you could create a temporal BaseDocument, add the cloner and everything needed to turn it into that state to the document and call the temporal document's ExecutePasses(). Then you can remove the cloner from that document.

Best wishes,

On 05/02/2015 at 04:04, xxxxxxxx wrote:

Thank you Sebastian, it works.
Now to my next problem, where I need to set some parameters to the cloner object. 
This is the code I used:

BaseObject *distributionLink = bc->GetObjectLink(LINKEDOBJ,doc);
BaseObject* tempObj;
if (distributionLink) 
	tempObj = static_cast<BaseObject*>(distributionLink->GetClone(COPYFLAGS_0, NULL)); 
	doc->InsertObject(tempObj , NULL, NULL, NULL);
	clonerObj->SetParameter(DescID(MG_OBJECT_LINK), tempObj , DESCFLAGS_SET_0);
clonerObj->SetParameter(DescID(MG_POLY_MODE_), 3, DESCFLAGS_SET_0); // Surface distribution mode
doc->InsertObject(clonerObj, NULL, NULL, NULL); // This inserts the cloner with the proper parameters set with a default total of 20 clones
BaseTag *tag = clonerObj->GetTag(ID_MOTAGDATA);
GetMoDataMessage msg_data;
tag->Message(MSG_GET_MODATA, &msg_data);
int clCount = msg_data.modata->GetCount();
//Get the positions of the clones in the cloner object
MoData *md = msg_data.modata;
MDArray<Matrix>mtx = md->GetMatrixArray(MODATA_MATRIX);
MDArray<LONG>farr = md->GetLongArray(MODATA_FLAGS);
for (int i=0; i<clCount; i++)
	printVector(mtx[i].off); //this is a custom print function for vectors

The problem is that it always return the original 3 positions of a default cloner, without taking into account the surface distribution mode. Anything I need to do to update the output to use the current cloner parameters?

On 05/02/2015 at 04:34, xxxxxxxx wrote:


as said before, the cloner object itself won't do anything on its own. Only if it is part of a document and that document is executed, the cloner will do it's work, create clones and edit the MoData.

For further questions on different topics please open a new thread for each question. Thanks.

Best wishes,

On 05/02/2015 at 05:03, xxxxxxxx wrote:

I finally got it working. The problem was coming from the insertion of the cloner into the active document (which I was using for debugging). Once I omitted the insertion I got the proper count output.

Thanks again for your help.

On 05/02/2015 at 14:30, xxxxxxxx wrote:

A follow up question if I may.
The temporary document solution works in case I was executing the command once. However, once I remove the cloner from the temporary document, I can no longer modify its parameters.

The situation is like this: I am creating an object plugin where I am using a hidden cloner to distribute clones over a surface. I want to change the clone count in GVO obviously. This is not updating since it's running outside the temporary document.

tempdoc->InsertObject(clonerObj, FALSE, FALSE);
//Any parameter set here works
//Now ExecutePasses
clonerObj->InsertUnder(doc); // Inserting the cloner back into the main document
//Parameters set outside the tempdoc do not work				
clonerObj->SetParameter(DescID(MG_POLYSURFACE_COUNT), 5, DESCFLAGS_SET_0); //does not work

So the question is: how do I make it work in GVO, so it's interactive.

On 06/02/2015 at 07:24, xxxxxxxx wrote:


I guess with "GVO" you mean GetVirtualObjects()?

To insert an object into a document you don't use InsertUnder(); use InsertObject() instead. And as I said, it is not enough for an object to be in an document. Functions like GetVirtualObjects() are only called when that document is evaluated. Depending on the object in question, just chaining a parameter value won't cause any action of that object.

When you write a generator that creates objects, these objects are not added to the document using InsertObject(). Instead, a generator creates a cache. The content of that cache is defined by the object (and it's children) returned by GetVirtualObjects().

For questions no longer related to this thread's original topic, please open a new thread. Thanks.

Best wishes,