SceneHook & GeDialog initialization [SOLVED]



  • On 30/04/2016 at 00:55, xxxxxxxx wrote:

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

    ---------
    Hello,

    I stumbled upon a little problem when accessing a scenehook from a Dialog.
    I integrated my Dialog into my Layout and from there I want to access my Scenehook.
    The problem is, that the Scenehook is initialized after the Dialog, so my scenehook pointer is always nullptr when creating a new scene.

    I know, I can add additional checks to my Dialog actions and set the SceneHook if neccessary. But is there another way? Maybe catching a message when the scenehook has been initialized or something?

    Thanks in advance,
    Robert

    edit:
    Never mind, figured it out.
    Thanks to the ActiveObject SDK example, EVMSG_CHANGE is the way to go. :)



  • On 01/05/2016 at 14:29, xxxxxxxx wrote:

    Another thing I noticed though:
    I'm currently inserting BaseList2D(NodeData) elements to my SceneHook. When calling "Reload Python plugins", my elements get removed from the document. Even if they are saved to the doc. (Something similar happens with e.g. Object ID-Multipasses - you can see that in the ActiveObjectDialog; it "loses" track of those passes)
    Is this normal behaviour?
    Can I prevent this somehow? (I suppose that using ObjectData solves this issue).

    Any hints would be appreciated. :)



  • On 02/05/2016 at 02:06, xxxxxxxx wrote:

    Hello,

    how exactly do you store elements in your ScenHook and how do you reference these elements? To properly store NodeData elements with a SceneHook (or any other NodeData based class) you have to do the following:

    The host object (your scene hook) stores a GeListHead. This GeListHead takes ownership of the child elements you add to it. You must inform Cinema about this new tree branch by implementing GetBranchInfo(). Also make sure to handle the list head in Read(), Write() and CopyTo() so that a copy of the scene hook also contains the copies of the child elements.

      
    Int32 GetBranchInfo(GeListNode* node, BranchInfo* info, Int32 max, GETBRANCHINFO flags)  
    {  
      Int32 count = SUPER::GetBranchInfo(node, info, max, flags);  
      
      if (!_branchHead)  
      return count;  
      
      if (count < max)  
      {  
      info[count].head = _branchHead;  
      info[count].name = &_branchName;  
      info[count].id = 123456;  
      info[count].flags = BRANCHINFOFLAGS_0;  
      
      count++;  
      }  
      
      return count;  
    }  
    

    I would recommend to use your scene hook to access the stored elements and not to store references of the elements themselves.

    Best wishes,
    Sebastian



  • On 02/05/2016 at 02:26, xxxxxxxx wrote:

    Hey Sebastian,

    thanks for the reply.
    I'm currently doing exactly the same in the example you provided. (Except that you return the parent BranchCount if head does not exist - will add that check and see, if that makes any difference).

    I'm currently using a method in a worker class that's called by my GeDialog to allocate my elements.
    Then I'm requesting the branch info to insert the allocated nodes into the desired ListHead.

    I'll post some code this evening to rule out any mistakes made by myself. :)

    edit: Totally forgot about CopyTo(). How will this method have to look like? Would it be sufficient to Store, Read and Copy just the ListHead?



  • On 02/05/2016 at 12:09, xxxxxxxx wrote:

    Okay, here's my (shortened) code so far:

    SceneHook:

      
    class MySceneHook : public SceneHookData   
    {   
    public:   
         static NodeData* Alloc(void);   
         virtual Bool Init(GeListNode* node) override;   
         virtual Bool Read(GeListNode* node, HyperFile* hf, Int32 level) override;   
         virtual Bool Write(GeListNode* node, HyperFile* hf) override;   
         virtual Int32 GetBranchInfo(GeListNode* node, BranchInfo* info, Int32 max, GETBRANCHINFO flags) override;   
    private:   
         AutoAlloc<GeListHead> _nodelist;   
         String _name = "Test Branch";   
    };   
      
    NodeData* MySceneHook::Alloc(void)   
    {   
         return NewObjClear(MyceneHook);   
    }   
      
    Bool MySceneHook::Init(GeListNode* node)   
    {   
         if (!_nodelist || !node)   
              return false;   
      
         _nodelist->SetParent(node);   
         return true;   
    }   
      
    Bool MySceneHook::Read(GeListNode* node, HyperFile* hf, Int32 level)   
    {   
         return _nodelist->ReadObject(hf, true);   
    }   
      
    Bool MySceneHook::Write(GeListNode* node, HyperFile* hf)   
    {   
         return _nodelist->WriteObject(hf);   
    }   
      
    Int32 MySceneHook::GetBranchInfo(GeListNode* node, BranchInfo* info, Int32 max, GETBRANCHINFO flags)   
    {   
         if (!node)   
              return 0;   
      
         info[0].head = _nodelist;   
         info[0].name = &_name;   
         info[0].id = MY_SCENEHOOK_PID;   
         info[0].flags = BRANCHINFOFLAGS_0;   
      
         return 1;   
    }   
      
    Bool RegisterMySceneHook(void)   
    {   
         return RegisterSceneHookPlugin(MY_SCENEHOOK_PID, MY_SCENEHOOK_PNAME, PLUGINFLAG_SCENEHOOK_SUPPORT_DOCUMENT_DESCRIPTION, MySceneHook::Alloc, EXECUTIONFLAGS_0, NULL);   
    }   
    

    Worker:

      
    class MyWorker   
    {   
    // ...   
    public:   
         void Init();   
         void AddNode();   
    private:   
         GeListHead* GetNodeRoot();   
         BaseSceneHook* _sceneHook = nullptr;   
    };   
      
    void MyWorker::AddNode()   
    {   
         if (!_sceneHook)   
              return;   
         BaseList2D* node = BaseList2D::Alloc(MY_NODE_PID);   
      
         GeListHead* head = GetNodeRoot();   
         BaseList2D* node = static_cast<BaseList2D*>(head->GetDown());   
      
         if(!node)   
              return;   
      
         node->InsertUnderLast(head);   
         // node->SetParameter(...);   
         EventAdd();   
    }   
    

    GeDialog:

      
    class MyDialog : public GeDialog   
    {   
    public:   
         //...   
    private:   
            //...   
         MyWorker worker;   
    };   
      
    Bool MyDialog::Command(Int32 id, const BaseContainer& msg)   
    {   
         switch (id)   
         {   
         case MY_ID:   
              worker.AddNode();   
              break;   
         }   
         return true;   
    }   
    

    By guessing around, I think to following happens:
    When calling "Reload python plugins" for example, The Document gets re-initialized. This means, that also the scenehook gets destroyed and re-allocated. So does the _nodelist - so it's empty, which is quite logical.
    But after that, "ReadObject()" is not called again, so the nodelist stays empty - which is also makes sense.

    But how am I able to "hold" these elements after re-allocating the Scenehook then?

    I have the feeling, it depends on the CopyTo() method being implemented? At least the docs suggest to do so.



  • On 03/05/2016 at 01:29, xxxxxxxx wrote:

    Hello,

    as a rule of thumb one should always implement all three functions of Read(), Write() and CopyTo() if one of these functions is implemented. CopyTo() is especially important since it is relevant for undo handling.

    Best wishes,
    Sebastian



  • On 03/05/2016 at 10:44, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Hello,as a rule of thumb one should always implement all three functions of Read(), Write() and CopyTo() if one of these functions is implemented. CopyTo() is especially important since it is relevant for undo handling.Best wishes,Sebastian

    Thanks again Sebastian!

    This was the right hint.

    After implementing CopyTo() correctly, everything works as expected.
    One question about though: What exactly happens when "Reload Py plugins" is called?

    Here is the code that fixes the problem:

      
    Bool MySceneHook::CopyTo(NodeData* dest, GeListNode* snode, GeListNode* dnode, COPYFLAGS flags, AliasTrans* trn)   
    {   
         MySceneHook* desthook = static_cast<MySceneHook*>(dest);   
         _nodelist->CopyTo(desthook->_nodelist, COPYFLAGS_0, trn);   
         return true;   
    }   
    


  • On 04/05/2016 at 00:37, xxxxxxxx wrote:

    Hello,

    for technical reasons the current BaseDocument will be reloaded when the Python plugins are reloaded.

    Best wishes,
    Sebastian



  • On 04/05/2016 at 01:26, xxxxxxxx wrote:

    I see, thanks again. This thread can be marked as solved then. :)


Log in to reply