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
    • Register
    • Login

    Dynamic elements in a CYCLE, CYCLE empty after loading document?

    Cinema 4D SDK
    c++ r20 r21 s22 r23
    2
    9
    1.1k
    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.
    • fwilleke80F
      fwilleke80
      last edited by fwilleke80

      Hello,

      In a ShaderData, I am using GetDDescription() to add elements to a LONG CYCLE description using this code:

      struct CycleElementData
      {
      	Int32 elementId; ///< Element id
      	maxon::String name; ///< Element name
      
      	/// \brief Construct from element ID and name
      	CycleElementData(Int32 t_elementId, const maxon::String& t_name) : elementId(t_elementId), name(t_name)
      	{}
      
      	/// \brief Copy constuctor
      	CycleElementData(const CycleElementData& src) : elementId(src.elementId), name(src.name)
      	{}
      };
      
      Bool AddCycleElements(GeListNode* node, Description* descr, Int32 myDescId, maxon::BaseArray<CycleElementData>& elements, Bool addSeparator)
      {
          const DescID* const singleId = descr->GetSingleDescID();
          if (IsSingleID(myDescId, singleId))
          {
              BaseContainer* descSettings = descr->GetParameterI(myDescId, nullptr);
              if (!descSettings)
                  return false;
      
              // Build BaseContainer with cycle elements
              BaseContainer cycleElements = descSettings->GetContainer(DESC_CYCLE);
      
              if (addSeparator)
                  cycleElements.SetString(-1, ""_s);
      
              for (maxon::BaseArray<CycleElementData>::ConstIterator it = elements.Begin(); it != elements.End(); ++it)
              {
                  const CycleElementData& currentElement = *it;
                  cycleElements.SetString(currentElement.elementId, currentElement.name);
              }
      
              descSettings->SetContainer(DESC_CYCLE, cycleElements);
      
              return true;
          }
          return true;
      }
      
      
      // Later...
      maxon::BaseArray<CycleElementData> shaderModes;
      shaderModes.Append(CycleElementData(123456, "Some mode"_s)) iferr_ignore();
      shaderModes.Append(CycleElementData(654321, "Some other mode"_s)) iferr_ignore();
      
      AddCycleElements(node, description, MYSHADER_MODES, shaderModes, true);
      

      So far, it seems to work fine. The additional entries appear in the LONG CYCLE in the shader's UI. I can also select them, and it works.

      However, when I save the scene, and then load it again, nothing is selected in the LONG CYCLE. I have to re-select the desired entry again. I guess, this is because the additional entries don't exist yet, when the scene is loaded, and are added later when the shader's GetDDescription() is called. So what might be the solution?

      Greetings & thanks in advance for help,
      Frank

      www.frankwilleke.de
      Only asking personal code questions here.

      1 Reply Last reply Reply Quote 0
      • fwilleke80F
        fwilleke80
        last edited by

        OK, after taking a little break and then getting back to the code, it magically works. I didn't change anything. So, I change my question to: Are there any pitfalls with dynamic CYCLE elements, or dynamic description elements in general, when loading a scene?

        www.frankwilleke.de
        Only asking personal code questions here.

        1 Reply Last reply Reply Quote 0
        • M
          m_adam
          last edited by

          Hi Franck, first of all, Happy new year!
          Glad that it somehow works.

          The biggest issue is we don't have all your code, so we need to do some guesswork.

          We don't know when ad how you are populating your BaseArray. Since it may be based on some other stuff from the scene, these other things may not be loaded yet.

          We don't have your GetDDescription code, but be sure that LoadDescription is called and you return DESCFLAGS_DESC::LOADED Overwise your change may simply be overwritten. For more information see Description Manual - Access.

          Within the GetDDescription, once you populated the cycle, be sure also to call SetParameter to actually set the value (be sure to store the currently active value by overriding NodeData::Write/NodeDataRead)

          Hope this help,
          Cheers,
          Maxime.

          MAXON SDK Specialist

          Development Blog, MAXON Registered Developer

          1 Reply Last reply Reply Quote 0
          • fwilleke80F
            fwilleke80
            last edited by fwilleke80

            Hi Adam, happy new year to you, too!

            Since it works now, for some reason, I am pretty happy with what I have. However, since it might interest other plugin developers, I'll share more code. Maybe you have some tipps about improvements or potentially dangerous stuff, too.

            The idea is that the shader has a LINK field where the user can link an object (which is also part of my plugin). The object can (but doesn't have to) provide a list of "custom outputs" that will be added to the shader's CYCLE. In the screenshot below it's the "Difference Map".

            When a rendering is started, the shader will request the according data from the linked object during InitRender(). But that's not part of this thread 😉

            Screenshot 2021-01-26 at 11.42.45.png

            The shader's GetDDescription():

            Bool TerrainOperatorShader::GetDDescription(GeListNode *node, Description *description, DESCFLAGS_DESC &flags)
            {
            	iferr_scope_handler
            	{
            		GePrint(err.GetMessage());
            		return false;
            	};
            
            	if (!description->LoadDescription(node->GetType()))
            		return false;
            	flags |= DESCFLAGS_DESC::LOADED;
            
            	BaseDocument* doc = node->GetDocument();
            	const BaseContainer& dataRef = static_cast<BaseShader*>(node)->GetDataInstanceRef();
            	
            	// Hide or show attributes, depending on shader mode
            	const Bool slopeMode = dataRef.GetInt32(XTERRAINOPERATORSHADER_DATA) == XTERRAINOPERATORSHADER_DATA_SLOPE;
            	TF4D::GUI::ShowDescription(node, description, XTERRAINOPERATORSHADER_SLOPE_DIRECTION_ENABLE, slopeMode);
            	TF4D::GUI::ShowDescription(node, description, XTERRAINOPERATORSHADER_SLOPE_DIRECTION, slopeMode);
            
            	// Get linked object
            	BaseObject *linkedObject = dataRef.GetObjectLink(XTERRAINOPERATORSHADER_OPERATORLINK, doc);
            	if (linkedObject)
            	{
            		// Get linked object's NodeData
            		TF4D::BaseTerrainOperatorData* linkedOperator = linkedObject->GetNodeData<TF4D::BaseTerrainOperatorData>();
            
            		// Get list of custom outputs (these are the elements to add to the CYCLE)
            		maxon::BaseArray<TF4D::GUI::CycleElementData> customOutputs;
            		if (linkedOperator->GetCustomOperatorOutputs(customOutputs))
            		{
            			if (!TF4D::GUI::AddCycleElements(node, description, XTERRAINOPERATORSHADER_DATA, customOutputs, true))
            				iferr_throw(maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not add LONG CYCLE elements!"_s));
            		}
            	}
            
            	return SUPER::GetDDescription(node, description, flags);
            }
            

            The linked object's NodeData's GetCustomOperatorOutputs():

            Bool ErosionOperator::GetCustomOperatorOutputs(maxon::BaseArray<TF4D::GUI::CycleElementData>& customOperatorOutputs) const
            {
            	iferr_scope_handler
            	{
            		GePrint(err.GetMessage());
            		return false;
            	};
            
            	customOperatorOutputs.Reset();
            
            	// GetCustomOutputName() simply returns a maxon::String
            	customOperatorOutputs.Append(TF4D::GUI::CycleElementData(TF4D_CUSTOMOUTPUT_EROSION_DIFFERENCE, GetCustomOutputName(TF4D_CUSTOMOUTPUT_EROSION_DIFFERENCE))) iferr_return;
            
            	return true;
            }
            

            Cheers,
            Frank

            Ah, damn. Now I've spoiled that I'm working on erosion for Terraform4D 😄

            www.frankwilleke.de
            Only asking personal code questions here.

            1 Reply Last reply Reply Quote 2
            • fwilleke80F
              fwilleke80
              last edited by fwilleke80

              @m_adam said in Dynamic elements in a CYCLE, CYCLE empty after loading document?:

              Within the GetDDescription, once you populated the cycle, be sure also to call SetParameter to actually set the value (be sure to store the currently active value by overriding NodeData::Write/NodeDataRead)

              What exactly do you mean? Why should I do that?

              Do be clear: Just because the user links an object that adds extra items to the CYCLE doesn't mean any of those extra items should be automatically selected. They should just be available in the CYCLE.

              www.frankwilleke.de
              Only asking personal code questions here.

              1 Reply Last reply Reply Quote 0
              • M
                m_adam
                last edited by m_adam

                @fwilleke80 said in Dynamic elements in a CYCLE, CYCLE empty after loading document?:

                However, when I save the scene, and then load it again, nothing is selected in the LONG CYCLE.

                What might happens is that the value is correctly set, but since the Cycle is not yet populated, the value is then reset.
                So that's why a SetParameter, may be required once cycle data are loaded.

                Cheers,
                Maxime.

                MAXON SDK Specialist

                Development Blog, MAXON Registered Developer

                1 Reply Last reply Reply Quote 0
                • fwilleke80F
                  fwilleke80
                  last edited by

                  Ah, ok, yes that sounds reasonable. When would I do that? In Read(), Write(), and CopyTo()? And what exactly would I do? Simple getting the value and immediately setting it again probably wouldn’t change anything.

                  www.frankwilleke.de
                  Only asking personal code questions here.

                  1 Reply Last reply Reply Quote 0
                  • M
                    m_adam
                    last edited by

                    Hi sorry for the long waiting.

                    As demonstrated in the code snippet from Description - Iterate. I think in your GetDDescription, once the description is saved you should call SetParameter with the correct value.
                    This value can only be known by yourself. So you have to store the cycle value within the Hyperfile by overriding Write. Then when Cinema 4D loads the object the Read is called, here you can save in the previous value saved for the cycle. And finally in the GetDDescription, since Read would have been already called, you can call SetParameter with the correct value.

                    Hope this clarifies a bit, and good work on Terraform looks solid! 🙂
                    Cheers,
                    Maxime.

                    MAXON SDK Specialist

                    Development Blog, MAXON Registered Developer

                    1 Reply Last reply Reply Quote 0
                    • fwilleke80F
                      fwilleke80
                      last edited by

                      Thanks, Maxime! Looks like I have it running flawlessly now.

                      good work on Terraform looks solid!
                      Thank you! It is pretty solid, yes 🙂

                      Cheers & happy Easter,
                      Frank

                      www.frankwilleke.de
                      Only asking personal code questions here.

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