Solved BaseLink from cloned document


I need to get a linked object from a cloned document.
At first I tought it was trivial and would just work, learned about AliasTrans, but that didn't help either.
This is what I'm trying...

	void NewAnimationProgressDialog::Setup( BaseDocument* doc, BaseObject* node )
		AutoAlloc<BaseLink> nodeLink;
		nodeLink->SetLink( node );
		AutoAlloc<AliasTrans> aliastrans;
		document_ = static_cast<BaseDocument*>( doc->GetClone( COPYFLAGS_NONE, aliastrans ) );
		aliastrans->Translate( true );
		node_ = GetBaseLinkLink<BaseObject>( document_, nodeLink );
		// node_ is null!

You might have to use ForceGetLink()
See the description in the docs. That may reflect what you are trying to do.

I only want the object from the cloned document, not elsewhere.
The problem is that the BaseLink does not find it on the clone.

Hi @rsodre I'm wondering whats your final purpose?

The normal use case is to copy the linked C4DAtom and insert it in the new doc like its demonstrated in the C++ C4DAtom Copy documentation.

While in your case you do BaseDocument::GetClone and expect the BaseLink that you have in memory (The BaseDocument have no idea of this BaseLink) to be updated.

But this is the BaseDocument::CopyTo (called by GetClone) that create an AliasTrans, recreate everything and update this document BaseLink as its explained in the doc.

So I don't see a way to have its memory only Link being updated.


Hi @m_adam.

I'm baking a heavy procedural animation, so basically what I do is:

  • Some modifiers create the procedural animation
  • The last modifier is a recorder, where I set an output alembic file and have a button to start recording
  • Button pressed, I open a custom modal dialog
  • In the dialog, I clone the active Document
  • For every frame in the cloned doc frame range:
    -- Set time/frame
    -- call ExecutePasses()
    -- call the recorder's StoreFrame() function
  • Call the recorder's SaveAnimation() function
  • Close the dialog

Now I can go back to the active scene, delete the procedural animaiton nodes and playback from alembic.

When I open the modal dialog, I pass the active doc and recorder custom ObjectData. I need to retrieve the recorder's equivalent in the cloned document to call the save functions, that's why I make a BaseLink that need to be retrieved on the clone.

Hi @rsodre, unfortunately as explained in my previous post, your GeDialog the one that hosts your BaseLink as its not part of the cloned document is not updated by the AliasTrans, during the GetClone operation.

so a quick workaround is to store this BaseLink in the BaseContainer of the document and then retrieve it (Kudo to @m_magalhaes for the idea).
Then there are two issues in your code, first, you never initialize the ailiasTrans, and you don't pass the flag COPYFLAGS::DOCUMENT while you are copying a document.

And here a code sample that demonstrates the use of a BaseContainer.

	BaseObject* const obj = doc->GetActiveObject();
	if (obj == nullptr || !obj->IsInstanceOf(Ocube))
		return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION);


	// Build the BaseLink
	AutoAlloc<BaseLink> nodeLink;
	if (!nodeLink)
		return maxon::NullptrError(MAXON_SOURCE_LOCATION);

	// Store it in GeData
	GeData baseLinkData;

	// Store the GeData in the document, I use the ID 1000000 but please use anunique ID from plugincafe
	doc->GetDataInstance()->SetData(1000000, baseLinkData);

	// Copy the document, dont forget to initialize the AliasTrans
	AutoAlloc<AliasTrans> aliastrans;
	BaseDocument* newDoc = static_cast<BaseDocument*>( doc->GetClone( COPYFLAGS::DOCUMENT, aliastrans ) );
	aliastrans->Translate( true );

	// Read the GeData in the document
	const GeData baseLinkDataNewDoc = newDoc->GetDataInstance()->GetData(1000000);
	BaseLink* baseLinkNewDoc = baseLinkDataNewDoc.GetBaseLink();
	if (baseLinkNewDoc == nullptr)
		return maxon::NullptrError(MAXON_SOURCE_LOCATION);

	BaseList2D* test = baseLinkNewDoc->GetLink(newDoc);
	if (test == nullptr)
		return maxon::NullptrError(MAXON_SOURCE_LOCATION);
	ApplicationOutput("@, @"_s, test->GetName(), test->GetDocument()->GetName());

	test = baseLinkNewDoc->ForceGetLink();
	if (test == nullptr)
		return maxon::NullptrError(MAXON_SOURCE_LOCATION):

	ApplicationOutput("@, @"_s, test->GetName(), test->GetDocument()->GetName());

	return maxon::OK;


Hi @m_adam,
I finally got a change to try this solution and remove my search by name hack.
Works perfectly, thanks!

@m_adam said in BaseLink from cloned document:

// Store the GeData in the document
doc->GetDataInstance()->SetData(1000000, baseLinkData);

What's the rules to store data in a document?
I mean, which IDs can I use?

Your example works, but I hit a debug breakpoint when destroying the cloned document with KillDocument()
I don't know if it's related to that or not.

From ddoc.h...

DOCUMENT_HOOKS = 1000000, // 2000 IDs reserved from here

Hi @rsodre correct, in general, when dealing with BaseContainer to store data when you are not the owner of it(aka an ObjectData, ShaderData, etc..) you should register a unique ID (from plugincafe) this way you are sure to not have a conflict.
I should have mentioned in my snippet sorry.


I don't understand the relation between the document BaseContainer data and plugin ids.
Are you saying that I can use my custom plugin IDs as the data ID on the container?

About the debug breakpoint, I found out that it was because I'm killing the cloned document on the end of my ProgressDialog::Main(), which is a bad idea. Moved it to AskClose().

There is no relationship between BaseContainer IDs and plugin IDs.

All you need is a unique ID, that you can use as a BaseContainer ID. This forum (the plugin ID page) provides such IDs.