Unsolved Object Plugin : Sweep with 2 Splines - After MakeEditable just 1 spline

Hi have a simple Object-Plugin:

  • It creates first a Sweep Nurb

  • then it creates the path spline related to the description parameters

  • it puts this spline under the sweep

  • then it loads a profile spline from an Hyperfile / printing works profile is loaded

  • then it puts this profile under the sweep

  • then the sweep is returned in the GVO

  • everthing is happening in the GVO

Returning in the GVO works, everthing is visible.
When I press MakeEditable in the active document of the node, just the sweep and the path-spline are present but not the profile

It also doesent work when I create a virtual document with the spline-profile and extract it from there

But when I press a button then I can add it to the active document....
I do not understand, because the path spline works , maybe since it is mathmetically created?
If I use just a Osplinerectangel as profile then it works

    def GetVirtualObjects(self, op, hh):       
        dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_DESCRIPTION)
        if not dirty:
            return op.GetCache(hh)       

        self.spline = BowSpline(op[c4d.PY_WINDOW_WIDTH], op[c4d.PY_WINDOW_HEIGHT], op[c4d.PY_ARCH_HEIGHT], op[c4d.PY_ARCH_SUBD]).spline.GetClone()
        self.frame = c4d.BaseObject(c4d.Osweep)
        profile = self.load_profile().GetClone()

        if op[10105]:
            self.frame.SetRelPos(c4d.Vector(0, 0,  op[10105]))

        return self.frame

    def load_profile(self):
        #c4d.documents.MergeDocument(self.new_doc, f"{path}/res/profile.c4d", c4d.SCENEFILTER_OBJECTS)
        patho = os.path.join(path, "res", "profile")
        profile = c4d.BaseObject(c4d.Ospline)
        c4d.storage.ReadHyperFile(None, profile, patho, 100)
        return profile

What I am also not understanding is, when I change the description parameter while dragging it, the sweep gets invisible and just the spline is shown which is changing
For this realtime refresh I just catched the global MSG_DESCRIPTION_POSTSETPARAMETER.
is there another way to refesh it instead of callcommand and the id for redraw, maybe DrawViews()?

def Message(self, node, type, data):     
                if type == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:
                    #if data['descid'][0].id == c4d.PY_WINDOW_WIDTH:   

My solution so far is to create a virtual document insert the object from the GVO and call ModelingCommand CURRENTSTATETOOBEJCT and return this object now it works. I got the finished object. But anyways I ask myself why it not works in the other way

Hello @thomasb,

Thank you for reaching out to us. Just as in your other thread, it is probably best to share the plugin with us, as I otherwise will be mostly guessing here too. Again, here are some things which stand out to me.

  • Doing things like self.frame and self.spline, i.e., binding a reference of the cache of the object instance op to the instance of the hook is at least very odd. What you return in GVO IS the cache of the node. By also tying the reference to the plugin hook instance, you might throw a wrench into the memory management of the C++ layer (Cinema 'owns', i.e., manages the lifetime of, the object returned by GVO). It also implies that you later want to access or even modify the cache with these plugin hook instance attributes. Accessing is technically fine, but the references might be dead by then, and modifying the caches is off limits.
  • def load_profile
    1. Using c4d.storage.ReadHyperFile in this manner seems dangerous since you clone the exact same node over and over. It might work in this context, I did not try. I would be a bit more defensive and only save the data I need. You could for example clone the data container of the node and just read/write that using c4d.storage.HyperFile. You could of course also be even more conservative and just read/write the parameters of the profile you really want to copy.
    2. load_profile is executed asynchronously because GVO is. File IO is a dangerous thing to do here, even for read operations. You should defer reading to the (non-drawing) main thread. Theoretically, the Asset API would be an alternative and you could use AssetCreationInterface.SaveDefaultPresetFromObject to create an object preset asset. But this will always invoke the object preset GUI dialog and gives you no control over the asset ID. You could wiggle yourself around this but nothing will be really won in the end [1].


[1] SaveDefaultPresetFromObject only thinly wraps AssetCreationInterface.SaveBrowserPreset and most of it can be emulated, but two crucial bits - a message for filtering/gathering the parameters to store and the special preset types handling this form of presets - are not public, even in C++. As a way out, you could either implement your own preset type (only possible in C++) or simply use something like AssetCreationInterface.CreateObjectAsset to save the whole object as an asset. The idea is then to load that asset and simply copy its data container to get the preset you are after. You would have to manually load the asset back, as shown in the C++ docs, because maxon.AssetManagerInterface.LoadAssets will load into the active document which is not what you want. Since this will effectively cause you to use c4d.documents.LoadFile to load the raw asset file, you are then again subject to the same threading restrictions as before and would have to preload your data.

MAXON SDK Specialist

Yes you are right, in relation to this the self.spline etc is unnecessary that could also be a normal local variable. Is correct.
But basically if I load a profile spline in my init method, for example, I can already save it in such a variable. I need to be able to access it from anywhere in the class.
But here it is totally superfluous....sorry