Mograph(cloner)



  • On 26/09/2013 at 09:19, xxxxxxxx wrote:

    Can we please not just sweep this one under the rug and walk away from it?
    Mograph is a major part of C4D. And this is far too important to just walk away from it unsolved.

    Could you please ask the developers how to access the cloner?

    Thanks,
    -ScottA



  • On 08/11/2013 at 10:09, xxxxxxxx wrote:

    Hello, I've been looking same thing as Scott.  Is there any chance we can get word on this please?

    Dan



  • On 10/11/2013 at 04:42, xxxxxxxx wrote:

    Hi Scott,

    at what point did you try to retrieve the MoData? From a Command plugin? I just tried it from a
    CommandData plugin and it didn't work. I have contacted the devs to clarify at what point it is
    should be possible to retrieve the MoData as the documentation doesn't state much about it.

    Best,
    -Niklas



  • On 10/11/2013 at 08:19, xxxxxxxx wrote:

    Hi Nik,

    Yes. I was using it in a command data type plugin.
    I could never get it to work properly.

    -ScottA



  • On 11/11/2013 at 11:19, xxxxxxxx wrote:

    Got the answer. The message needs to be sent to the MoData Tag of the Cloner/Matrix object. This
    information will be added to the docs.

    > class Command : public CommandData {
    >
    >
    >
    >
    > public:
    >
    >
    >
    >
    >     virtual Bool Execute(BaseDocument* doc) {
    >
    >         BaseObject* op = doc->GetActiveObject();
    >
    >         if (!op) return true;
    >
    >
    >
    >
    >         /* Find the MoData tag. */
    >
    >         BaseTag* tag = op->GetTag(ID_MOTAGDATA);
    >
    >         if (!tag) return true;
    >
    >
    >
    >
    >         /* Retrieve the MoData. */
    >
    >         GetMoDataMessage msg_data;
    >
    >         if (!tag->Message(MSG_GET_MODATA, &msg_data)) {
    >
    >             GePrint("MSG_GET_MODATA message failed.");
    >
    >             return true;
    >
    >         }
    >
    >         if (!msg_data.modata) {
    >
    >             GePrint("MSG_GET_MODATA did not return a MoData pointer.");
    >
    >             return true;
    >
    >         }
    >
    >
    >
    >
    >         Int32 count = msg_data.modata->GetCount();
    >
    >         String message = "Got MoData with " + String::IntToString(count) + " particles. It is ";
    >
    >         if (msg_data.user_owned) message += "user owned.";
    >
    >         else message += "owned by the object.";
    >
    >         GePrint(message);
    >
    >
    >
    >
    >         if (msg_data.user_owned) {
    >
    >             MoData::Free(msg_data.modata);
    >
    >         }
    >
    >         return true;
    >
    >     }
    >
    >
    >
    >
    > };

    Best,
    -Niklas



  • On 11/11/2013 at 13:09, xxxxxxxx wrote:

    Thanks Nik. That helps a lot! 👍
    But I still can't figure out how to get at a specific clone from it.

        BaseObject *clonerObj = doc->SearchObject("Cloner");  
      if (!clonerObj) return true;  
       
      BaseTag *tag = clonerObj->GetTag(ID_MOTAGDATA);  
      if (!tag) return TRUE;  
      
      GetMoDataMessage msg_data;  
      if (!tag->Message(MSG_GET_MODATA, &msg_data)) return TRUE;  
      
      if (!msg_data.modata) return TRUE;  
      
      LONG count = msg_data.modata->GetCount();   //Get the number of clones from the cloner's "Count" attribute  
      //GePrint(LongToString(count));      
      
      BaseContainer *bc = clonerObj->GetDataInstance();    //Get the cloner's container items  
      bc->SetReal(MG_LINEAR_OBJECT_AMOUNT, 0.75);          //Set the strength value to 75%  
      
        
    /////////////////////////////////////////////////////////////////////////////////////      
    /// Now I want to get a specific clone from the cloner's array  
    /// But How?  
        
      //None of these are getting me a specific clone  
      bc->GetContainerInstance(MODATA_MATRIX);  
      LONG first = msg_data.modata->GetArrayIndex(0);  
      void *marr = msg_data.modata->GetArray(MODATA_MATRIX);
    

    I've only used the coffee&python effectors to access a specific clone.
    Is there a way to do this directly from the Cloner object itself?

    -ScottA



  • On 06/01/2014 at 11:14, xxxxxxxx wrote:

    Sorry for bumping this old thread.
    But I figured out how to get the individual clones from the cloner object. So I figured I should post it since nobody else seems to know how to do it.

    //This is how to get the Modata from the cloner directly. Instead of from the gui's description attributes  
      
    #include "c4d_baseeffectordata.h"  
      
      
      
      //The cloner object  
      BaseObject *clonerObj = doc->SearchObject("Cloner");  
      if (!clonerObj) return true;  
      
      //Get the hidden MoData tag that is on the cloner object  
      BaseTag *tag = clonerObj->GetTag(ID_MOTAGDATA);  
      if (!tag) return true;  
      
      //Retrieve the MoData  
      GetMoDataMessage msg_data;  
      if (!tag->Message(MSG_GET_MODATA, &msg_data))   
      {  
          GePrint("MSG_GET_MODATA message failed.");  
          return true;  
      }  
      
      //Check if the pointer exists  
      if (!msg_data.modata)   
      {  
          GePrint("MSG_GET_MODATA did not return a MoData pointer.");  
          return true;  
      }  
      
      //Get the number of clones from the cloner's "Count" attribute  
      LONG count = msg_data.modata->GetCount();  
      GePrint(LongToString(count));      
      
      
      //Get the positions of the clones in the cloner object  
      MoData *md = msg_data.modata;  
      MDArray<Matrix>mtx = md->GetMatrixArray(MODATA_MATRIX);  
      MDArray<LONG>farr = md->GetLongArray(MODATA_FLAGS);  
      
      //Start from the end of the array and work backwards so we never get index errors  
      for(LONG i = count - 1; i >= 0; --i)  
      {  
          //Only grab the visible clones  
          if (!(farr[i]&MOGENFLAG_CLONE_ON) || (!(farr[i]&MOGENFLAG_DISABLE)))  
          {  
              Vector pos = mtx[i].off;  
              GePrint("X: " + LongToString(pos.x)+ " Y: " + LongToString(pos.y) + " Z: " + LongToString(pos.z));  
          }  
      }  
      
      //Check if the clones are user owned  
      //If they are user_owned then we have to free the memory manually ourselves  
      //Otherwise..C4D will free the memory for us  
      if(msg_data.user_owned)   
      {  
          GePrint("userOwned");  
          MoData::Free(msg_data.modata);  
      }
    

    -ScottA



  • On 06/01/2014 at 13:32, xxxxxxxx wrote:

    You're getting that error because in the SDK example the flags array is defined as an array of LONGs - which you'd expect - but you've defined it as a Matrix array. So the compiler rejects it with the error message you showed. Don't forget to change the call to GetLongArray as well as changing the array type.

    Steve



  • On 06/01/2014 at 13:52, xxxxxxxx wrote:

    Doh!
    Silly me. I fixed the code.

    Thanks Steve,
    ScottA



  • On 19/04/2014 at 08:56, xxxxxxxx wrote:

    Just to dig this thread back up..

    I`m looking at getting the bounding radius of the individual clones:

    I can do this by accessing the Scale() parameter of the modata matrix ( similar to getting the .off position as in Scott`s code above ) and then multiply it by the GetRad() of the original base object being cloned and adding that result to the GetRad() value.  So basically - original object radius + ( clone scale * original object radius ) = actual clone size.

    HOWEVER - what happens if there are more than one child objects of the cloner?  The modata matrix for each clone provides the scale but doesnt differentiate between which child object its cloned from..

    In GetMoDataReference theres an index parameter, which is apparently for objects with multiple modatas ( text being an example apparently ) but i cant see any way of using that..  I thought maybe thered be a separate modata for each source clone if that makes sense.

    So - is there any way to do this or is it just not possible?  Any way to tell which clone came from which source object or anything like that..?

    Any ideas or suggestions appreciated..



  • On 19/04/2014 at 09:20, xxxxxxxx wrote:

    Just thinking out loud here..  I guess if the Cloner is set to Iterate, i could then step through the matrix array and reference the Scale value to each source objects GetRad value in order - but if the Cloner is set to Random or something else that isn`t going to be possible..



  • On 19/04/2014 at 09:33, xxxxxxxx wrote:

    Ahh wait - i`ve just found the MoData.GetVectorArray() which has MODATA_SIZE and MODATA_CLONE etc..  Dunno why i never saw those *doh*

    MODATA_SIZE seems to always return 1 so i think i was right the first time.

    MODATA_CLONE is SUPPOSED to return the clone offset ( which child of the cloner is getting cloned ) but i`m getting 0 all the time from it..

    Will keep plugging away at this :)



  • On 19/04/2014 at 11:39, xxxxxxxx wrote:

    Here`s the test code so far:

    LONG m;
    Vector thisPos;
    Vector objSize;
    Vector thisScale;
    BaseObject* childObj;
    c4d_misc::BaseArray<Vector> childObjSizeArray;
      
    BaseList2D *bl = objList->ObjectFromIndex(doc, 0);  // Im getting my Cloner from an INEXCLUDE_LIST
    BaseObject *curObj = (BaseObject* )bl;
      
    if (bl->GetTypeName() == "Cloner")
    {
    	BaseTag *tag = curObj->GetTag(ID_MOTAGDATA);
    	GetMoDataMessage msg_data;
    	tag->Message(MSG_GET_MODATA, &msg_data);
      
    	childObj = curObj->GetDown();                       // First Child of Cloner Object //
    		
    	childObjSizeArray.Append(childObj->GetRad());       // Add Bounding Radius to Array //
      
    	while (childObj->GetNext() != NULL)                 // Add Bounding Radius of all other Child Objects to Array if more exist //
    	{
    		childObj = childObj->GetNext();
    		childObjSizeArray.Append(childObj->GetRad());
    	}
      
    	if (msg_data.modata)
    	{
    		LONG numClones = msg_data.modata->GetCount();
    		MoData *md = msg_data.modata;
      
    		MDArray<Matrix>mtx = md->GetMatrixArray(MODATA_MATRIX);
    		MDArray<LONG>farr = md->GetLongArray(MODATA_FLAGS);
    		MDArray<Vector>cloneSizeArray = md->GetVectorArray(MODATA_SIZE);
    		MDArray<Real>cloneOffsetArray = md->GetRealArray(MODATA_CLONE);
      
    		for (m=0; m<numClones; m++)
    		{
    			//Only grab the visible clones
    			if (!(farr[m]&MOGENFLAG_CLONE_ON) || (!(farr[m]&MOGENFLAG_DISABLE)))
    			{
    				thisPos = mtx[m].off * curObj->GetMg();      // The Global Position of the clone //
    				thisScale = mtx[m].Scale();                  // The Scale component of the clones Matrix //
    				objSize = childObjSizeArray[0]*2;            // Multiply object radius ( only using the first child in the array for this example ) by two to get full size //
      
    				GePrint("-----------");
    				GePrint("Supposed Offset..");
    				GePrint(LongToString(cloneOffsetArray[m]));     // Always returns zero //
    				GePrint("Supposed Size..");
    				GePrint(LongToString(cloneSizeArray[m].x));     // Always returns 1 //
    				GePrint(LongToString(cloneSizeArray[m].y));     // Always returns 1 //
    				GePrint(LongToString(cloneSizeArray[m].z));     // Always returns 1 //
    				GePrint("Scale..");
    				GePrint(LongToString(thisScale.x));        // Returns an integer dependant on the scaling caused by Random Effector //
    				GePrint(LongToString(thisScale.y));        // Returns an integer dependant on the scaling caused by Random Effector //
    				GePrint(LongToString(thisScale.z));        // Returns an integer dependant on the scaling caused by Random Effector //
    				GePrint("ObjectSize..");
    				GePrint(LongToString(objSize.x));        // Returns the Cloners child objects actual size //
    				GePrint(LongToString(objSize.y));        // Returns the Cloners child objects actual size //
    				GePrint(LongToString(objSize.z));        // Returns the Cloners child objects actual size //
    			}
    		}
      
    	}
    }
    

    So thats the test and the results im getting are in the comments..

    The MODATA_SIZE and MODATA_CLONE arrays are always returning i think a default value.

    The Scale() value im getting from the clones matrixes is more useful, as it is reflecting the scaling cause by the Random Effector.  The only problem with that is that its returning a whole number ( ie 0, 1, 2, 3 etc ) rather than an intermediate scale value ( like 0.4, 0.8, 2.3 etc )..  Maybe thats just the nature of Vectors and im missing something here..

    In the code above, i`ve included a loop to iterate through children of the Cloner object and append their GetRad() sizes to an array, but inside the main loop i only refer to the first entry ( just for example/testing sake )..

    Any ideas / observations / wisdom appreciated..



  • On 19/04/2014 at 15:46, xxxxxxxx wrote:

    Seems the way i had my pointers/references set up for the matrix Scale() variable above was wrong.  Prints out the correct values when changed to the following:

    Vector *thisScale;
    thisScale = &mtx[m].Scale();           //  The Scale component of the clones Matrix //
    GePrint("Scale..");
    GePrint(RealToString(thisScale->x));   //  Returns correct scale - 0.98 for example //
    GePrint(RealToString(thisScale->y));   //  Returns correct scale - 1.32 for example //
    GePrint(RealToString(thisScale->z));   //  Returns correct scale - 1.14 for example //
    

    Which will give 100% accurate clone sizes when multiplied by the child of the clone objects GetRad() size.

    But doesn`t solve the issue of more than one child object, so still have to figure out how to access MODATA_SIZE and MODATA_CLONE.  I have a feeling those only work with Effector plugins but will keep trying..

    /End Monologue.



  • On 19/04/2014 at 17:27, xxxxxxxx wrote:

    And here`s the working code for finding the clones accurate boundingbox size and also the index number of which child of the cloner the particular clone represents:

    //Only grab the visible clones
    if (!(farr[m]&MOGENFLAG_CLONE_ON) || (!(farr[m]&MOGENFLAG_DISABLE)))
    {
    	thisPos = mtx[m].off * curObj->GetMg();  // The Global Position of the clone //
    	thisScale = &mtx[m].Scale();  // The Scale component of the clones Matrix //
    	thisClone = &cloneOffsetArray[m];              // The number of the Cloner objects Child being used - represented as a blend value ( ie between 0 and 1 ) // 
    							
    	int clonedObjIndex = int(floor(*thisClone/(float(1)/childObjSizeArray.GetCount())));  // Blend value converted to a useable Index integer //
      
    	thisSize = childObjSizeArray[clonedObjIndex];
      
    	thisSize.x *= thisScale->x;
    	thisSize.y *= thisScale->y;
    	thisSize.z *= thisScale->z;
      
    	GePrint("this Clone Size..");
    	GePrint(RealToString(thisSize.x));
    	GePrint(RealToString(thisSize.y));
    	GePrint(RealToString(thisSize.z));
    	
    }
    

    Hope that`s useful to somebody!



  • On 05/05/2014 at 13:29, xxxxxxxx wrote:

    Well, i'm a totally newbie in plugin programming, i'have been reading for a while now i've decided to register, this forum is impressive. First thank you all for the tons of infos that a curious like me can read freely, an invaluable source, really.
    I've a question that i think could be related to this post, or that can be somewhat close to.
    Inside a ModifyPoints() function of my Test Effector plugin i can, as example,  get all clones matrix retrieving a pointer with:

    MDArray<Matrix> \*mat_array = md->GetMatrixArray(MODATA_MATRIX);
    

    now i can change, as example, the position of all the clones in a 'for' cycle (previously i multiply for the GenMg matrix, obtaining the global position).

    mat_array _.off = Vector (Noise()\*100,Noise()\*200,Noise()\*300);
    

    the dropeffector plugins is clear about this, but i've a question: in the successive call of ModifyPoints() i expect, when i read again the MODATA_MATRIX, to find all the new positions that i've set in the previous frame....but this doesn't happen.
    The object's position is update frame after frame in the screen, but the

    MDArray<Matrix> *mat_array = md->GetMatrixArray(MODATA_MATRIX);

    retrieve a matrix array that is always the same, every position is the start position of the clones inside the cloner...and...i don't figure out why 
    Someone can give me an hint? Thank you so much for dedicating me some of your time and sorry but...my english is more rusty than my coding skills 


Log in to reply