Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Setting node dirty does not trigger an update.

    Cinema 4D SDK
    r21 c++
    3
    18
    2.0k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • O
      Ogers
      last edited by

      Hello,
      I have a scenario where I am trying to trigger an update after a specific message but setting the node dirty does not seem to trigger this update.
      I am doing the update on getDDescription() function and set the flag in NodeData message function i_node->SetDirty(DIRTYFLAGS::DESCRIPTION);
      Although the node has been successfully set dirty, it does not execute getDDescription() function after that.
      Am I missing something?

      1 Reply Last reply Reply Quote 0
      • ManuelM
        Manuel
        last edited by

        Hi,

        GetDDescription is called by cinema 4D if the UI need any update. Setting the nodeData dirty could not be enough in certain cases.
        How do you determine if GetDDescription have been called or not?
        Sharing some code would help us to reproduce the issue.

        Cheers,
        Manuel

        MAXON SDK Specialist

        MAXON Registered Developer

        1 Reply Last reply Reply Quote 0
        • O
          Ogers
          last edited by Ogers

          This post is deleted!
          1 Reply Last reply Reply Quote 0
          • O
            Ogers
            last edited by

            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.

            1 Reply Last reply Reply Quote 0
            • ManuelM
              Manuel
              last edited by Manuel

              Hi,

              when you register your videopostdata, you need to define a description resource file see [RegisterVideoPostPlugin.] (https://developers.maxon.net/docs/cpp/2023_2/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

              MAXON SDK Specialist

              MAXON Registered Developer

              O 1 Reply Last reply Reply Quote 0
              • O
                Ogers @Manuel
                last edited by

                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.

                1 Reply Last reply Reply Quote 0
                • ManuelM
                  Manuel
                  last edited by

                  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

                  MAXON SDK Specialist

                  MAXON Registered Developer

                  O 1 Reply Last reply Reply Quote 0
                  • O
                    Ogers @Manuel
                    last edited by Ogers

                    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;
                    }
                    
                    1 Reply Last reply Reply Quote 0
                    • ManuelM
                      Manuel
                      last edited by

                      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

                      MAXON SDK Specialist

                      MAXON Registered Developer

                      1 Reply Last reply Reply Quote 0
                      • O
                        Ogers
                        last edited by

                        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.

                        1 Reply Last reply Reply Quote 0
                        • ManuelM
                          Manuel
                          last edited by

                          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

                          MAXON SDK Specialist

                          MAXON Registered Developer

                          1 Reply Last reply Reply Quote 0
                          • O
                            Ogers
                            last edited by

                            Hello,
                            Tried that by still the same :/.

                            Thanks,
                            Ogers.

                            1 Reply Last reply Reply Quote 0
                            • ferdinandF
                              ferdinand
                              last edited by

                              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

                              MAXON SDK Specialist
                              developers.maxon.net

                              1 Reply Last reply Reply Quote 0
                              • O
                                Ogers
                                last edited by Ogers

                                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

                                1 Reply Last reply Reply Quote 0
                                • ManuelM
                                  Manuel
                                  last edited by

                                  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

                                  MAXON SDK Specialist

                                  MAXON Registered Developer

                                  O 1 Reply Last reply Reply Quote 0
                                  • O
                                    Ogers @Manuel
                                    last edited by Ogers

                                    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

                                    1 Reply Last reply Reply Quote 0
                                    • ManuelM
                                      Manuel
                                      last edited by

                                      Hi,

                                      I tried again to reproduce this issue but i can't. Would be interesting to send us some of your code using our mailbox [email protected]

                                      Cheers,
                                      Manuel

                                      MAXON SDK Specialist

                                      MAXON Registered Developer

                                      1 Reply Last reply Reply Quote 0
                                      • ferdinandF
                                        ferdinand
                                        last edited by

                                        Hello @Ogers,

                                        without any further questions, we will consider this topic as solved by Monday, the 25th and flag it accordingly.

                                        Thank you for your understanding,
                                        Ferdinand

                                        MAXON SDK Specialist
                                        developers.maxon.net

                                        1 Reply Last reply Reply Quote 0
                                        • First post
                                          Last post