Intercepting Messages of another object [SOLVED]



  • On 14/02/2015 at 09:53, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   16 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    I have a TagData plugin. It reads and sets its input object's coordinates bidirectionally.

    If I move object A in the viewport or in its Attribute Manager, my tag changes object B's coords.
    If I move object B, the tag changes object A's coords.

    The problem is when I attempt to undo. The only object that is given an undo is the object I moved in the viewport/AM. Any object whose coords I change via my tag's Execute() has no undo.

    My plan is to add undo steps to all the objects whose coords were affected. But I don't think it's safe to do so in Execute() since this is called for every event update.

    There's the message MSG_DESCRIPTION_USERINTERACTION_END that's sent when a user stops dragging an object. I was hoping I could tap into this to create an undo state before I move my objects.

    Is it possible for my tag to read what goes through object A's or object B's Message() method?



  • On 16/02/2015 at 10:24, xxxxxxxx wrote:

    Hi Gene,

    Other object's message can be intercepted with event notifications.
    The 3 methods are defined in BaseList2D: AddEventNotification/RemoveEventNotification/FindEventNotification.

    These methods and related symbols are currently private but can be used externally and will be documented soon.
    bl for the 3 methods is the BaseList2D to add/remove/find a notification.
    In your case NOTIFY_EVENT_MESSAGE is the eventid to filter another object's messages.
    flags parameter can be just passed NOTIFY_EVENT_FLAG_0.

    When a notification is added for a base list, the notification messages are passed to the NodeData Message(). The message ID is MSG_NOTIFY_EVENT and the message struct is NotifyEventData.



  • On 16/02/2015 at 10:42, xxxxxxxx wrote:

    Awesome. I'll try those out right now.

    Thanks Yannick. :)

    EDIT:

    Btw, do you have some example code with how to use it? I don't know where I should be calling those methods...



  • On 17/02/2015 at 11:29, xxxxxxxx wrote:

    Hi Gene,

    I added a LINK parameter to the description of LookAtCamera SDK example. After this parameter is set I call AddEventNotification().
    It may be needed to remove the notification for a previous linked object before a new one is added. I don't do that in the code example.
    AddEventNotification() has to be invoked on the base list to receive the notifications for.
    The first parameter of AddEventNotification() is the base list that receives the notifications.
    The notifications are then sent to Message() with MSG_NOTIFY_EVENT id.

    To watch undo notifications pass NOTIFY_EVENT_UNDO.
    Note it's possible to call AddEventNotification() several times with different event types for the same base list.

    Here is the example code:

    Bool LookAtCamera::Message(GeListNode* node, Int32 type, void* data)
    {
      BaseTag*       tag = (BaseTag* )node;
      BaseContainer* bc  = tag->GetDataInstance();
      BaseDocument*  doc = tag->GetDocument();
      
      switch (type)
      {
        case MSG_DESCRIPTION_POSTSETPARAMETER:
          if (data)
          {
            DescriptionPostSetValue* descpsv = (DescriptionPostSetValue* )data;
            if (descpsv==nullptr) break;
            
            if ((*descpsv->descid)[0].id==LOOKATCAMERAEXP_LINK)
            {
              BaseLink* link = bc->GetBaseLink(LOOKATCAMERAEXP_LINK);
              if (link)
              {
                BaseList2D* bl = link->GetLink(doc);
                BaseObject* op = tag->GetObject();
                if (bl && op)
                {
                  if (!bl->FindEventNotification(doc, tag, NOTIFY_EVENT_MESSAGE))
                  {
                    Bool res = bl->AddEventNotification(tag, NOTIFY_EVENT_MESSAGE, NOTIFY_EVENT_FLAG_0, nullptr);
                    GePrint(res? "true" : "false");
                  }
                }
              }
            }
          }
          break;
        case MSG_NOTIFY_EVENT:
          if (data)
          {
            NotifyEventData* nedata = (NotifyEventData* )data;
            if (nedata==nullptr) break;
            
            switch (nedata->eventid)
            {
              case NOTIFY_EVENT_MESSAGE:
                if (nedata->event_data)
                {
                  NotifyEventMsg* nemsg = (NotifyEventMsg* )nedata->event_data;
                  if (nemsg==nullptr) break;
                
                  GePrint(String::IntToString(nemsg->msg_id));
                }
                break;
            }
            break;
          }
      }
      
      return TagData::Message(node, type, data);
    }
    


  • On 17/02/2015 at 17:12, xxxxxxxx wrote:

    Hi Yannick,

    Placing it in MSG_DESCRIPTION_POSTSETPARAMETER makes so much sense. I originally tested it in AddToExecution, which seemed to work but probably was a bad idea.

    I'm testing the code and it properly reads the object's MSG_MOVE_FINISHED  and MSG_DESCRIPTION_USERINTERACTION_END messages.
    I added this inside my tag's Message() :

      
      case MSG_NOTIFY_EVENT:  
          if(data){  
              NotifyEventData* nedata = (NotifyEventData* )data;  
              if(nedata == nullptr) break;  
              switch(nedata->eventid){  
              case NOTIFY_EVENT_MESSAGE:  
                  if(nedata->event_data){  
                      NotifyEventMsg* nemsg = (NotifyEventMsg* )nedata->event_data;  
                      if (nemsg==nullptr) break;  
                      switch(nemsg->msg_id){  
                      case MSG_MOVE_FINISHED:  
                      case MSG_DESCRIPTION_USERINTERACTION_END:  
                          {  
                              BaseObject    *root_fk    = bc->GetObjectLink(ROOT_FK, doc),  
                                          *mid_fk        = bc->GetObjectLink(MID_FK, doc),  
                                          *tip_fk        = bc->GetObjectLink(TIP_FK, doc),  
                                          *mid_ik        = bc->GetObjectLink(MID_IK, doc),  
                                          *tip_ik        = bc->GetObjectLink(TIP_IK, doc);  
                              Bool        ISFORWARD    = bc->GetBool(FORWARD);  
                              doc->StartUndo();  
                              doc->AddUndo(UNDOTYPE_HIERARCHY_PSR, root_fk);  
                              doc->AddUndo(UNDOTYPE_HIERARCHY_PSR, mid_fk);  
                              doc->AddUndo(UNDOTYPE_HIERARCHY_PSR, tip_fk);  
                              ResetP(root_fk);  
                              AimYZ(root_fk, mid_ik, ISFORWARD);  
                              TwistX_mem(root_fk, tip_ik, ISFORWARD);  
                              ResetP(mid_fk);  
                              AimYZ(mid_fk, tip_ik, ISFORWARD);  
                              ResetP(tip_fk);  
                              doc->EndUndo();  
                              break;  
                          }  
                      }  
                  }break;  
              }break;  
          }  
    

    So far it works the way I want it to. All the objects linked in my tag now have the correct undo stacks when one of them is moved via viewport/AM.

    Thank you so much!



  • On 18/02/2015 at 00:05, xxxxxxxx wrote:

    Hi,

    I'm glad to hear you've made it work the way you wanted.
    You're welcome, the SDK support team is here to help.


Log in to reply