UNSOLVED Setting node dirty does not trigger an update.

Hello, here is a part of the code I am using.
Note that my RenderSettings class is inherited from VideoPostData

static const Int32 DLRenderFinished = 10000;


Bool RenderSettings::Init(GeListNode* i_node)
{
	render_started = false;
	return TRUE;
}


Bool RenderSettings::GetDDescription(GeListNode* i_node, Description* i_description, DESCFLAGS_DESC& i_flags)
{
	i_description->LoadDescription(ID_RENDERSETTINGS);

	const DescID* singleid = i_description->GetSingleDescID();
	const Int32 ID = DL_RENDER_BUTTON;
	const DescID cid = DescLevel(ID, DTYPE_BUTTON, 0);

	if (!singleid || cid.IsPartOf(*singleid, nullptr))
	{
		BaseContainer bc = GetCustomDataTypeDefault(DTYPE_BUTTON);

		bc.SetInt32(DESC_CUSTOMGUI, CUSTOMGUI_BUTTON);
		if (render_started)
			bc.SetString(DESC_NAME, "Stop Render"_s);
		else
			bc.SetString(DESC_NAME, "Start Render"_s);

		i_description->SetParameter(cid, bc, DescLevel(ID_OBJECTPROPERTIES));
	}
	i_flags |= DESCFLAGS_DESC::LOADED;
	return SUPER::GetDDescription(i_node, i_description, i_flags);
}


Bool RenderSettings::Message(GeListNode* i_node, Int32 i_type, void* i_data)
{
	switch (i_type) 
	{
	case MSG_DESCRIPTION_COMMAND: 
	{
		DescriptionCommand* dc = (DescriptionCommand*) i_data;
		const Int32 id = dc->_descId[0].id;

		if (id == DL_RENDER_BUTTON)
		{
			//Start rendering and show stop render button
			render_started = true;
			SpecialEventAdd(DL_START_RENDER);
			i_node->SetDirty(DIRTYFLAGS::DESCRIPTION);
		}
		break;
	}
	
	case DLRenderFinished:
               //DLRenderFinished is executed successfully when rendering is finished, but this does not trigger an update on the UI.
		render_started= false;
		i_node->SetDirty(DIRTYFLAGS::DESCRIPTION);
		break;
	}
	return SUPER::Message(i_node, i_type, i_data);
}

Thank you.

Hi,

when you register your videopostdata, you need to define a description resource file see [RegisterVideoPostPlugin.] (https://developers.maxon.net/docs/Cinema4DCPPSDK/html/c4d__videopostdata_8h.html#a331d072052d737de4730f4a6ea9020cb) Otherwise, the function GetDDescription will never be called.

In GetDDescription, you can than load the dummy resource file and add your button defining the parent to "ID_VIDEOPOSTPROPERTIES" and not ID_OBJECTPROPERTIES.

Bool PC13401::GetDDescription(GeListNode* node, Description* description, DESCFLAGS_DESC& flags)
{
	if (!description->LoadDescription(node->GetType()))
		return false;


	const DescID* singleid = description->GetSingleDescID();
	const DescID cid = DescLevel(g_LaunchButtonID, DTYPE_BUTTON, 0);

	if (!singleid || cid.IsPartOf(*singleid, nullptr))
	{
		BaseContainer bc = GetCustomDataTypeDefault(DTYPE_BUTTON);

		bc.SetInt32(DESC_CUSTOMGUI, CUSTOMGUI_BUTTON);
		if (_render_started)
			bc.SetString(DESC_NAME, "Stop Render"_s);
		else
			bc.SetString(DESC_NAME, "Start Render"_s);

		description->SetParameter(cid, bc, DescLevel(ID_VIDEOPOSTPROPERTIES));
	}

	flags |= DESCFLAGS_DESC::LOADED;
	return NodeData::GetDDescription(node, description, flags);
}
// .....

// in my message function:
	i_node->SetDirty(DIRTYFLAGS::DESCRIPTION);
	_render_started = !_render_started;

Cheers,
Manuel

Hello,
Yes, I am aware of that part, I had already registered my VideoPost Plugin and ID_RENDERSETTINGS is the plugin ID I am using for that (Loading it on GetDDescription which loads fine), but I had not included register function on my sample code.

Also, GetDDescription is being called when I click the 'render' button on my Render Settings (VideoPost) but the problem is that it does not get called when the rendering has finished (for this to happen I don't interact with the UI at all), but only set my node dirty, thinking it would automatically trigger GetDDescription function to be called.

Defined the parent to 'ID_VIDEOPOSTPROPERTIES' as suggested but this did not help too.

Thank you.

Hi,

Sorry i'm not able to reproduce that issue, it's working fine here.
Could you provide the RegisterVideoPostPlugin function and how you send the message to the VP?

Cheers,
Manuel

Hello, here is the code.

Bool Register3DelightPlugin(void)
{
	return RegisterVideoPostPlugin(ID_RENDERSETTINGS, "3Delight"_s, PLUGINFLAG_VIDEOPOST_ISRENDERER, RenderSettings::Alloc, "dlrendersettings"_s,  0, 0);
}

The message is sent through RenderManager which is inherited from Message Data.

Bool RenderManager::CoreMessage(Int32 id, const BaseContainer& bc)
{
//This is executed when rendering has finished.
	if (id == DL_FRAME_COMPLETED) 
	{
		ApplicationOutput("Frame has completed Rendering");
		BaseDocument* doc = GetActiveDocument();
	
		RenderData* rd = doc->GetActiveRenderData();
		bool has_vp = false;
		BaseVideoPost* vp = rd->GetFirstVideoPost();
		
		while (vp != NULL && !has_vp) 
		{
			has_vp = (vp->GetType() == ID_RENDERSETTINGS); // Look for rendersettings

			if (!has_vp) 
			{
				vp = vp->GetNext();
			}
		}

		if (has_vp) 
		{
//Message to VP is sent correctly. This triggers the RenderSettings::Message function for case DLRenderFinished.
			vp->Message(DLRenderFinished);
		}
		StatusSetText(String(""));
		parser = 0x0;
		RenderNextFrame();
	} 
       return TRUE;
}

Hi,
Sorry I'm probably missing something.

I'm using R21.023, what's your version?
What if you drag and drop the VP into the python console and add .Message(DLRenderFinished) ? (of course, the number itself must be used and not DLRenderFinished)

I'm wondering if there's not a thread issue now.

Cheers,
Manuel

Hello,
I am using R21.027.
From python console, it worked fine. After setting the node dirty, getDDescription function got executed. But I wonder what is different as the same event is being executed when the rendering finishes and the node is set Dirty except that getDDescription does not get executed after.

Hi,
Cool we are progressing 😄
Can you try something like that? This will execute the call to message directly in the MainThread.

if (has_vp)
{
   maxon::ExecuteOnMainThread([]()
     {
       vp->Message(DLRenderFinished);
     }
   );
}

Cheers,
Manuel

Hello,
Tried that by still the same :/.

Thanks,
Ogers.

Hello @everyone,

don't mind me, this is just a slightly bored myself surfing on private time and did not read the thread in its entirety. But what stands out to me is that you do this in your RenderManger::CoreMessage:

BaseDocument* doc = GetActiveDocument();

Is there actually a guarantee that your render manager will only render the active document? And when you use the rendering logic provided by Cinema, the active document and rendering document will be two different documents, even when the rendering was spawned for the active document, because for rendering, a document is being cloned. So, unless this is intentional, you might be operating on the wrong document there, because as I do understand, this message of yours is coming in from a node/something in the rendered document.

Just pointing this out because it is a common pitfall in the Cinema 4D SDK.

Cheers,
Ferdinand

Hello,

BaseDocument* doc = GetActiveDocument();
BaseDocument* renderdoc = (BaseDocument*) doc->GetClone(COPYFLAGS::DOCUMENT, nullptr);

RenderData* rd = renderdoc->GetActiveRenderData();

If this is what you mean for getting the rendering document, I am using this way on my original code. I am not sure if the issue is related to this.

Cheers,
Ogers

Hi,

as the VideoPostData is inside the current document, it's right to retrieve the current document. But as Ferdinand said, that's a common pitfall.

I was able to reproduce the issue. Using the Console automatically add an EventAdd(). That's why it was working using the console. If you send the message with the script manager, it will not work.

So, you have to "force" the UI update with an EventAdd(). SetDirty only set the VideoPostData dirty but doesn't trigger a UI update.
As it's the end of the render, the scene should not have changed too much and the redraw shouldn't take too much time.

	case DLRenderFinished:
		{
			//DLRenderFinished is executed successfully when rendering is finished, but this does not trigger an update on the UI.
			_render_started = false;
			i_node->SetDirty(DIRTYFLAGS::DESCRIPTION);
			EventAdd();
			break;
		}

Just one question, that's strange to launch the render from the render preferences. Is that the final workflow?

Cheers,
Manuel

Hello,
Trying to "force" the UI update using EventAdd() after setting the node dirty still does not trigger GetDDescription function. I thought this was what I was missing but seems there is something else.

I am not 100% sure if it will be the final workflow, but for now, that's our intention. We are trying to keep a uniform UI on all DCCs (as in the image below) and this way has proven to be efficient.
Of course, this will not be the only way to launch a render, but if we see it to be efficient in Cinema4D too then yes this will also be a way we will keep.
Unfortunately, our C4D plugin is too far away from other plugins that we support on other DCCs, and there's still a lot to do.

As for the buttons' workflow, it would be:
Clicking Render will set the button to Abort Render and make the other two buttons insensitive until rendering finishes (that is why I want to update the UI after rendering is finished), or abort render is clicked which this updates the UI fine.

The same goes for the other two buttons (Start IPR -> Abort IPR and make the other two buttons insensitive until you abort it).

a6afe6b4-f6e3-481e-bb85-cdcfa0bb9049-image.png

Cheers,
Ogers