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

    Create Gradient Attribute for Nodes Programmatically

    Cinema 4D SDK
    c++ r25
    2
    12
    1.7k
    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.
    • ManuelM
      Manuel
      last edited by

      hi,

      the ID you are looking for is maxon::DESCRIPTION::UI::NET::MAXON::UI::VARIADICPORT::COMPLEXCUSTOMUI but i was not able to make it work, even if i defined the port as variadic and add the commands. So I asked the dev what i am missing.

      Cheers,
      Manuel

      MAXON SDK Specialist

      MAXON Registered Developer

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

        Hello,
        Thanks for the reply. After setting the right ID as you said, I was able to get the UI look I was looking for, but same as you I can't make it work properly. Here is the part of the code I have used for this.

        maxon::BaseArray<maxon::Id> var_commands;
        
        var_commands.Append(maxon::Id("addvariadicport"));
        var_commands.Append(maxon::Id("removevariadicport"));
        
        node.Set(maxon::DATADESCRIPTION_CATEGORY_DATA, maxon::DESCRIPTION::DATA::BASE::DATATYPE, maxon::Id("net.maxon.render.portbundle.gradient")) iferr_return;
        
        node.Set(maxon::DATADESCRIPTION_CATEGORY_UI, maxon::DESCRIPTION::UI::BASE::GUITYPEID, maxon::Id("net.maxon.ui.variadicport")) iferr_return;
        
        node.Set(maxon::DATADESCRIPTION_CATEGORY_DATA, maxon::DESCRIPTION::DATA::BASE::ISVARIADIC, TRUE) iferr_return;
        
        node.Set(maxon::DATADESCRIPTION_CATEGORY_DATA, maxon::DESCRIPTION::DATA::BASE::VARIADICCOUNT, 2) iferr_return;
        
        node.Set(maxon::DATADESCRIPTION_CATEGORY_DATA, maxon::DESCRIPTION::DATA::BASE::VARIADICCOMMANDS, var_commands) iferr_return;
        
        node.Set(maxon::DATADESCRIPTION_CATEGORY_UI, maxon::DESCRIPTION::UI::NET::MAXON::UI::VARIADICPORT::COMPLEXCUSTOMUI, 200001011);
        
        1 Reply Last reply Reply Quote 0
        • O
          Ogers
          last edited by

          Hello?
          Is there any news about this one yet?
          868391ba-efe4-4226-b077-5ab64582f925-image.png

          So far with the code above, I can only get to this part, but as you can see the gradient's colours are not showing on the UI.

          Also, how can I get the values set for each knot (colour, interpolation, position etc.)
          Do I have to build my own gradient data type to make it work or does the built-in gradient offers all these?

          Thank you.

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

            hi,

            there are some progresse yes, but it is still not working correctly.

            you cannot define the value of the knot while you are defining the node itself. That is a limitation of the variadic ports.

            you must define the values after using the DescriptionFinalizers with the exact same ID you used to register the node itself. You will have access to the knots and will be able to define the values. The problem is that the gradient remains black i still don't have the answer for that from our dev.

            MAXON_DECLARATION_REGISTER(nodes::DescriptionFinalizers, "net.maxon.node.procedural.firstexample")
            {
            	return nodes::DescriptionFinalizers::EntryType([](const nodes::MutableRoot& root)-> Result<void>
            		{
            			iferr_scope;
            			nodes::MutablePort knot0 = root.GetInputs().FindPort(PATTERN::NODE::GENERATOR::GRADIENT::GRADIENT).FindPort(Id("_0")) iferr_return;
            			if (knot0)
            			{
            				knot0.FindPort(RENDER::PORTBUNDLE::GRADIENT::COLOR).SetDefaultValue(ColorA(0, 0, 1, 1)) iferr_return;
            				knot0.FindPort(RENDER::PORTBUNDLE::GRADIENT::POSITION).SetDefaultValue(Float(0.0)) iferr_return;
            			}
            			return OK;
            		});
            }
            

            I've changed the title of the thread from pragmatically to programmatically so people will find this thread when using our search engine.

            MAXON SDK Specialist

            MAXON Registered Developer

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

              Hello,
              Thanks for the update.

              Regarding the ports you have used, are they already defined as ports, or they should be created too?
              At the moment these ports PATTERN::NODE::GENERATOR::GRADIENT::GRADIENT, RENDER::PORTBUNDLE::GRADIENT::COLOR and RENDER::PORTBUNDLE::GRADIENT::POSITION cannot be accessed. I tried searching all files but could not find them.

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

                Hi,
                Those ports are part of the Gradient DataType.

                just like you did

                node.Set(maxon::DATADESCRIPTION_CATEGORY_DATA, maxon::DESCRIPTION::DATA::BASE::DATATYPE, maxon::Id("net.maxon.render.portbundle.gradient")) iferr_return;
                

                I'm doing

                b.Set(DATADESCRIPTION_CATEGORY_DATA, DESCRIPTION::DATA::BASE::DATATYPE, maxon::RENDER::PORTBUNDLE::GRADIENT::GetId()) iferr_return;
                

                The problem is to find the right information and the right symbol to use. The resource editor helps a lot. I usually presse the first top left dropdown arrow and start typing "gradient" to find a bit faster the Data Type I'm looking for. This will search among all databases.
                02ad5984-b022-4d7a-ac39-3eb6bc9aaca9-image.png

                From there you can find the file where those symbols are exported
                4488b14f-6bc4-456b-99d1-398bc00b5c00-image.png

                The ID are in the render.framework -> patternnodes.h and you have the equivalent of the resource editor.

                6286404a-0710-418d-ad3e-4e5dc5610dfd-image.png

                You can also find the file by search the ID itself: net.maxon.render.portbundle.gradient

                As the gradient port, is a Variadic Port composed by a Port Bundle the first Knot will have the id "_0"
                that is why I'm using

                root.GetInputs().FindPort(PATTERN::NODE::GENERATOR::GRADIENT::GRADIENT).FindPort(Id("_0"))
                

                And i know that this Port Bundle is composed of several port that i can find with the right ID.

                the code I'm using to create the gradient port so far.

                                b.BeginPort(PORT_DIR::INPUT, Id("gradient")) iferr_return;
                		b.Set(DATADESCRIPTION_CATEGORY_DATA, DESCRIPTION::DATA::BASE::DATATYPE, maxon::RENDER::PORTBUNDLE::GRADIENT::GetId()) iferr_return;
                		b.Set(DATADESCRIPTION_CATEGORY_DATA, DESCRIPTION::DATA::BASE::ISVARIADIC, true) iferr_return;
                		b.Set(DATADESCRIPTION_CATEGORY_DATA, maxon::DESCRIPTION::DATA::BASE::VARIADICCOUNT, 2) iferr_return;
                
                		Array<Id> commands;
                		commands.Append(Id("addvariadicport")) iferr_return;
                		commands.Append(Id("removevariadicport")) iferr_return;
                		b.Set(DATADESCRIPTION_CATEGORY_DATA, DESCRIPTION::DATA::BASE::COMMANDS, std::move(commands)) iferr_return;
                		
                		b.Set(DATADESCRIPTION_CATEGORY_UI, DESCRIPTION::UI::BASE::GROUPID, NODE::BASE::GROUP_INPUTS) iferr_return;
                		b.Set(DATADESCRIPTION_CATEGORY_UI, DESCRIPTION::UI::BASE::GUITYPEID, Id("net.maxon.ui.variadicport")) iferr_return;
                		b.Set(DATADESCRIPTION_CATEGORY_UI, maxon::DESCRIPTION::UI::NET::MAXON::UI::VARIADICPORT::COMPLEXCUSTOMUI, 200001011) iferr_return;
                		b.Set(LANGUAGE_ENGLISH_ID, DESCRIPTION::STRING::BASE::TRANSLATEDSTRING, "gradient"_s) iferr_return;
                		b.EndPort() iferr_return;
                
                

                MAXON SDK Specialist

                MAXON Registered Developer

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

                  Hello.
                  It's weird as I don't have a render.framework on my machine, although the include file points there on my side as well.. Could this be part of S26 only?
                  I have also referred to the resource editor to build my own gradient where I found everything I needed except for the file which is missing.

                  df669c9d-fb0b-4373-9ed0-4ede7ed822a3-image.png

                  Also as you can notice from my previous messages where I showed how I was building the gradient, you can see that the only difference is that I set the datatype by typing the ID manually ("net.maxon.render.portbundle.gradient"), and all that because I could not find the right file.

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

                    ha,

                    proof that I am an idiot, if we need any, the render.framework is private xD

                    mib.gif

                    Cheers,
                    Manuel

                    MAXON SDK Specialist

                    MAXON Registered Developer

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

                      Haha, I forget even more important stuff sometimes 🙂
                      Btw is there any workaround for this? Or will it be public in the future?

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

                        you must use the ID themself and not the symbols

                        MAXON SDK Specialist

                        MAXON Registered Developer

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

                          hi,

                          Sorry for the late reply, but this needed investigation and test from my side and communication between me and the dev.

                          You can create the NodeTemplate that way. I am using the class GradientWorkaround to create the template.
                          NodesLib::BuildNodeFromDescription must be called with false set to not create the dependency wires.

                          		
                          		NodeTemplate t = NodesLib::CreateLazyTemplate(firstNodeId,
                          			[firstNodeId]() -> Result<NodeTemplate>
                          			{
                          				iferr_scope;
                          				
                          				maxon::nodes::NodeTemplate templ = maxon::nodes::NodesLib::BuildNodeFromDescription(firstNodeId, CoreNodesNodeSystemClass(), false) iferr_return;
                          				templ = GradientWorkaround::CreateInit(templ) iferr_return;
                          				return templ;
                          
                          			}, CoreNodesNodeSystemClass()) iferr_return;
                          

                          The class itself look like this. We can create a gradient node inside the usernode itself and connect the ports. This is done inside the function InstantiateImpl.

                          class GradientWorkaround : public maxon::Component<GradientWorkaround, maxon::nodes::NodeTemplateInterface>
                          {
                          	MAXON_COMPONENT(NORMAL, maxon::nodes::NodeTemplateBaseClass);
                          
                          public:
                          	maxon::ResultOk<void> Init(const maxon::nodes::NodeTemplate& templ)
                           	{
                          		_wrapped = templ;
                          		return maxon::OK;
                          	}
                          
                          	MAXON_METHOD maxon::Result<Bool> SupportsImpl(const maxon::nodes::NodeSystemClass& cls) const
                          	{
                          		return cls.IsSubclassOf(CoreNodesNodeSystemClass());
                          	}
                          
                          	MAXON_METHOD maxon::Result<maxon::nodes::NodeSystem> InstantiateImpl(const maxon::nodes::InstantiationTrace& parent, const maxon::nodes::TemplateArguments& args) const
                          	{
                          		iferr_scope;
                          
                          		maxon::nodes::NodeSystem sys = _wrapped.Instantiate(parent, args) iferr_return;
                          
                          		maxon::nodes::MutableRoot root = sys.BeginInstantiationModification(self) iferr_return;
                          		// Asset ID : net.maxon.pattern.node.generator.gradient
                          
                          		const AssetRepositoryRef& repository = AssetInterface::GetBuiltinRepository();
                          		nodes::NodeTemplate gradient = nodes::NodesLib::LoadTemplate(repository, Id("net.maxon.pattern.node.generator.gradient")) iferr_return;
                          		MutableNode gradientNode = root.AddChild(Id("Gradient"), gradient) iferr_return;
                          		const maxon::nodes::MutablePort output = root.GetOutputs().FindPort(maxon::Id("testoutput")) iferr_return;
                          		const maxon::nodes::MutablePort gradientPort = root.GetInputs().FindPort(maxon::Id("gradient")) iferr_return;
                          
                          		const maxon::nodes::MutablePort gradientResult = gradientNode.GetOutputs().FindPort(maxon::Id("result")) iferr_return;
                          		const maxon::nodes::MutablePort gradientGradient = gradientNode.GetInputs().FindPort(maxon::Id("gradient")) iferr_return;
                          
                          		gradientResult.Connect(output) iferr_return;
                          		gradientPort.Connect(gradientGradient) iferr_return;
                          	
                          
                          
                          
                          		sys = root.EndModification() iferr_return;
                          		return sys;
                          	}
                          
                          private:
                          	maxon::nodes::NodeTemplate _wrapped;
                          };
                          MAXON_COMPONENT_CLASS_REGISTER(GradientWorkaround, "net.maxonexample.nodes.class.gradientworkaround");
                          

                          It is just like having a group of nodes and propagated ports.

                          Cheers,
                          Manuel

                          MAXON SDK Specialist

                          MAXON Registered Developer

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