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

    CommandData.Message() implementation and Message concepts

    Cinema 4D SDK
    python 2023
    2
    4
    755
    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.
    • John_DoJ
      John_Do
      last edited by

      Hi,

      I'm writing my first plugin and I have a hard time understanding how to implement the Message method of the CommandData class.

      The command is straightforward: it creates new shaders and the corresponding widgets for the Corona Node Material Editor. So in essence all the command is doing is modifying BaseList2D objects (shaders and node widgets) which are parented to another BaseList2D object (the Node Editor view).
      In this case, using EventAdd() refresh the Node Editor view instantly and populate it with new nodes, shaders, and links. On the other hand, using C4DAtom.Message() gives partial results = broken objects and links. And for the Message method, I just don't get it, even after looking at the docs and the example plugins extensively.

      class AddFilterShader(c4d.plugins.CommandData):
      
          def Execute(self, doc):
      
      
              context = CoronaNodeContext()
              nodes = context.active_nodes
              active_view = context.active_view
      
      
              if nodes:
      
                  parent_shader = set()
      
                  for node in nodes:
                      # Get the shader represented by the node widget
                      node_shader = node[2001]
      
                      # Skip if its a material
                      if isinstance(node_shader, c4d.BaseMaterial):
                          c4d.StatusSetText("Can't run on a material")
                          continue
      
                      # Filter shader
                      filter = c4d.BaseShader(c4d.Xfilter)
                      filter[c4d.SLA_FILTER_TEXTURE] = node_shader
      
                      # We check for a parent node, if not
                      # we parent to the Corona Node shaderhook
                      parent = node_shader.GetUp()
                      if parent is None:
                          parent = node_shader.GetMain()
                      # Add the parent for further operations
                      parent_shader.add(parent)
      
                      # Look for the link in the parent
                      if isinstance(parent,c4d.BaseMaterial) or isinstance(parent, c4d.BaseShader) :
                          parent_bc = parent.GetDataInstance()
                          index = bc_get_id(parent_bc, node_shader)
                          parent_bc.SetLink(index, filter)
      
                      # Create a new widget for the shader
                      cnode = create_node_widget(filter)
      
                      # Insert shaders
                      node_shader.InsertUnder(filter)
      
                      if isinstance(parent,c4d.BaseMaterial):
                          parent.InsertShader(filter)
                      else:
                          filter.InsertUnder(parent)
      
                      # Insert widget
                      cnode.InsertUnder(active_view)
      
                      # Selection state
                      node.DelBit(c4d.BIT_ACTIVE)
                      cnode.SetBit(c4d.BIT_ACTIVE)
      
                      # Update the node and the shaders
                      # Doesn't work ? 
                      node.Message(c4d.MSG_UPDATE)
                      parent.Message(c4d.MSG_UPDATE)
      
              # Update the view
              # Doesn't work ? 
              active_view.Message(c4d.MSG_UPDATE)
      
              c4d.EventAdd()
      
              return True
      

      As you can see I tried the C4DAtom.Message() command inside the Execute method but it didn't get any better.

      So how should I use the Message method to get the same result as with EventAdd()? Or should I not bother and use the function? But even so, I'd like to understand the Message concept.

      Thanks !

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

        Hey @John_Do,

        Thank you for reaching out to us. I must point out that your question is out of scope of support because you are using Corona. Thrid party libraries are out of scope of support as declared in our Forum Procedures. Please understand that we cannot run, debug, or help you with foreign APIs.

        I'm writing my first plugin and I have a hard time understanding how to implement the Message method of the CommandData class.

        You do not implement any Message method in your code, and also not in particular CommandData.Message. I think you just misspoke a little, and meant to say use. But since this has been placed so prominently in the title and first sentence, I had to point it out.

        So how should I use the Message method to get the same result as with EventAdd()? Or should I not bother and use the function? But even so, I'd like to understand the Message concept.

        In general I would recommend to have a look at the Message Manual (which you might have already done), because there are quite a few misconceptions here.

        There are three main message streams in Cinema 4D:

        • Messages sent to scene elements such as objects, shaders, materials, etc. They are sent with C4DAtom.Message and .MultiMessage. These messages always concern only the node(s) they are being sent to and do stuff like "hey your parameter xyz has changed", "hey, your button uvw has been clicked", "hey, could you please update your icon?".
        • Messages sent to the core of Cinema 4D. They can be sent in multiple ways but the must prominent one is SendCoreMessage. These message are sent to the core of Cinema 4D but can also be received in a few other places like a MessageData plugin or a GeDialog. They convey information like "the scene just has been updated", "please redraw the whole UI", etc. I.e., broad information that concerns everyone.
        • The third category are GUI messages which are irreverent in this case.

        MSG_UPDATE is a scene element message which is sent to nodes when they have been manipulated and must update their internal data. A common example are point objects, you must send MSG_UPDATE to them when you have changed their points. But sending MSG_UPDATE is the exception, and you usually do not have to do it for pure parameter manipulation. Stacking shaders together does not require any MSG_UPDATE call. At least in our API, Chaos Group could be doing things differently with Corona (but that seems a bit unlikely).

        EventAdd is adds an item to the event queue in Cinema 4D and corresponds to the core message EVMSG_CHANGE. This mechanism primarily exists so that the GUI and other secondary systems can catch up with the primary data, the scene graph. You call it when you add for example a cube to scene. The cube will be part of your scene as soon as you call BaseDocument.InsertObject. But only when you call EventAdd, it will be reflected in the UI, have its caches built, etc.¹

        So, your question does not make too much sense, and all the MSG_UPDATE calls are likely not required in the first place.

        What is more likely going wrong here, is how you assemble your shader graph. For your first plugin, this little stretch of code is trying to do a lot all at once. And especially when I would run into problems, I would try to reduce that. In play comes here then how the Corona API works. It is probably best when you ask the Corona team for help with that.

        Cheers,
        Ferdinand

        ¹ An added event will not be carried out right away but when the event queue is resolved. I.e., having one EventAdd at the end of your operation is equivalent to having multiple calls in one operation. EventAdd will only be carried out once you give control back to Cinema 4D. CommandData plugins also call EventAdd on their own after their Execute ran, so you do not have to do it yourself.

        MAXON SDK Specialist
        developers.maxon.net

        John_DoJ 1 Reply Last reply Reply Quote 0
        • John_DoJ
          John_Do @ferdinand
          last edited by John_Do

          Hi @ferdinand,

          As always thank you very much for the detailed answer. Please mind that I'm not a seasoned programmer at all, not even a programmer. That doesn't excuse anything, but there are likely many mistakes in my messages.

          Thank you for reaching out to us. I must point out that your question is out of scope of support because you are using Corona. Thrid party libraries are out of scope of support as declared in our Forum Procedures. Please understand that we cannot run, debug, or help you with foreign APIs.

          I know that Corona is out of the scope of this forum since I've already posted some questions about it, and we already discussed about it. But the objects and data particular to Corona are based on the class and methods of the Cinema 4D API as far as I can tell. If it is still out of scope let me know. I've already mailed the Corona devs and had a bit of help few years ago but that's all. I made a diagram recently that shows how the Node Editor is integrated through the API.

          Messages
          Thanks for the explanation regarding messages, but I still don't get it. I think it doesn't make sense to me that I have to tell Cinema something has changed since something has really changed ( = through the interpreted Python code) from my point of view when executing the code. My lack of programming background maybe doesn't help me here.
          So in most cases, messages are used to trigger UI events / updates?
          How do I know if I have to send a message or not to an object to update it ( or it's representation in the current document )?
          How should I leverage CommandData.Message() and what is the difference with calling C4DAtom.Message / SendCoreMessage in Execute ? I'm really puzzled here.

          What is more likely going wrong here, is how you assemble your shader graph. For your first plugin, this little stretch of code is trying to do a lot all at once. And especially when I would run into problems, I would try to reduce that. In play comes here then how the Corona API works. It is probably best when you ask the Corona team for help with that.

          It's quite possible, but the thing is the code do exactly what I want (inserting a filter shader on top of any selected shader). But maybe I take too many shorcuts or that things are done in a unorthodox way. What would be the proper way to do this ? Split what's happening in Execute into multiple methods ?

          ¹ An added event will not be carried out right away but when the event queue is resolved. I.e., having one EventAdd at the end of your operation is equivalent to having multiple calls in one operation. EventAdd will only be carried out once you give control back to Cinema 4D.

          Is it a problem ? The command is really simple and linear, really it's just a script wrapped in a class' method.

          CommandData plugins also call EventAdd on their own after their Execute ran, so you do not have to do it yourself.

          I suspected that but I don't get the same result if I comment out EventAdd. Ui doesn't update and links are missing on the node widgets. So what's happening here ?

          alt text
          alt text
          alt text

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

            Hey @John_Do,

            you can add as many EventAdd as you want, it does not really hurt. All that will happen is that Cinema 4D will see that there is already an event and then do nothing. Technically speaking, it can make sense to have multiple EventAdd calls in a row - when you set the flags in c4d.EventAdd(flags=EVENT_NONE) as your call might then add a flag to the next upcoming event.

            It is a bit odd that you need an EventAdd at the end of your CommandData:Execute, but your screen grab clearly shows that it is necessary, so just keep it there. Maybe I also just misremembered something there.

            But I am a bit confused to what is now concretely not working.

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

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