Modata arrays + Mograph Cache tag problem [SOLVED]

  • On 16/04/2015 at 08:46, xxxxxxxx wrote:

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

    Hi everyone,
    I've been playing around with Modata, trying to get and set Modata arrays in C++, and generally it's working, however I've been running into problems when a Mograph Cache tag is present (and has been baked), so I was hoping someone might be able to help me out with this?

    Here's what happens:

    1. I get the MoData with GetMoDataMessage
    2. I use the GetArray functions to look at the values within the MoData - this works just fine, until a baked Mograph Cache Tag is present on the object I'm retrieving the MoData from. Then C4D either hangs or crashes.

    Things I've tried:
    - Using AutoLocker for the MoData, but this hangs when I try it on the cached MoData (it works fine without the cache).
    - Using GetArray on the returned MoData, this crashes C4D
    - I noticed that the 'user_owned' switch in the GetMoDataMessage is set to true when I get the MoData with the Mograph Cache present (without the Mograph cache this is always false), so I've tried freeing the MoData I get back, but the call to Free also crashes.
    - I've tried calling the GetMoDataMessage as normal on the hidden MoData tag, and then on the MoGraph Cache tag itself, but neither of those work.

    If anyone knows their way around the MoGraph library and can give me some pointers about where I might be going wrong that would be a great help to me!


  • On 17/04/2015 at 05:29, xxxxxxxx wrote:

    I've been digging around in this problem a bit more, and I've found that the MoData that gets returned when the cache tag is baked usually has no arrays (as indicated by GetArrayCount), which probably explains why it's giving me an access violation crash when I try getting any of the arrays. However occasionally it returns an unexpectedly high number of arrays (in the millions) which has me stumped.

    Is there an extra step (beyond just using GetMoDataMessage) needed to correctly access MoData when it's been cached?

  • On 17/04/2015 at 07:36, xxxxxxxx wrote:

    Hi Dan,

    what you describe doesn't sound all too wrong.
    Actually here's some code as it is done somewhere inside C4D:

    BaseTag *mtag = pObj->GetTag(ID_MOTAGDATA);
    if (!mtag)
      return false;
    BaseTag*  *cachetag = pObj->GetTag(ID_MOBAKETAG);
    if (cachetag)
      cachetag->Message(MSG_GET_MODATA, &msg);
    if (!msg.modata)
      if (!mtag->Message(MSG_GET_MODATA, &msg) || !msg.modata)
        return false;
    ... work with MoData here

    It doesn't look too different from what you describe. If you don't find your problem by checking the above code, then you will have to provide me with some more information:
    - In which context/where you try to access the MoData
    - Perhaps some code demonstrating, how you do it

  • On 17/04/2015 at 07:56, xxxxxxxx wrote:

    Hi Andreas,
    Thanks for getting back to me. That is more or less how I'm accessing the MoData. Reading back through my question I realised I forgot an important point - which is that I'm creating the MoData tag myself on a generator plugin object (sorry - I was so focused on the one aspect of the problem I forgot about this). So if I'm not doing that correctly that could be the source of my problem - currently I'm simply doing:

    BaseObject * obj = (BaseObject* )node;  
    motag = obj->MakeTag(ID_MOTAGDATA);

    during the plugin's Init function, and accessing the MoData from 'motag' (or the Mograph cache if it's present).

    Is there something else at that end of the MoGraph process I could be missing?

  • On 17/04/2015 at 11:21, xxxxxxxx wrote:

    Hi Andreas,
    I've just gotten back from the vet, so here's a more thorough overview of what I'm doing, hopefully this might shed some light. I have a generator object, as I said previously I'm adding the MoData tag during Init, using obj->MakeTag(ID_MOTAGDATA), then I'm doing most of the other work during GetVirtualObjects where I'm using the MoData by:

    1. Getting it via the GetMoDataMessage as we've already talked about.
    2. acquiring an AutoLock on the MoData (I don't know if this is necessary here, I added it while I was exploring this problem - uncached MoData was working fine without it).
    3. calling SetCount() and AddArray() on it (adding all the built in arrays, MODATA_MATRIX etc) - I seem to have to do this every time, otherwise it gives me empty arrays
    4. Getting the arrays (using eg GetMatrixArray()) and manipulating the values of those arrays.
    5. Calling effectors using an Effector_PassData message.
    6. Getting the arrays again to find where to position generated objects.


  • On 20/04/2015 at 09:16, xxxxxxxx wrote:

    Hi Dan,

    it's a rare case somebody trying to create his own MoData...
    Can you perhaps show me some code from GVO? I think, it would help me understand a bit better...

  • On 20/04/2015 at 13:19, xxxxxxxx wrote:

    Hi Andreas,
    Yes of course - I've reduced this down as much as possible to avoid a massive posting, so this just has the MoData related bits:

    MoData * CAGenerator::GetMoData()  
      if (!IsLibraryInstalled(MODATALIB_ID))  
          return nullptr;  
      BaseObject * obj = (BaseObject* )Get();  
      BaseTag * motag = obj->GetTag(ID_MOTAGDATA);  
      if (motag == nullptr)  
          motag = obj->MakeTag(ID_MOTAGDATA);  
      if (motag == nullptr)  
          return nullptr;  
      BaseTag * mocachetag = obj->GetTag(ID_MOBAKETAG);  
      GetMoDataMessage msg = GetMoDataMessage();  
      msg.index = 0;  
      if (mocachetag)  
          mocachetag->Message(MSG_GET_MODATA, &msg);  
          if (msg.modata)  
              mdcached = TRUE;  
      if (!msg.modata)  
          mdcached = FALSE;  
          if (!motag->Message(MSG_GET_MODATA, &msg) || !msg.modata)  
              return nullptr;  
      if (msg.user_owned)  
          mduserowned = TRUE;  
          mduserowned = FALSE;  
      return msg.modata;  
    Bool CAGenerator::AddDefaultArrays(MoData * md)  
      if (md->AddArray(MODATA_MATRIX, DTYPE_MATRIX, "Matrix") == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_COLOR, DTYPE_VECTOR, "Color") == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_SIZE, DTYPE_VECTOR, "Size") == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_UVW, DTYPE_VECTOR, "UVW") == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_FLAGS, DTYPE_LONG, "Flags", MOGENFLAG_CLONE_ON) == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_WEIGHT, DTYPE_REAL, "Weight") == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_CLONE, DTYPE_REAL, "Clone") == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_TIME, DTYPE_REAL, "Time") == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_LASTMAT, DTYPE_MATRIX, "Last matrix") == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_STARTMAT, DTYPE_MATRIX, "Start matrix") == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_ALT_INDEX, DTYPE_LONG, "Alt index") == NOTOK) return FALSE;  
      if (md->AddArray(MODATA_FALLOFF_WGT, DTYPE_REAL, "Falloff weight") == NOTOK) return FALSE;  
      return TRUE;  
    BaseObject* CAGenerator::GetVirtualObjects(BaseObject* op, HierarchyHelp* hh)  
      BaseContainer * data = op->GetDataInstance();  
      const CustomDataType * effectorsraw = data->GetCustomDataType(ID_MG_MOTIONGENERATOR_EFFECTORLIST, CUSTOMDATATYPE_INEXCLUDE_LIST);  
      InExcludeData * effectors = nullptr;  
      if (effectorsraw)  
          effectors = (InExcludeData* )effectorsraw;  
      MoData * md = GetMoData();  
      //If not dirty, return cache  
      if (!op->CheckCache(hh))  
          if (md && mduserowned)  
          return op->GetCache(hh);  
      main = BaseObject::Alloc(Onull);  
      if (!main) goto Error;      
      //generate a bunch of objects to insert under main  
      //run mograph  
      if (md) //I've also tried skipping this bit in case the MoData was retrieved from the MOBAKETAG  
              AutoLocker al(md->GetAutoLock()); //I've tried with and without this  
              //mograph arrays  
              MDArray<Matrix> matarray;  
              matarray = md->GetMatrixArray(MODATA_MATRIX);  
              for (Int i = 0; i < objects->GetCount(); i++)  
                  matarray[i].off = position; //this is the start position of our generated objects  
          //run effectors  
          if (effectors)  
              Effector_PassData emsg = Effector_PassData();  
              emsg.op = op;  
     = md;  
              emsg.weight = 1.0;  
              emsg.thread = nullptr;  
              for (Int i = 0; i < effectors->GetObjectCount(); i++)  
                  BaseObject * e = (BaseObject* )effectors->ObjectFromIndex(doc, i);  
                  if (!e) continue;  
                  if (!e->GetDeformMode()) continue;                                  
                  e->Message(MSG_EXECUTE_EFFECTOR, &emsg);  
      //use altered MoData here - use the matrix offset array to reposition our generated objects  
      if (md && mduserowned)  
      return main;  
      if (md && mduserowned)  
      return NULL;  

    Thanks for taking the time to look through this,

  • On 26/04/2015 at 23:54, xxxxxxxx wrote:

    Hi Andreas,
    Just checking in to see if you've had any ideas that might help me with this? If not I should be able to make a custom cache and stop the Mograph Cache from effecting our generator, but it'd be great to take advantage of the built in tools if possible.


  • On 26/04/2015 at 23:59, xxxxxxxx wrote:

    Hi Dan,
    I have no idea, yet. But to be honest, I lacked the time to look into it thoroughly. I'll try to squish it in today.

  • On 27/04/2015 at 13:48, xxxxxxxx wrote:

    Hi Andreas,
    Thanks for your PM earlier, sorry I haven't been able to reply as it says your inbox is full :)

    I have a project prepared though that I can send you - let me know when you have some space for new messages and I'll send you a link.


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

    Phew! I thought I had a solution to this, but now I'm pretty sure I really do have a solution - the answer is to call Release() on the GetMoDataMessage if user_owned is set (which it always is if it comes from a mograph cache tag). Otherwise the GetMoDataMessage's destructor will free the MoData itself, which is why all my later attempts to access it gave me headaches. So simple!

    Thanks very much for your help Andreas.

Log in to reply