function on frame change



  • On 12/08/2014 at 05:06, xxxxxxxx wrote:

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

    ---------
    i know that this is a beginner question but...

    i have to run a specific function only when the timeline frame change.. how can i do that? i searched for a virtual function in nodedata called by cinema when the frame change but i didn't find anything..

    thank you in advance



  • On 12/08/2014 at 05:39, xxxxxxxx wrote:

    Howdy,

    You can store the timeline's frame in a BaseContainer and then compare the stored frame with the current frame like this:

    LONG oldFrame = data->GetLong(MY_FRAME_STORAGE);
    LONG fps = doc->GetFps();
    LONG curFrame = doc->GetTime().GetFrame(fps);
      
    if(oldFrame != curFrame)
    {
    	// frame changed so do something
    }
    data->SetLong(MY_FRAME_STORAGE,curFrame);
    

    Adios,
    Cactus Dan



  • On 13/08/2014 at 01:48, xxxxxxxx wrote:

    yes but.. when this method will be called by the system? i mean.. may this be inside the GetVirtualObject overrided method or there is a better place to do this?



  • On 13/08/2014 at 05:56, xxxxxxxx wrote:

    Howdy,

    Well, I'd use that method in whatever function needs to do something if the frame has changed. It's difficult to say for sure without knowing what your plugin needs to do.

    Adios,
    Cactus Dan



  • On 13/08/2014 at 13:38, xxxxxxxx wrote:

    Hello aerydna,

    you could use a SceneHookData plugin. In it's Execute function listen for a specific set of flags when the frame is changed:

      
      virtual EXECUTIONRESULT Execute(BaseSceneHook* node, BaseDocument* doc, BaseThread* bt, Int32 priority, EXECUTIONFLAGS flags)  
      {  
          const EXECUTIONFLAGS newFrame = EXECUTIONFLAGS_CACHEBUILDING | EXECUTIONFLAGS_EXPRESSION | EXECUTIONFLAGS_ANIMATION;  
      
          if (flags == newFrame)  
          {  
              GePrint("new frame");  
          }  
            
          return EXECUTIONRESULT_OK;  
      };  
    

    Please be careful when writing a SceneHook plugin so it won't slow down Cinema too much.

    best wishes,
    Sebastian



  • On 19/08/2014 at 16:39, xxxxxxxx wrote:

    i think that's what i need... for now it's a objectdata.. and it work on the viewport but not at render time.



  • On 19/08/2014 at 16:42, xxxxxxxx wrote:

    last question... can a scene hook plugin generate object?



  • On 20/08/2014 at 12:13, xxxxxxxx wrote:

    Hello,

    SceneHookData::Execute() is called in a thread context so you should not modify the scene within that function.

    A way to be sure is to use a core message. Simply define your own message by using a new plugin ID:

    const Int32 EXAMPLE_MESSAGE_ID = 1016549;
    

    In the SceneHookData::Execute() call SpecialEventAdd to send this message.

        virtual EXECUTIONRESULT Execute(BaseSceneHook* node, BaseDocument* doc, BaseThread* bt, Int32 priority, EXECUTIONFLAGS flags)  
      {  
          const EXECUTIONFLAGS newFrame = EXECUTIONFLAGS_CACHEBUILDING | EXECUTIONFLAGS_EXPRESSION | EXECUTIONFLAGS_ANIMATION;  
      
          if (flags == newFrame)  
          {  
              **SpecialEventAdd(EXAMPLE_MESSAGE_ID, 0, 0);**  
          }  
            
          return EXECUTIONRESULT_OK;  
      };
    

    Finally you can catch the message in a MessageData plugin:

    class SimpleMessageData : public MessageData  
    {  
      virtual Bool CoreMessage(Int32 id, const BaseContainer &bc)  
      {  
          switch (id)  
          {  
          case **EXAMPLE_MESSAGE_ID** :  
              {  
                  // do something with the scene  
      
                  BaseDocument * doc = GetActiveDocument();  
      
                  if (doc)  
                  {  
                      Random rnd;  
                      rnd.Init(GeGetMilliSeconds());  
      
                      const Vector pos = Vector(rnd.Get01() * 1000.0, rnd.Get01() * 1000.0, rnd.Get01() * 1000.0);  
      
                      BaseObject * cube = BaseObject::Alloc(Ocube);  
                      cube->SetAbsPos(pos);  
      
                      doc->InsertObject(cube, nullptr, nullptr);  
                        
                      EventAdd();  
                  }  
      
      
                  break;  
              }  
          }  
            
          return true;  
      }  
    };
    

    best wishes,
    Sebastian



  • On 21/08/2014 at 05:02, xxxxxxxx wrote:

    thankyou... but is it possible to cach a message with a generator object (an objectdata)?

    cause the situation is this.

    i have my generator plug in that generate a specific geometry that react to time changes (like the ocean for example).. now in the viewport it work fine with a simple objectdata that listen to frame changes in the GetVirtualObject function and react properly. in the picture viewer this dosn't happen.. you are telling me that for this kind of thing i need 3 separate object (sceene hook, messagedata and objectdata)?? thankyou very much and sorry for my english



  • On 21/08/2014 at 07:31, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    thankyou... but is it possible to cach a message with a generator object (an objectdata)?cause the situation is this.i
    have my generator plug in that generate a specific geometry that react
    to time changes (like the ocean for example).. now in the viewport it
    work fine with a simple objectdata that listen to frame changes in the
    GetVirtualObject function and react properly. in the picture viewer this
    dosn't happen.. you are telling me that for this kind of thing i need 3
    separate object (sceene hook, messagedata and objectdata)?? thankyou
    very much and sorry for my english

    You don't need all those extra plugins. Do it the way CactusDan suggested. Check the frame change in the Execute() function of the ObjectData, there you can make whatever data changes you need in your object and let GetVirtualObjects handle building the object. Don't look for a frame change in GVO because if you do, GVO will only execute once, when the frame changes. It isn't supposed to work like that, GVO is called by Cinema whenever it needs to get your virtual object - e.g. if you rotate the viewport. So it has to be called multiple times per frame.

    If you make the changes to any data used by GVO in the Execute function then that only happens once per frame but GVO can be called many times.

    If it doesn't work correctly in the picture viewer it may be that whatever data you are using isn't being copied over to the new document which is created whenever you render to the PV. It's difficult to say without seeing the way your plugin works, but suppose you have a set of data used to build the object. If that data is held in a class-level variable, you will need to override CopyTo() and copy the data to the destination object there.



  • On 22/08/2014 at 18:27, xxxxxxxx wrote:

    there are some things that i don't understand.

    1. if i override the execute funtion nothing happen.. cinema don't call it. i also overrided the addtoexecution but cinema don't call it neither.

    2. if i check the frame changes in the GVO all is fine (in the viewport).. the structure is this:
    the frame is changed? yes, modify and return obj.
    No, return obj as is

    so the system can cal it whenever he want  but it will modify the object only when the frame change.

    3. the pictures viewer ignore all the data changed by the users and the time value from doc->GetTime().Get() he render always the frame 0. all the data are stored in a basecontainer exept 3 pointers.

    whats wrong? thank you all for all by the way



  • On 27/08/2014 at 13:25, xxxxxxxx wrote:

    someone please! i can't really make the Execute() method work.. i don't know how to make cinema call it. the picture viewer don't see any changes to the data in the base container and i have another problem.. when i disable the object and then re-enable it cinema crash. it might be because i have some array as member variable? here some code:

    HEADER:

    class sea : public ObjectData
    {
      INSTANCEOF(Sea, ObjectData);

    public:
      Vector* grid;
      waveD* wave;
      String* debug = NewObj(String);
      PolygonObject* obj;
      BaseContainer* data;

    void Updatesea();
      void Buildwavedata();
      void Buildgeo();

    Bool Init(GeListNode* node);
      virtual void Free(GeListNode* node);

    virtual BaseObject* GetVirtualObjects(BaseObject* op, HierarchyHelp *hh);
      static NodeData* Alloc(void) { return NewObjClear(sea); }
    };

    INIT method

    Bool sea::Init(GeListNode* node)
    {
      BaseObject* op = (BaseObject* )node;
      data = op->GetDataInstance();
      if (!data)
          return false;
      data->SetInt32(RES, 150);
      data->SetInt32(ANGLE, 89);
      data->SetFloat(FREQ_MIN, 0.02);
      data->SetFloat(FREQ_MAX, 20);
      data->SetFloat(DIRECTION, 0);
      data->SetFloat(DIR_TOLLERANCE, 90);
      data->SetFloat(STATE, 1);
      data->SetFloat(Q, 4);
      data->SetInt32(WAVE_NUM, 100);
      obj = PolygonObject::Alloc(pcount(), polycount());
      grid = NewMem(Vector, pcount());
      Buildwavedata();
      Buildgeo();
      Updatesea();
      return true;
    }

    GVO method

    BaseObject* sea::GetVirtualObjects(BaseObject* op, HierarchyHelp *hh)
    {
      BaseDocument* doc = GetActiveDocument();
      Int32 oldFrame = data->GetInt32(FRAME);
      Int32 fps = doc->GetFps();
      Int32 curFrame = doc->GetTime().GetFrame(fps);

    if (oldFrame != curFrame)
      {
          clock_t startTime = clock();

    Updatesea();

    Float elaps = Float(clock() - startTime) / (Float)CLOCKS_PER_SEC;
          GePrint(debug->FloatToString(elaps));

    data->SetInt32(FRAME, curFrame);

    }

    return obj;
    }



  • On 27/08/2014 at 21:30, xxxxxxxx wrote:

    Try this:

    1. I think you need to get the document using hh to get the current frame to update:

    BaseDocument *doc = hh->GetDocument();
    

    2. Define the curFrame and oldFrame as class level variables.
    In GVO, update the curFrame as such:

    curFrame  = doc->GetTime().GetFrame(doc->GetFps());   
    data->SetInt32(FRAME, curFrame);
    oldFrame = curFrame;
    

    This will animate the document.



  • On 28/08/2014 at 04:28, xxxxxxxx wrote:

    Howdy,

    Originally posted by xxxxxxxx

    ...someone please! i can't really make the Execute() method work.. i don't know how to make cinema call it...

    The Execute() function of an ObjectData() is not called by default by Cinema 4D. You have to use the AddToExecution() function to tell Cinema 4D to call the Execute() function.

    Bool MyObject::AddToExecution(BaseObject *op, PriorityLIst *list)
    {
    	list->Add(op, EXECUTIONPRIORITY_EXPRESSION, EXECUTIONFLAGS_0);
    	return true;
    }
    

    Adios,
    Cactus Dan



  • On 29/08/2014 at 07:05, xxxxxxxx wrote:

    Howdy,_<_t_>_
    Originally posted by aerydna

    ...someone please! i can't really make the Execute() method work.. i don't know how to make cinema call it... _/tr>
    The Execute() function of an ObjectData() is not called by default by Cinema 4D. You have to use the AddToExecution() function to tell Cinema 4D to call the Execute() function.
    _h="99%">|

    Bool MyObject::AddToExecution(BaseObject \*op, PriorityLIst \*list)
    
    {
    
    	list->Add(op, EXECUTIONPRIORITY_EXPRESSION, EXECUTIONFLAGS_0);
    
    	return true;
    
    }
    
    <\_<\_t\_>\_>  
    ---  
      
      
    
    
    Adios,
    
    Cactus Dan
    
    
    
    
     \_<\_t\_>\_div>  
      
      
    as i wrote a couple of message ago.. i tried that way using different flag and priority and nothing happened.  thankyou anyway  
     
    


  • On 29/08/2014 at 07:06, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Try this:

    1. I think you need to get the document using hh to get the current frame to update:

    BaseDocument *doc = hh->GetDocument();
    

    2. Define the curFrame and oldFrame as class level variables.
    In GVO, update the curFrame as such:

    curFrame  = doc->GetTime().GetFrame(doc->GetFps());   
    data->SetInt32(FRAME, curFrame);
    oldFrame = curFrame;
    

    This will animate the document.

    thank you i'll try it



  • On 29/08/2014 at 07:07, xxxxxxxx wrote:

    Howdy,

    Well, then you must've done something wrong in your code. Can you post the code you used?

    Adios,
    Cactus Dan



  • On 29/08/2014 at 13:36, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Howdy,

    Well, then you must've done something wrong in your code. Can you post the code you used?

    Adios,
    Cactus Dan

    Bool sea::AddToExecution(BaseObject* op, PriorityList* list)
    {
      list->Add(op, EXECUTIONPRIORITY_ANIMATION, EXECUTIONFLAGS_0);
      return true;
    }

    EXECUTIONRESULT sea::Execute(BaseObject* op, BaseDocument* doc, BaseThread* bt, Int32 priority, EXECUTIONFLAGS flags)
    {
      GePrint("ciao");
      return EXECUTIONRESULT_OK;
    }



  • On 30/08/2014 at 04:28, xxxxxxxx wrote:

    In the function registering the plugin you also have to set the OBJECT_CALLADDEXECUTION flag so that AddToExecution() is called.



  • On 31/08/2014 at 05:31, xxxxxxxx wrote:

    Howdy,

    Originally posted by xxxxxxxx

    In the function registering the plugin you also have to set the OBJECT_CALLADDEXECUTION flag so that AddToExecution() is called.

    Yep, that would certainly explain why it wasn't working for you. 😉

    Adios,
    Cactus Dan


Log in to reply