How to preserve the animation of a sphere
-
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?
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!
-
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 -
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
-
@i_mazlov Or is there a way to preserve the animation after using MCOMMAND_EXPLODESEGMENTS
-
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 -
@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();
-
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,
IliaThe 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; }
-
This post is deleted! -
@i_mazlov thank you very much