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

    Getting an effective value of an enum from a GraphNode

    Cinema 4D SDK
    c++ 2025
    3
    6
    333
    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.
    • E
      ECHekman
      last edited by ECHekman

      I am trying to wrap my mind around the GraphNode api, and starting to get it a bit (i think)
      However, im trying to retrieve the type of a RS Bump Map, however im having a bit of trouble.
      I can get the bump map node itself, but I cannot seem to get the effective value that is in its port.
      This is my latest attempt, it fails at typeNode.GetValue(maxon::EffectivePortValue,

      ifnoerr(auto & bumpRes = inputs.FindChild(maxon::Id("com.redshift3d.redshift4c4d.nodes.core.standardmaterial.bump_input")))
      {
          maxon::GraphNode bumpNode = getConnectedNode(bumpRes, maxon::NODE_KIND::NODE);
      
          auto assetId = bumpNode.GetValue(maxon::nodes::AssetId).GetValueOrDefault().GetValue();
          const maxon::Id& nodeId = assetId.Get<0>();
      
          if (nodeId == maxon::Id("com.redshift3d.redshift4c4d.nodes.core.bumpmap"))
          {
              ifnoerr(auto & typeNode = bumpNode.GetInputs().FindChild(maxon::Id("com.redshift3d.redshift4c4d.nodes.core.bumpmap.inputType")))
              {
                  maxon::ConstDataPtr inputData2 = typeNode.GetValue(maxon::EffectivePortValue, maxon::DataType::DefaultValue()) iferr_return;
      
                  auto type = inputData2.GetType();
                  auto typeId = type.GetId();
      
              }
          }
      }
      
      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @ECHekman
        last edited by ferdinand

        Hey @ECHekman,

        The value of a port should be accessed with GraphNodeFunctions.GetPortValue or GraphNodeFunctions::GetEffectivePortValue. Usually, the former is fine, and only for some specialty cases where the actual port data does not reflect what the user sees, and you want to access the user facing value, you have to use the latter.

        What you are doing there, is the old access via GetValue which is primarily meant for attribute access these days. But it should still work. As always for non-executable code examples, it is hard to judge what is going wrong there for you. But note that checks like these are not doing what you probably think they are doing:

        ifnoerr(auto & typeNode = bumpNode.GetInputs().FindChild(maxon::Id("...")))
        {
          ...
        }
        

        I personally would use auto only sparingly and especially with the added line-break and the unfortunate placement of the reference operator had to look twice what you are doing here. But that is just personal taste, even if we wrote it like this in a more Maxonic way, it would not do what you probably think it does.

        void SomeFunction()
        {
          iferr_scope_handler
          {
            // DiagnosticOutput("@ failed with error: @", MAXON_FUNCTIONNAME, err);
            return;
          };
        
          const maxon::GraphNode& someInput = bumpNode.GetInputs().FindChild(maxon::Id("...")) iferr_return;
          if (!someInput)
            return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Input node not found"_s);
        }
        

        The return value of FindChild will always be a maxon::GraphNode, unless an internal error occurs. A non-valid node path will not raise an error but return an empty and with that invalid node.

        if (someInput.IsValid())
        {
          // Do something with someInput
        }
        else
        {
          // Handle the case where the input node is not found
        }
        

        Cheers,
        Ferdinand

        edit: It is good to see that you are moving towards the Maxon API. But when I see things like this:

        maxon::GraphNode bumpNode = getConnectedNode(bumpRes, maxon::NODE_KIND::NODE);
        

        I am not so sure you are on the right track. When you write functions within the Maxon API, you should use our error handling. When you terminate the error handling within your functions, you will probably not have a good time. I.e., the signature should be this

        getConnectedNode(const GraphNode& input, const NODE_KIND kind) -> Result<GraphNode>
        

        and not -> GraphNode& or -> GraphNode. Note that there is also GraphModelHelperInterface which likely already implements what you are implementing there. See the GraphModelInterface Manual for an overview.

        MAXON SDK Specialist
        developers.maxon.net

        DunhouD E 2 Replies Last reply Reply Quote 0
        • DunhouD
          Dunhou @ferdinand
          last edited by

          Hey @ferdinand,

          Sorry to interject here, I'm just curious why it's recommended to put the return type at the end (of course, I prefer this Python-like writing style), but as far as I know, this is a relatively old-fashioned writing style (c++11), and the code I observed during my learning process was all of the predecessor types, this is quite confusing for a C++ beginner like me.

          Cheers~
          DunHou

          Result<GraphNode> getConnectedNode(const GraphNode& input, const NODE_KIND kind);```

          https://boghma.com
          https://github.com/DunHouGo

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

            Hey @Dunhou ,

            That is a good question, although slightly off-topic. What I used there is called trailing return type. It was introduced in C++11, specifically in the context of lambda expressions. The syntax allows you to specify the return type after the function parameters, which can be useful in certain situations, especially when dealing with complex types or when the return type depends on template parameters. It is functionally identical to the leading style. Python is deeply related to C and C++, and many of its conventions, concepts, and features are rooted in C/C++ programming practices.

            I just went here instinctively for this notation, as it is somewhat common to use it when you talk about a function in terms of its signature, rather than its concrete implementation. It is not a requirement, and you can write the function both ways, we in fact - except for lambda expressions - do not use this notation in our codebase.

            
            // This is the same as
            Sum1(const int a, const int b) -> int  { return a + b; }
            
            // as this (except for the name so that they can coexist).
            int Sum2(const int a, const int b) { return a + b; }
            
            // But we need the trailing notation for lambdas, of which modern Maxon API Cinema 4D makes heavy use.
            void Foo() {
                auto add = [](const int a, const int b) -> int { return a + b; };
                cout << add(1, 2) << std::endl;
            }
            

            So, to recap, we recommend to use Result<T> as the return type for everything that comes into contact with Maxon API error handling, so that you can propagate errors. We do not make any strong recommendations regarding return type conventions. We slightly recommend the traditional leading return type notation, but it is not a requirement.

            Cheers
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 1
            • E
              ECHekman @ferdinand
              last edited by ECHekman

              @ferdinand

              I wrote a whole a4 reply about your suggestions about maxon style, error handling and my opinions about the sparse documentation and the new api in general, but i deleted it because im just so baffled by how hard it is to just get a connected node, and then get a single enum value from a node that I literally stopped caring about code architecture and style. I have tried so many different thing now im at a loss at what is supposed to be the way to do this?

              All I want to do:
              1: Get the bumpmap that is connected to the standard material node. (com.redshift3d.redshift4c4d.nodes.core.standardmaterial.bump_input)
              2: Get the inputType enum value of the bumpmap node (com.redshift3d.redshift4c4d.nodes.core.bumpmap.inputType)

              I have tried so many different things im not even going to post code. I have read the sdk manual. I have rooted through the examples projects. I have read all the code comments in the headers. Never in my life have i spent so much effort doing something this conceptually simple. What am i supposed to do here?

              I just want this value
              747d99fc-52c2-4950-89a4-3fdc242a363d-image.png

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

                Hey @ECHekman,

                I sense there is some frustration, but I am not sure telling us how bad our API is will get us anywhere. Yes, the Nodes API ist not trivial, but you are only on the using part (which is not that hard to understand) not the implementation part (which is the trickly one). There are multiple render engine vendors who took that hurdle. I already answered your questions, and as always you will not see source code from us, unless you give us executable code in the first place upon which we can build, or we deem a subject new.

                I often bend these rules a bit where it makes sense to meet our customers and third parties halfway. But you cannot just throw a snippet at us and then expect us to invent everything around it and then fix that for you. Executable code makes a difference as lined out in our support procedures. My hunch would be that your getConnectedNode does not work because you do not check if your nodes are valid.

                You can get the value of a port with GetPortValue or GetEffectivePortValue. What you are doing with GetValue and EffectivePortValue is the old way but will still work.

                // Redshift expresses a lot of its enums as strings and not as ints (did not check if that is here the case).
                const String value = myPort.GetEffectivePortValue<String>().GetOrDefault() iferr_return;
                

                And as lined out before, what your function is trying to do, can likely be done via GraphModelHelper too, e.g., with GraphModelHelper::GetDirectPredecessors. An alternative and more manual approach would be using GraphNode.GetInnerNodes and GraphNode.GetConnections.

                And as always, I am not really looking for a discussion about what you or I would consider a good API. I am telling you that you will be in a world of hurt when you terminate your errors everywhere as you did in your code. Your code will then just silently fail without you knowing why. So, I gave you an example on how to use our error handling.

                Cheers,
                Ferdinand

                MAXON SDK Specialist
                developers.maxon.net

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