Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Get the random result of Cloner Object

    Cinema 4D SDK
    c++ 2025 windows
    2
    9
    697
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • F
      freeze
      last edited by ferdinand

      I created a Cloner Object and place a cube inside,using Random as clones type.The result clones changed with the order in Cloner Object like below:1.png 2.png

      But when I try to get the information of the clones like:

      			Matrix clonerMatrix = clonerObj->GetMg();
      			BaseTag* motag = clonerObj->GetTag(ID_MOTAGDATA);
      			if (motag == nullptr)
      				return;
      			// populate the GetMoDataMessage instance by sending a MSG_GET_MODATA
      			GetMoDataMessage modataMsg;
      			if (!motag->Message(MSG_GET_MODATA, &modataMsg))
      				return;
      
      			if (!modataMsg.modata)
      				return;
      			// access the MoData from the returned GetMoDataMessage and check its validity
      			MoData* moData = modataMsg.modata;
      			if (!moData)
      				return;
      
      			// retrieve the number of Matrix items as well as the array containing the matrix for each of them
      			Int32 matrixItemsCnt = moData->GetCount();
      			Matrix* itemsMtxArray = static_cast<Matrix*>(moData->GetArray(MODATA_MATRIX));
      

      the matrixItemsCnt always be 60 ,full of the instances in this case ,same as the Matrix I got.Any way can I just get the clones shown as the random result?Many thanks.

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        Hey @freeze,

        Thank you for reaching out to us. I do not understand your question, specifically this part:

        the matrixItemsCnt always be 60 ,full of the instances in this case ,same as the Matrix I got.Any way can I just get the clones shown as the random result?

        Could you please reiterate what you want to achieve or what you would consider incorrect/surprising about your results?

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        F 1 Reply Last reply Reply Quote 0
        • F
          freeze @ferdinand
          last edited by

          @ferdinand Sure,I mean that how can I get the cubes' matrix shown in the scene in the picture I uploaded for example.

          ferdinandF 1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand @freeze
            last edited by ferdinand

            Hey @freeze,

            I am still not quite sure how you mean that. MoGraph is a particle system under the hood, i.e., you will not find the actual cloned objects in it unless you reach into the cache of the cloner. The particle arrays is all there exists, e.g., MODATA_MATRIX for the transform of the particles. When you want to retrieve the matrix of the input object, you must get that.

            Cheers,
            Ferdinand

            Result

            98fa49cd-5e64-4294-aaeb-030bf87a8317-image.png

            Code

            import c4d
            import mxutils
            
            doc: c4d.documents.BaseDocument  # The currently active document.
            op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
            
            def main() -> None:
                """Called by Cinema 4D when the script is being executed.
                """
                moData: c4d.modules.mograph.MoData | None = c4d.modules.mograph.GeGetMoData(op) if op else None
                if not moData:
                    raise RuntimeError("Please select an object that holds MoGraph particle data.")
                
                print ("Particles:\n")
            
                # Iterate over the (local) particle matrices for this MoGraph data. Local means here in relation
                # to the MoGraph generator, i.e., cloner.
                for i, ml in enumerate(moData.GetArray(c4d.MODATA_MATRIX)):
                    print(f"{i = }, {ml = }")
            
                print ("\nInputs:\n")
            
                # Iterate over the child objects of the cloner, i.e., the clone sources.
                for node in op.GetChildren():
                    print (f"{node.GetName() = }, {node.GetMg() = }")
            
                print ("\nCache:\n")
            
                # Look into the cache of the cloner which might contain the flattened particle data (when in
                # Instance or Render-Instance mode). When the cloner is in Multi-Instance mode, the cache will
                # not be (fully) populated.
                cache: c4d.BaseObject | None = op.GetCache()
                if cache:
                    for node in mxutils.IterateTree(cache):
                        print (f"{node.GetName() = }, {node.GetMg() = }") 
            
            
            if __name__ == '__main__':
                main()
            

            MAXON SDK Specialist
            developers.maxon.net

            F 1 Reply Last reply Reply Quote 0
            • F
              freeze @ferdinand
              last edited by

              @ferdinand So how can I look into the cache of the cloner in c++ in my case with the clonerObj?I already got the moData and op

              ferdinandF 1 Reply Last reply Reply Quote 0
              • ferdinandF
                ferdinand @freeze
                last edited by ferdinand

                Hey @freeze,

                yes, clonerObj would be in your case the object which holds the generated MoGraph particle output in its cache. But generally speaking, you should not reach into caches. Sometimes it is necessary, but caches (be it generator or deform caches) are a dynamically generated part of the scene graph you usually should not interact with.

                Just getting the matrices of the elements in the cache is fine (to print them or do something similar harmless). Establishing pointers into caches or relying on the fact that there is an established cache is not fine (note that my code checks for the cache actually being populated).

                I still do not really understand what you want to do, but it strikes me as being on the 'you should really not do that' side. As I said in the beginning, MoGraph is a particle system and you are only supposed to interact with the abstract particle data.

                I have the feeling you want to interact with the MoGraph particles as if they were tangible objects in the scene graph. This is simply not supported/intended.

                Cheers,
                Ferdinand

                MAXON SDK Specialist
                developers.maxon.net

                F 1 Reply Last reply Reply Quote 0
                • F
                  freeze @ferdinand
                  last edited by ferdinand

                  @ferdinand I got the transform I need as a result matrix by following code:

                  			Matrix clonerMatrix = clonerObj->GetMg();
                  			Int32 matrixItemsCnt = moData->GetCount();
                  			Matrix* itemsMtxArray = static_cast<Matrix*>(moData->GetArray(MODATA_MATRIX));
                  			for (int matrixIdx = 0; matrixIdx < matrixItemsCnt; matrixIdx++)
                  			{
                  				auto result = clonerMatrix * itemsMtxArray[matrixIdx];
                  			}
                  

                  In this case I have 2 objects as children of clonerObj like the picture below:
                  3.png
                  Now I just want to find out that every result matrix I got before belongs "Cube" or "group" Object. Any way to do this in c++? Thanks!

                  ferdinandF 1 Reply Last reply Reply Quote 0
                  • ferdinandF
                    ferdinand @freeze
                    last edited by ferdinand

                    Hey @freeze,

                    I assume your question is how to associate a clone index with its corresponding input geometry. You can do that with MODATA_CLONE and the formula ⌊cloneWeight * childCount⌋.

                    Cheers,
                    Ferdinand

                    This is untested/uncompiled pseudo code:

                    using namespace cinema;
                    using namespace maxon;
                    
                    static Result<void> GetChildren(const BaseObject* op, BaseArray<BaseObject*>& children) 
                    {
                      iferr_scope;
                      if (!op)
                        return NullptrError(MAXON_SOURCE_LOCATION, "Parent is null."_s);
                      
                      BaseObject* child = op->GetDown();
                      while (child)
                      {
                        children.Append(child) iferr_return;
                        child = child->GetNext();
                      }
                    
                      return OK;
                    }
                    
                    static Int32 main() 
                    {
                      // We need this to terminate an error handling chain, i.e., an error returned via iferr_return.
                      // The #DebugStop will invoke a break(point) when a debugger is attached and is purely optional. 
                      iferr_scope_handler
                      {
                        DebugStop(FormatString("@: @"_s, MAXON_FUNCTIONNAME, err));
                        return 1;
                      };
                    
                      BaseObject* cloner; // Some object that holds and generates MoGraph particles.
                      MoData* mdata; // Its MoGraph data.
                      BaseArray<BaseObject*> children; // The children of the cloner.
                      GetChildren(cloner, children) iferr_return;
                    
                      const Int32 particleCount = Int32(mdata->GetCount()); // The number of MoGraph particles.
                      const Int32 childCount = children.GetCount(); // The number of cloner children, i.e., input objects.
                      const MDArray<Float> cloneWeights = mdata->GetRealArray(MODATA_CLONE); // The clone weights.
                    
                      for (Int32 i = 0; i < particleCount; i++)
                      {
                        // Get the index of the child for which this particle is for, the formula is: floor(weight * childCount).
                        // Internally we use casting to an Int32 instead of dedicated flooring which is not so nice, but I would
                        // recommend to replicate that for the sake of consistency.
                        const Int32 childIndex = Int32(
                          ClampValue(cloneWeights[i] * Float(childCount), Float(0), Float(childCount - 1))
                        );
                        
                        // The input geometry for the MoGraph particle at index #i.
                        const BaseObject* const inputGeometry = children[childIndex];
                      }
                    
                      return 0;
                    }
                    

                    MAXON SDK Specialist
                    developers.maxon.net

                    F 1 Reply Last reply Reply Quote 0
                    • F
                      freeze @ferdinand
                      last edited by

                      @ferdinand Thank you very much! The problem is solved and now I know the way of MODATA_CLONE used.

                      1 Reply Last reply Reply Quote 0
                      • First post
                        Last post