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
    • Register
    • Login

    How to preserve the animation of a sphere

    Cinema 4D SDK
    s26 c++ windows
    2
    9
    1.1k
    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.
    • P
      pchg
      last edited by

      Hello colleagues, I used C++ to write the C4D S26 plugin on Windows 10, I import .abc and want to explode this file, but I found using SendModelingCommand()that the sphere animation disappeared, what should I do?
      111.png
      333333.png

      BaseDocument* doc = GetActiveDocument();
      BaseObject* activeObject = doc->GetActiveObject();
      if (!activeObject)
      	return break;
      ModelingCommandData mcd;
      mcd.doc = doc;
      mcd.op = activeObject;
      const bool result = SendModelingCommand(MCOMMAND_EXPLODESEGMENTS, mcd);
      if(result)
      {
      	EventAdd();
      }
      

      Although I got all the spheres, all the sphere animation was lost, Thanks in advance!

      i_mazlovI 1 Reply Last reply Reply Quote 0
      • i_mazlovI
        i_mazlov @pchg
        last edited by

        Hi @pchg ,

        You are using alembic scene file that's a little special, as after import it results in alembic objects, which differ from normal polygon objects. If you bake it using CSTO or MakeEditable commands, it would result in a plain polygon object with an alembic tag, which works as a bridge between abc file and the polygon object you have. To sum up, animated alembic objects are not easy to work with and the approach would highly depend on the goal you're trying to achieve.

        Your question looks like a follow up from the thread: How to get sphere coordinates. There you're trying to get positions of each sphere in the scene. Please elaborate on what are your intentions on this information, in other words what are you going to do with it?

        If you're only interested in points themselves then you can iterate over frames and process geometry cache at each frame. The processing can be as described in the above-mentioned thread by using the Explode Segments command. Another equally possible way is to use Select Connected command, namely you can select some point on the object and call the command to get all the points on current sphere. Then you repeat this process for other spheres.

        On the other hand, if you would like to change the polygon object, then you would need to first process the animation and then remove the alembic tag, otherwise it will always be overwriting the cache to keep it in sync with the abc file.

        Cheers,
        Ilia

        MAXON SDK Specialist
        developers.maxon.net

        P 2 Replies Last reply Reply Quote 0
        • P
          pchg @i_mazlov
          last edited by

          Thanks @i_mazlov I wrote a plugin that would read the coordinates of every sphere in every frame and then output, and get all frame coordinates of all spheres to the user

          1 Reply Last reply Reply Quote 0
          • P
            pchg @i_mazlov
            last edited by

            @i_mazlov Or is there a way to preserve the animation after using MCOMMAND_EXPLODESEGMENTS

            i_mazlovI 1 Reply Last reply Reply Quote 0
            • i_mazlovI
              i_mazlov @pchg
              last edited by

              Hi @pchg ,

              please excuse the delayed answer!

              The main issue you're struggling with here is the alembic objects that are created by alembic importer. The issue is that alembic objects keep animation data inside and export it to the alembic tag in case of CSTO and MakeEditable commands. Hence, you don't have any access to the ordinary C4D tracks with data that you could easily scrap from.

              I see the best way to proceed for you would be to iterate over frames of your animation and at each frame extract the coordinates.

              To extract coordinates you have multiple options. The lazy (but computationally expensive) way is to "bake" your alembic object with the CSTO command and then execute Explode Segments on this baked object, this allows you to process objects the way you need (calculate coordinates). After you are done, you remove the baked object, go to the next frame and then start the loop over (CSTO -> Explode Segments -> Calculate).

              If you need some more efficient way, I think calculating the coordinates without baking can be a little faster. In this case instead of doing CSTO and Explode Segments, at each frame you would need to access object's geometry and collect all the points that are connected in a single "island" (sphere). You can do this completely manually, or using helping functions in the Modeling kernel, or by sequentially executing the Select Connected command. When having all the points of your sphere, you can calculate the coordinate of the point cloud as its Center of Mass.

              Cheers,
              Ilia

              MAXON SDK Specialist
              developers.maxon.net

              P 1 Reply Last reply Reply Quote 0
              • P
                pchg @i_mazlov
                last edited by

                @i_mazlov Thanks for your explanation. How to use code to execute the "Select Connected" command and loop through all points. I have obtained all the points now, but I don't know how to execute the "Select Connected" command for each point

                BaseObject* obj = doc->GetActiveObject();
                PointObject* pointObj = static_cast<PointObject*>(obj);
                Int32 pointCount = pointObj->GetPointCount();		
                Vector* points = pointObj->GetPointW();
                
                
                i_mazlovI 1 Reply Last reply Reply Quote 0
                • i_mazlovI
                  i_mazlov @pchg
                  last edited by i_mazlov

                  Hi @pchg ,

                  Please excuse the delayed answer.

                  Select Connected() command is just an arbitrary modeling command and is executed using SendModelingCommand() function call. You can have a look at the MCOMMAND_CURRENTSTATETOOBJECT manual entry for the usage code snippet. Notice, that if you set arr field to nullptr of the ModelingCommandData struct, cinema would execute command on the currently active object. If you like to execute command on some specific set of objects, you need to assigne AtomArray of objects to the arr field.

                  I've created a code snippet that shows what I described in previous postings (attached below). It is based on our Make Cube example command.

                  Please note, this thread started plainly diverging from the original question, which violates our Support Procedures , namely the singularity of the question:

                  Singular Question: The initial posting of a question topic must contain a singular question.

                  Singular Subject: From all this follows that a topic must have a singular and sparse subject tied to a specific problem.

                  If you have any specific follow up questions, please create a new thread, formulate your question and provide the minimal code snippet that shows the issue in a reproducible way.

                  Cheers,
                  Ilia

                  The Execute() function of the "Make Cube" example:

                  	virtual Bool Execute(BaseDocument* originalDoc, GeDialog* parentManager)
                  	{
                  		iferr_scope_handler
                  		{
                  			err.DiagOutput();
                  			return false;
                  		};
                  		CheckArgument(originalDoc);
                  		CheckState(GeIsMainThread());
                  
                  		BaseObject* originalOp = originalDoc->GetFirstObject();
                  		if (!originalOp)
                  			return true;
                  
                  		if (originalOp->GetType() != Oalembicgenerator)
                  			return true;
                  
                  		// Allocate temporary document to not mess up with the existing one
                  		AutoAlloc<BaseDocument> doc;
                  		AutoAlloc<AliasTrans>		at;
                  		CheckState(at && at->Init(originalDoc));
                  
                  		// Clone the alembic object to the temporary document
                  		BaseObject* op = static_cast<BaseObject*>(originalOp->GetClone(COPYFLAGS::NONE, at));
                  		CheckState(op);
                  		doc->InsertObject(op, nullptr, nullptr);
                  		at->Translate(true);
                  
                  		// "Bake" it to get PolygonObject with the alembic tag
                  		BaseObject* bakedOp = MakeEditable(op) iferr_return;
                  		CheckState(bakedOp);
                  
                  		// Double check
                  		CheckState(bakedOp->GetType() == Opolygon);
                  		PolygonObject* polyOp = ToPoly(bakedOp);
                  		CheckState(polyOp);
                  
                  		doc->SetActiveObject(bakedOp, SELECTION_NEW);
                  		doc->SetMode(Mpoints);
                  
                  		const BaseTime timeMin = doc->GetMinTime();
                  		const BaseTime timeMax = doc->GetMaxTime();
                  		const Int32 fps = doc->GetFps();
                  
                  		BaseSelect* sel = polyOp->GetWritablePointS();
                  		CheckState(sel);
                  
                  		// Iterate over frames from minimal to maximal
                  		for (Int32 frameIdx = timeMin.GetFrame(fps); frameIdx < timeMax.GetFrame(fps); ++frameIdx)
                  		{
                  			DiagnosticOutput("Processing frame #@", frameIdx);
                  
                  			// Set current time and update the scene
                  			doc->SetTime(BaseTime((Float)frameIdx / fps));
                  			doc->ExecutePasses(nullptr, false, true, true, BUILDFLAGS::NONE);
                  
                  			const Vector* pointsPtr = polyOp->GetPointR();
                  
                  			// Keep the set of the point indices to be processed
                  			maxon::HashSet<Int32> remainingPointIndices;
                  			remainingPointIndices.ResizeTable(polyOp->GetPointCount()) iferr_return;
                  			for (Int32 idx = 0; idx < polyOp->GetPointCount(); ++idx)
                  			{
                  				remainingPointIndices.Insert(idx) iferr_return;
                  			}
                  
                  			// While there's something still not visited by us ...
                  			while (remainingPointIndices.IsPopulated())
                  			{
                  				// ... select one of the points
                  				sel->DeselectAll();
                  				sel->Select(*remainingPointIndices.Begin());
                  				
                  				// Call "Select Connected" command
                  				SelectConnected(polyOp) iferr_return;
                  				if (!sel->GetCount())
                  					break;
                  
                  				// Calculate center position
                  				Vector selectionCenter;
                  
                  				// Iterate over selected points
                  				Int32 segmentIdx = 0, idxFirst, idxLast;
                  				while (sel->GetRange(segmentIdx++, polyOp->GetPointCount(), &idxFirst, &idxLast))
                  				{
                  					for (Int32 idx = idxFirst; idx <= idxLast; ++idx)
                  					{
                  						remainingPointIndices.Erase(idx);
                  						selectionCenter += pointsPtr[idx]; // calculate center position
                  					}
                  				}
                  
                  				DiagnosticOutput("@", selectionCenter / sel->GetCount());
                  			}
                  		}
                  
                  		EventAdd();
                  		return true;
                  	}
                  

                  The helping functions for the "Make Editable" and "Select Connected" commands execution:

                  	static maxon::Result<void> SelectConnected(BaseObject* op)
                  	{
                  		iferr_scope;
                  		CheckArgument(op);
                  
                  		ModelingCommandData mcdata;
                  		mcdata.op = op;
                  		mcdata.doc = op->GetDocument();
                  		CheckState(mcdata.doc->GetMode() == Mpoints);
                  
                  		mcdata.flags = MODELINGCOMMANDFLAGS::NONE;
                  		mcdata.mode = MODELINGCOMMANDMODE::POINTSELECTION;
                  
                  		CheckState(SendModelingCommand(MCOMMAND_SELECTCONNECTED, mcdata));
                  		
                  		return maxon::OK;
                  	}
                  
                  	static maxon::Result<BaseObject*> MakeEditable(BaseObject* op)
                  	{
                  		iferr_scope;
                  		CheckState(op);
                  
                  		BaseObject* marker = nullptr;
                  		finally
                  		{
                  			if (marker)
                  			{
                  				marker->Remove();
                  				BaseObject::Free(marker);
                  			}
                  		};
                  
                  		marker = BaseObject::Alloc(Onull);
                  		CheckState(marker);
                  		marker->InsertAfter(op);
                  
                  		ModelingCommandData data;
                  		BaseContainer				bc;
                  		data.result = nullptr;
                  		data.doc = op->GetDocument();
                  		data.bc = &bc;
                  		data.mode = MODELINGCOMMANDMODE::ALL;
                  		data.flags = MODELINGCOMMANDFLAGS::NONE;
                  		data.op = op;
                  
                  		// Original op is deleted as result of make editable command
                  		CheckState(SendModelingCommand(MCOMMAND_MAKEEDITABLE, data));
                  		BaseObject* bakedOp = (BaseObject*)data.result->GetIndex(0);
                  		CheckState(bakedOp);
                  		bakedOp->InsertBefore(marker);
                  		return bakedOp;
                  	}
                  

                  MAXON SDK Specialist
                  developers.maxon.net

                  P 2 Replies Last reply Reply Quote 0
                  • P
                    pchg @i_mazlov
                    last edited by

                    This post is deleted!
                    1 Reply Last reply Reply Quote 0
                    • P
                      pchg @i_mazlov
                      last edited by

                      @i_mazlov thank you very much

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