Modifier invocation

I'm building a kind of autofit feature through a modifier. It works like a charm, with a single exception. The modifier is invoked only when the frame is entered or if a part of its GUI is modified. I would like it to react whenever the geometry it's supposed to autofit to is modified.

To make it clear, I have a cloth. It's not attached to a figure, but linked through the modifier. If I change a parameter or move a part of the figure, I would like the cloth (and specifically my modifier DeformMesh method) to be invoked.

It works more or less if the figure is attached, but the whole point is not to attach... The point is to have the clothes to follow the figure without the need to link it to bones, weight paint it and so on.

Comments

  • I SamuelI Samuel Posts: 226

    Just a thought since I am still in the process of exploring Carrara SDK and there may be better options via Carrara Shell. It looks like modifier needs to be notified via some sort of call back event. Have you looked into IChangeManager and IChangeChannel interfaces (Utilities)?

    On another thought may be modifier extension can create another child component (which will allow user to choose the object to link to e.g. figure) to notify it via Shell. Theoretically Shell should be able to provide access to modifier extension. Above will add little complexity but probably quick way will be to provide refresh button on Modifier’s GUI as a versioin one.

     

  • I found how to use the IChangeChannel in the SimpleModeler sample in the Modeler.cpp and Modeler.h files and made some notes which might save you time.  You should also look at the contents of the actual IChangeManagement.h file in your IDE which has some information I didn't find in the main SDK Docs.HTML files.

    That module sample is derived from TBasicModule which already implements IChangeListener so if you are using a deform modifier you will change the class declaration in your .h file to:

    #include "IChangeManagement.h"
    ...
    class MyPlugin : public TBasicDeformModifier, public IChangeListener
    ...

    Declare the virtual :DataChanged() and counted pointers for the change channels that you need:

    ...
    virtual void MCCOMAPI DataChanged (IChangeChannel *channel, IDType changeKind, IMCUnknown *changedData);
    TMCCountedPtr<IChangeChannel> fImmediateUpdateChannel;
    ...

    In your .cpp file You will need to make the usual changes in your plugin's ::QueryInterface() and declare it if you haven't got one;

    MCCOMErr MyPlugin::QueryInterface(const MCIID& riid, void** ppvObj)
      {
      if( MCIsEqualIID(riid, CLSID_MyPlugin) )
      ...
      else if(MCIsEqualIID( riid, IID_IChangeListener) )
        {
        TMCCountedGetHelper <IChangeListener> result(ppvObj) ;
        result = this;
        return MC_S_OK;
        }
      else
      ...
      }

    For your plugin you will probably need to use the Immediate Update Channel which informs listeners of a data change when anything is moved.  For parameter changes you can use I3DShScene::GetTreePropertyChangeChannel() which includes any changes to a master's properties, even a change of selection in the scene and any parameters.

    The change channel needs to be registered as a listener and unregistered later.  In the SimpleModeler sample it does this in the ::Initialize2() function in Modeler.cpp which is called by TBasicModule::Initialize(); and then unregisters them in ::Destroy()  In a modifier or other plugin type I'm not sure where the best or correct place is to register but I did this in the constructor and destructor for the channel I needed.

    MyPlugin::MyPlugin()
      {
      ...
      TMCCountedPtr<ISceneDocument> sceneDoc;
      sceneDoc=gShell3DUtilities->GetLastActiveSceneDoc();
      MCVerify(sceneDoc);
      TMCCountedPtr<I3DShScene> scene;
      scene=sceneDoc->GetScene();
      scene->GetTreePropertyChangeChannel(&TreePropertyChangeChannel);
      ThrowIfNil(TreePropertyChangeChannel);
      TreePropertyChangeChannel->RegisterListener(this);
      ...
      }   
    
    MyPlugin::~MyPlugin()
      {
      ...
      if(TreePropertyChangeChannel)  
      TreePropertyChangeChannel->UnregisterListener(this);
      TreePropertyChangeChannel = NULL;
      ...
      }

    I didn't ::Clone() the pointer to the change channel.  Let each cloned instance of your plugin register it's own change channel.

    With the channel(s) registered you can add the body of ::DataChanged().  Refer to the SimpleModeler sample for guidance in Modeler.cpp.  Note in their sample that it uses the ImmediateUpdateChannel and queries the changedData to get the primitive.  This might be where you can look for specific changes to your autofit clothing since you would want to avoid constant unnecessary updates.  I don't have an answer for that but I will look into it when I have more time.  

    At the time I did a bit of experimentation with the debugger to observe some of the change signals that were sent in response to what I clicked on or changed in Carrara and got some of the flags I couldn't find in the docs or other files.  Here is the code I used in ::DataChanged() to peek into the change channels that were coming in and what their flags were.

    void MyPlugin::DataChanged(IChangeChannel *channel, IDType changeKind, IMCUnknown *changedData)
      {
      ...
      char fourcc[5]="code";
      *((uint32 *)fourcc)=changeKind;
      int32 channelID=channel->GetChannelID();
      *((uint32 *)fourcc)=channelID;
      ...

    In your case once you detect that change you want your plugin to update.  I haven't tested this either but I suggest you get hold of the I3DShTreeElement and try:

    tree->PostMoveChange();

    This works in most cases...

  • PhilemoPhilemo Posts: 885

    Thank you very much Sparrowhawke.It's very helpful.

    I will have to experiment to filter as much as possible before updating (the process in deformFacetMesh takes something like a 20th of a  second, but if I send a boatload of postMove changes, it may take a lot of time to process them all. One possibility is not to send a new one unless the previous ones has been consumed.

     

  • PhilemoPhilemo Posts: 885
    edited March 2017

    First tests, it works like a charm.

    I have3 additionnal data on your description

    1 .tree->PostMoveChange(); should be changed to tree->PostMoveChange(true);

    /// \param invalidateDeformedGeometry True if the deformer should be invalidated.

    virtual void MCCOMAPI PostMoveChange(boolean invalidateDeformedGeometry = false) = 0;

    2. ChangeType can be decoded with constants from "TreeChanges.h"

    For instance, a tree moving is

    #define kChange_MiscTreePostMove&nbsp;&nbsp;0x00060002

    3. ChangedData can be queryied through QueryInterface to identify what object has been changed. I use it to check if a parent or a child of the figure i'm tracking is moving

    Post edited by Philemo on
  • PhilemoPhilemo Posts: 885
    edited March 2017
    Philemo said:
    Sorry, double post
    Post edited by Philemo on
  • I ran a few more tests in the debugger to see what was happening.  With the Immediate Update Channel the changeData was often valid but could only be queried to be an I3DShPrimitive when the primitive was actually opened into a modeler window - as with the module sample.  I couldn't find what the interface in the changeData was for other changes in that channel in response to other actions.

    With the Tree Property Change Channel the changedData was able to query for the I3DShTreeElement and that tree can be compared to one you are watching for.  I tested with a figure but couldn't narrow it down to a single specific changeKind signal after finishing the move of a bone or morph dial setting.  It seemed to be sending a change for all of the bones !

    It sounds like you are on the right track now if the tree->PostMoveChange(true) only generates one change in response to the right changeKind signal.

Sign In or Register to comment.