Update Attribute Manager after SetParameter



  • On 13/06/2018 at 10:00, xxxxxxxx wrote:

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

    ---------
    I'm sure I'm missing something obvious, but having not used the C++ SDK for quite a while...

    I'm updating a few parameters from a custom function. This is an ObjectData plugin with a few string and long fields. NR_FRAMEINDEX in this case is LONG.

    // a static callback function
    void(__stdcall NeuronReader::BVHFrameDataReceived)(void* customObj, SOCKET_REF sender, BvhDataHeader* header, float* data) {
    	NeuronReader* pthis = (NeuronReader* )customObj;
    	BaseObject *op = (BaseObject* )(pthis->Get());
    	pthis->ReadNeuronBvhData(sender, header, data, op);
    }
      
    // this should update the parameters
    void NeuronReader::ReadNeuronBvhData(SOCKET_REF sender, BvhDataHeader* header, float* data, BaseObject* op) {
    	if (sender) {
    		Int32 frameIndex = (Int32)(header->FrameIndex);
    		op->SetParameter(DescID(NR_FRAMEINDEX), GeData(frameIndex), DESCFLAGS_SET_0);
    		//op->SetDirty(DIRTYFLAGS_DATA);
    		//op->Message(MSG_CHANGE);
    		//EventAdd(EVENT_ANIMATE);
    	}
    }
    

    This  feels straightforward enough. Checking the NR_FRAMEINDEX parameter via GeConsoleOut shows me that it writes the correct values. However, the Attribute Manager attribute never gets updates.

    Do I need to go through some SetDParameter magic or something? The various EventAdds and MSG_Change etc don't seem to do anything here.



  • On 14/06/2018 at 03:58, xxxxxxxx wrote:

    I don't get it. I have now gone and implemented a (feeling somewhat convoluted) way to make sure that the SetParameter happens in the MainThread (by creating a MessageData plugin, sending a SpecialEventAdd() there containing a pointer to my object, sending a GePluginMessage() back (again containing the pointer) and then calling a function to apply the parameter.
    I'm checking with GeIsMainThread() all along the way. Yet the UI will not update. Argh!



  • On 14/06/2018 at 09:50, xxxxxxxx wrote:

    Hi,

    to be honest I don't immediately see the issue, either.
    Normally a EventAdd() (and also your EventAdd(EVENT_ANIMATE)) should do the trick.
    You seem to have had the same sus**cion as I had in your second post. Nevertheless I'd like to ask in which context the functions you posted are called/used? Maybe a rough description of the structure of your ObjectData could help as well.
    In any case I will discuss this tomorrow with the team.



  • On 15/06/2018 at 02:32, xxxxxxxx wrote:

    This should be all the relevant code...

    This is the output I get:

    Registered the NeuronReader plugin
    Connected to Neuron server at 127.0.0.1:7001
    Demo message
    IS MainThread
    ApplyNeuronData running in MainThread // Modifying:1004
    Before: 0
    MSG_DESCRIPTION_POSTSETPARAMETER: 1004
    POSTSETPARAMETER_VALUE:40
    After: 40
    

    So the parameter is set correctly internally, but somehow it does not get reflected in the UI at all.

    // This is a callback function, being called from a threaded context
    void(__stdcall ONeuronReader::BVHFrameDataReceived)(void* customObj, SOCKET_REF sender, BvhDataHeader* header, float* data) {
        ONeuronReader* pthis = (ONeuronReader* )customObj;
        BaseObject *op = (BaseObject* )(pthis->Get());
        pthis->ReadNeuronBvhData(sender, header, data, op);
    }
      
    // This does nothing other than send a MessageData, which in turn sends a GePluginMessage() to this plugin
    void ONeuronReader::ReadNeuronBvhData(SOCKET_REF sender, BvhDataHeader* header, float* data, BaseObject* op) {
        if (sender) {
            SpecialEventAdd(MSG_NEURONMESSAGE, 0, (UInt)op);
        }
    }
      
    // Thanks to being called from PluginMessage, this is running in the MainThread (does it even have to?)
    // Attempting to set the LONG parameter to a test value of "40"
    // Value appears to be set correctly, however UI does not change
    void ONeuronReader::ApplyNeuronData(BaseObject *op) {
        if (GeIsMainThread()) {
            GeConsoleOut("ApplyNeuronData running in MainThread // Modifying:" \+ String::IntToString(NR_FRAMEINDEX));
            GeData frameIndex;
            op->GetParameter(NR_FRAMEINDEX, frameIndex, DESCFLAGS_GET_0);
            GeConsoleOut("Before: " \+ String::IntToString(frameIndex.GetInt32()));
            op->SetParameter(DescID(NR_FRAMEINDEX), GeData(40), DESCFLAGS_SET_0);
            op->Message(MSG_CHANGE);
            EventAdd();
            op->GetParameter(NR_FRAMEINDEX, frameIndex, DESCFLAGS_GET_0);
            GeConsoleOut("After: " \+ String::IntToString(frameIndex.GetInt32()));
        }
        else {
            GeConsoleOut("ApplyNeuronData NOT running in MainThread");
        }
    }
      
    BaseObject* ONeuronReader::GetVirtualObjects(BaseObject* op, HierarchyHelp* hh) {
        return BaseObject::Alloc(Onull);
    }
      
    Bool ONeuronReader::Message(GeListNode* node, Int32 type, void* data) {
        BaseContainer* bc;
    ...
        else if (type == MSG_DESCRIPTION_POSTSETPARAMETER)
        {
            // Get message data
            DescriptionPostSetValue* dparm = (DescriptionPostSetValue* )data;
            Int32 parameterId = (*(dparm->descid))[0].id;
            bc = static_cast<BaseObject*>(node)->GetDataInstance();
            GeConsoleOut("MSG_DESCRIPTION_POSTSETPARAMETER: " \+ String::IntToString(parameterId));
            bc->SetInt32(NR_FRAMEINDEX, bc->GetInt32(NR_FRAMEINDEX));
      
            switch (parameterId)
            {
            case NR_FRAMEINDEX:
                GeConsoleOut("POSTSETPARAMETER_VALUE:" \+ String::IntToString(bc->GetInt32(NR_FRAMEINDEX)));
                break;
            }
        }
    ...
        return SUPER::Message(node, type, data);
    }
      
    Bool NeuronMessage::CoreMessage(Int32 id, const BaseContainer& bc)
    {
        switch (id)
        {
        case MSG_NEURONMESSAGE:
            GeConsoleOut("Demo message");
            BaseObject* pointer_op = (BaseObject* )bc.GetVoid(BFM_CORE_PAR2);
            //GeConsoleOut(pointer_op->GetName()); // Just checking...
            GePluginMessage(MSG_NEURONMESSAGE, (void * )pointer_op);
        }
        return true;
    }
      
    Bool RegisterNeuronReaderObject() {
        return RegisterObjectPlugin(ID_NEURONREADER, GeLoadString(IDS_NEURONREADER), OBJECT_GENERATOR, ONeuronReader::Alloc, "Oneuronreader", AutoBitmap("atom.tif"), 0);
    }
      
    Bool PluginMessage(Int32 id, void *data)
    {
        switch (id)
        {
            case C4DPL_INIT_SYS:
                if (!resource.Init())
                    return false;       // don't start plugin without resource
                return true;
      
            case MSG_NEURONMESSAGE:
                if (GeIsMainThread()) {
                    GeConsoleOut("IS MainThread");
                    ONeuronReader* op = (ONeuronReader* )data;
                    //GeConsoleOut(op->GetName()); // just to confirm it is the correct pointer
                    op->ApplyNeuronData((BaseObject* )op);
                }
                else {
                    GeConsoleOut("NotMainThread");
                }
        }
        return false;
    }
    

Log in to reply