Loft incorrectly returning input objects

  • Hello,

    For my plugin I am creating a SplineObject by using a child PolygonObject as input. From the child object I am properly getting the polygon data and using it to create the SplineObject as seen below.

    Picture 1.png

    This is working exactly as I want it to, including the child objects not appearing in the viewport.

    The problem comes in when I create a Loft and place the copies of my plugin as its children.

    Picture 2.png

    For some reason the Loft is returning the child Cubes despite them being inputs to my plugin.

    When I make the Loft editable this is what the hierarchy is showing.

    Picture 3.png

    This is the stripped down code I am using to do this example. In the code I am just taking the points of the first polygon from the child object and using it to generate the spline. I know that it doesn't have the proper safeguards in place to prevent a crash outside of the scope of my test.

    class TestProblem : public ObjectData
    	INSTANCEOF(TestProblem, ObjectData)
    	BaseObject* GetVirtualObjects(BaseObject *op, HierarchyHelp *hh);
    	virtual SplineObject* GetContour(BaseObject* op, BaseDocument* doc, Float lod, BaseThread* bt);
    	static NodeData* Alloc() { return NewObjClear(TestProblem); }
    BaseObject* TestProblem::GetVirtualObjects(BaseObject * 	op, HierarchyHelp *hh)
    	Bool dirtyBool;
    	BaseObject* down = op->GetDown();
    	while (down != nullptr)
    		op->GetHierarchyClone(hh, down, HIERARCHYCLONEFLAGS::ASPOLY | HIERARCHYCLONEFLAGS::ASSPLINE, &dirtyBool, nullptr);
    		down = down->GetNext();
    	return nullptr;
    SplineObject* TestProblem::GetContour(BaseObject* op, BaseDocument* doc, Float lod, BaseThread* bt)
    	BaseObject *childObj = op->GetDown();
    	if (childObj == nullptr)
    		return nullptr;
    	BaseObject *cachedObject = childObj->GetCache();
    	PolygonObject *poly = (PolygonObject*)cachedObject;
    	Matrix polyObjMat = poly->GetMg();
    	if (poly == nullptr)
    		return nullptr;
    	// Get the data from the polygon object
    	const CPolygon *polyArray = poly->GetPolygonR();
    	if (polyArray == nullptr)
    		return nullptr;
    	const Vector *posArray = poly->GetPointR();
    	// Create a spline using the four points from the first polygon on the child object
    	SplineObject *spline;
    	spline = SplineObject::Alloc(2, SPLINETYPE::LINEAR);
    	spline->ResizeObject(4, 0);
    	Segment *splineSegment = spline->GetSegmentW();
    	Vector *vectors = spline->GetPointW();
    	vectors[0] = polyObjMat * posArray[polyArray[0].a];
    	vectors[1] = polyObjMat * posArray[polyArray[0].b];
    	vectors[2] = polyObjMat * posArray[polyArray[0].c];
    	vectors[3] = polyObjMat * posArray[polyArray[0].d];
    	BaseContainer *splineData = spline->GetDataInstance();
    	splineData->SetInt32(SPLINEOBJECT_CLOSED, TRUE);
    	return spline;
    Bool RegisterTestProblem()
    	return RegisterObjectPlugin(ID_TestProblem, "Test"_s, OBJECT_GENERATOR | OBJECT_ISSPLINE | OBJECT_INPUT, TestProblem::Alloc, "Test"_s, AutoBitmap("circle.tif"_s), 0);

    Is there some method to prevent the Loft, and any other similar Cinema objects, from returning the child objects of my plugin?

    Any help would be greatly appreciated.

    John Thomas

  • I've spent more time working on it and have still not found a solution. Any help would be greatly appreciated.

    John Thomas

  • Hi @JohnThomas, first and foremost I sincerely apologise for getting back to this thread so lately.

    A few notes on your code that are very likely to prevent your cubes to disappear:

    • ObjectData::GetVirtualObjects() is not supposed to return a nullptr but rather return a BaseObject::Alloc(Onull);
    • it's rather awkward how DependencyList is used in your code: from the code it's actually doing nothing without calling BaseObject::AddDependence()
    • it's also useless to invoke BaseObject::GetHierarchyClone() without storing its results in somewhere and I also have doubts on the combination of HIERARCHYCLONEFLAGS you used.
    • always check a pointer before (see poly->GetMg()) calling any method belonging to the instance it points to or accessing its data ( see accessing posArray values)

    I recommend to have a look at the Generating section of the BaseObject Manual where fundamental concepts about BaseObject generation are explained.

    Cheers, R.

  • Thanks for the response.

    From my tests if GetHierarchyClone is commented out then the child object never disappears, as opposed to not appearing in the viewport. So it does seem to be doing something.

    To be clear, my goal is to have the child objects correctly marked, (so they wouldn't appear when under the loft or at all), and to correctly retrieve the polygon/spline objects of the children for use in either GetContour(since sometimes I want to return a spline) or GetVirtualObject(since sometimes I want to return an polygon object).

    From what I understand the ways to get the polygon/spline objects are CurrentStateToObject, GetCache, GetDeformCache, and GetHierarchyClone.

    It seems like CurrentStateToObject doesn't seem like an option because cloning the object over to a new document and rebuilding it can be slow.

    Using GetCache and GetDeformCache is faster but far more complicated given the number of exceptions that need to be handled. And these can't be used with AddDependence/TouchDependenceList clears the caches right?

    GetHierarchyClone seems to work but that's only available in GetVirtualObjects, so what should be done in GetContour?

    What would be the best approach to mark the children objects of my plugins and to retrieve the contents of those children in both GetContour and GetVirtualObject?

    John Thomas

  • I still haven't had any success in getting my code to work as expected. Any help would be appreciated.

    John Thomas

  • Hi @JohnThomas, sorry for the delay in getting back to you.

    This topic was throughly discussed in a few threads already which I warmly recommend to look for.
    The first was back in 2016 with GetVirtualObjects v GetContour for Spline Gen which ended up in the creation of the Offset-Y Spline example available on @dskeithbuck / @dskeith GitHub repo.

    The second came on 2019 with GetRealSpline() not returning a result where @m_adam also come in to refine the Offset-Y Spline code in this post.

    Reading it and dissecting the code will answer all your answers.
    Last but not least, the spline example is registered passing the OBJECT_INPUT flag which as throughly discussed on OBJECT_INPUT Child Updating doesn't work in combination with Dynamic Tags.

    Cheers, R

  • Thanks for the reply.

    I went through the threads you linked but I've got a couple of clarification questions from them as I dig into them more.

    I have concerns about the speed of some of the operations that are proposed.

    From putting some prints in the GVO and GetContour of the OffsetYSpline, it seems like both GVO and GetContour are both getting called every time the plugin updates, so the entire spline is being rebuilt twice.

    Is there a concern about the speed of the plugin given that it seems to be rebuilding twice?

    From the code presented in

    def GetCloneSpline(op):
            #Emulates the GetHierarchyClone in the GetContour by using the SendModelingCommand
            #:param op: The Object to clones and retrieves the current state (take care the whole hierarchy is join into one object.
            #:return: The merged object or None, if the retrieved object is not a Spline
            # Copies the original object
            childSpline = op.GetClone(c4d.COPYFLAGS_NO_ANIMATION)
            if childSpline is None:
                raise RuntimeError("Failed to copy the child spline.")
            # Retrieves the original document (so all links are kept)
            doc = op.GetDocument() if op.GetDocument() else c4d.documents.BaseDocument()
            if not doc:
                raise RuntimeError("Failed to retrieve a Doc")
            # Inserts the object into the document, this is needed for Current State To Object operation
            # Performs a Current State to Object
            resultCSTO = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT, list=[childSpline], doc=doc)
            if not isinstance(resultCSTO, list) or not resultCSTO:
                raise RuntimeError("Failed to perform MCOMMAND_CURRENTSTATETOOBJECT.")
            # Removes the original clone object
            childSpline = resultCSTO[0]
            # If the results is a Null, performs a Join command to retrieves only one object.
            if childSpline.CheckType(c4d.Onull):
                resultJoin = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_JOIN, list=[childSpline], doc=doc)
                if not isinstance(resultJoin, list) or not resultJoin:
                    raise RuntimeError("Failed to perform MCOMMAND_JOIN.")
                childSpline = resultJoin[0]
            if childSpline is None:
                raise RuntimeError("Failed to retrieves cached spline.")
            # Checks if childSpline can be interpreted as a Spline.
            if not childSpline.GetInfo() & c4d.OBJECT_ISSPLINE and not childSpline.IsInstanceOf(c4d.Ospline) and not childSpline.IsInstanceOf(c4d.Oline):
                return None
            return childSpline

    It seems like cloning the object and inserting it back into the scene to CurrentStateToObject it and then removing it is going to be potentially really slow.

    It seems like it would be a lot of overhead to just get the input points. Forgive my ignorance, but do native Cinema objects face this sort of overhead? Would this amount of overhead be a limiting factor?

    John Thomas

  • Hi @JohnThomas, consider that spline generators using children as input are not officially supported and the solution offered, although can show from case to case performance penalties, it's the sole way to go.

    Forgive my ignorance, but do native Cinema objects face this sort of overhead?

    Yes, they do.

    Would this amount of overhead be a limiting factor?

    It heavily depends on the implementation, but I'm pretty confident to see it as a little overhead.

    Cheers, R

  • Thanks for the response.

    I'll spend some time looking into it.

    John Thomas

Log in to reply