Hide children in Objects-Tree

  • On 18/06/2018 at 02:08, xxxxxxxx wrote:

    Hello there!

    I was wondering if it is possible to hide chidren of an object generator plugin. I'm creating a bunch of stuff, like MoSplines and Instances and I don't want the user to go in there and change stuff. I want to hide those in the tree. Is that possible?


  • On 18/06/2018 at 23:48, xxxxxxxx wrote:

    Come on guys, you can do it! I can't! :D

  • On 19/06/2018 at 04:54, xxxxxxxx wrote:

    Never mind. Here's the solution: NBIT_OHIDE

  • On 19/06/2018 at 08:53, xxxxxxxx wrote:


    I hope you don't mind, but first of all, I'd like to ask you to please not cross-post questions on multiple forums (https://www.c4dcafe.com/ipb/forums/topic/103431-hide-children-in-objects-tree/). Chances are people start working on your question in parallel and wasting time because they don't see you already have a solution from another place. Having said this, thanks for providing the answer from C4DCafé here.

    Secondly there's usually no need to bump threads in this forum. This is MAXON's official forum for developers and we, MAXON's SDK Team, usually make sure you get an answer. It may take a day or two, depending on our workload, but usually (and I hope somebody corrects me if I'm wrong) we get to every thread.

    And now:
    Welcome to the Plugin Café forums 🙂

    For OBIT_HIDE: Yes, it's true, it can be used to hide objects in Object Manager. But I'm not sure that's the way to go in your case. You said, you have a object generator plugin (ObjectData) and you are creating a "bunch of stuff". Can you elaborate a bit on how and where (in which function) you generate stuff? Usually the "stuff" generated by an ObjectData gets returned from GetVirtualObjects() (and alike) and by doing so, this stuff per se is not visible in the OM. No need to hide. Whereas children of a generator from a user point of view are usually considered input objects of a generator. The important point is, you are not allowed to modify the scene (insert objects) from within for example GetVirtualObjects(). Doing so will most likely end up with Cinema getting unstable and crashing eventually.
    So, maybe you can provide us with a few more details, what you are actually trying to do/achieve, and then we can probably help you out, explaining the concepts and suggesting a route to go.

  • On 19/06/2018 at 12:32, xxxxxxxx wrote:

    Hi Andreas,

    Nun mal langsam. First of all, I thought after almost a full day of waiting it's absolutely okay to post the question in another forum. I didn't know that the forum also belongs to you guys. Because, who has two forums for the same thing right? And I really don't have "days" to wait around.
    And here's the other issue. Sorry to be so blunt, but the documentation regarding making plugins for Cinema is one of the worst ever. I was coding for years (not python) and it's really hard to learn anything about how to make a plugin. The examples are fine and all, but no comments in the code, no explanation of anything really. So thats how people like me end up generating "a bunch of stuff". At least it's working fine this way. If you guys can't create some good documenation of your code with good examples (Tutorials, especially for beginners), then don't be mad at me!

    Anyways. according to you my code is probably not the way it should be. But I will post it anyway, because I want to know how a pro like you, would achieve the same thing. If you still are interested in helping me, then please explain WHY is it possible for me, to modify the scene, but actually I'm not allowed... bad SDK maybe? ;-)

    So here's the code. Essentially I want to create cascaded "stuff".

        class MSFibreData(c4d.plugins.ObjectData) :  
            PLUGIN_ID = 1000001  
            PLUGIN_NAME = 'MS-Fibre'  
            PLUGIN_DESC = 'Omsfibre'  
            PLUGIN_ICON = load_bitmap('res/icons/MS-Fibre.tiff')  
            PLUGIN_DISKLEVEL = 0  
            a = 1  
            def Register(cls) :  
                return c4d.plugins.RegisterObjectPlugin(  
                    cls.PLUGIN_ID, cls.PLUGIN_NAME, cls, cls.PLUGIN_DESC, cls.PLUGIN_INFO,  
                    cls.PLUGIN_ICON, cls.PLUGIN_DISKLEVEL)  
            def __init__(self) :  
            def Init(self, node) :  
                self.InitAttr(node, c4d.BaseList2D, [res.MS_FIBRE_SETUP_SPLINENCURVE])  
                self.InitAttr(node, c4d.BaseList2D, [res.MS_FIBRE_SETUP_CONTOURNCURVE])  
                self.InitAttr(node, c4d.BaseList2D, [res.MS_FIBRE_SETUP_SPLINENMATERIAL])  
                self.InitAttr(node, c4d.BaseList2D, [res.MS_FIBRE_SETUP_LIGHTNMATERIAL])  
                self.InitAttr(node, float, [res.MS_FIBRE_SWEEPNLIGHT_POSITION])  
                self.InitAttr(node, float, [res.MS_FIBRE_SWEEPNLIGHT_LENGTH])  
                self.InitAttr(node, float, [res.MS_FIBRE_SWEEPNCABLE_WIDTH])  
                self.InitAttr(node, float, [res.MS_FIBRE_SWEEPNCABLE_END])  
                self.InitAttr(node, float, [res.MS_FIBRE_SWEEPNCABLE_START])  
                node[res.MS_FIBRE_SWEEPNLIGHT_POSITION] = 1.0  
                node[res.MS_FIBRE_SWEEPNLIGHT_LENGTH] = 0.3  
                node[res.MS_FIBRE_SWEEPNCABLE_WIDTH] = 4.0  
                node[res.MS_FIBRE_SWEEPNCABLE_END] = 1.0  
                node[res.MS_FIBRE_SWEEPNCABLE_START] = 0.0  
                return True  
            def initializeRig(self, op) :  
                theNull = op  
                theSweep2 = c4d.BaseObject(c4d.Osweep)  
                theSweep2.SetPhong(True, True, 60.0)  
                c4d.documents.GetActiveDocument().InsertObject(theSweep2, theNull)  
                theMoSpline2 = c4d.BaseObject(440000054)  
                theMoSpline2[c4d.MGMOSPLINEOBJECT_MODE] = 1  
                c4d.documents.GetActiveDocument().InsertObject(theMoSpline2, theSweep2)  
                theInstance2 = c4d.BaseObject(5126)  
                c4d.documents.GetActiveDocument().InsertObject(theInstance2, theSweep2)  
                theSweep1 = c4d.BaseObject(c4d.Osweep)  
                theSweep1.SetPhong(True, True, 60.0)  
                c4d.documents.GetActiveDocument().InsertObject(theSweep1, theNull)  
                theMoSpline1 = c4d.BaseObject(440000054)  
                theMoSpline1[c4d.MGMOSPLINEOBJECT_MODE] = 1  
                c4d.documents.GetActiveDocument().InsertObject(theMoSpline1, theSweep1)  
                theInstance1 = c4d.BaseObject(5126)  
                c4d.documents.GetActiveDocument().InsertObject(theInstance1, theSweep1)  
                return True  
            def GetVirtualObjects(self, op, hh) :  
                dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTY_DATA)  
                if dirty is False: return op.GetCache(hh)  
                firstChild = op.GetDown()  
                if not firstChild: self.initializeRig(op)  
                fibre = op  
                # Cable-Sweep  
                cableSweep = fibre.GetDown()  
                cableSweepTag = cableSweep.GetTag(c4d.Ttexture)  
                mat1 = op[res.MS_FIBRE_SETUP_SPLINENMATERIAL]  
                if mat1 != None:  
                # Base-MoSpline  
                baseMoSpline = fibre.GetDown().GetDown().GetNext()  
                baseMoSpline[c4d.MGMOSPLINEOBJECT_GROWTH_END] = op[res.MS_FIBRE_SWEEPNCABLE_END]  
                # Light-Sweep  
                lightSweep = fibre.GetDown().GetNext()  
                lightSweepTag = lightSweep.GetTag(c4d.Ttexture)  
                lightSweep[c4d.SWEEPOBJECT_GROWTH] = op[res.MS_FIBRE_SWEEPNLIGHT_POSITION]  
                lightSweep[c4d.SWEEPOBJECT_STARTGROWTH] = lightSweep[c4d.SWEEPOBJECT_GROWTH] - op[res.MS_FIBRE_SWEEPNLIGHT_LENGTH]  
                mat2 = op[res.MS_FIBRE_SETUP_LIGHTNMATERIAL]  
                if mat2 != None:  
                # Light-Sweep-MoSpline  
                lightMoSpline = fibre.GetDown().GetNext().GetDown().GetNext()  
                lightMoSpline[c4d.MGMOSPLINEOBJECT_SPLINE_WIDTH] = op[res.MS_FIBRE_SWEEPNCABLE_WIDTH] * 1.1  
                # Contour-Instances  
                theInstance1 = fibre.GetDown().GetDown()  
                theInstance1[c4d.INSTANCEOBJECT_LINK] = op[res.MS_FIBRE_SETUP_CONTOURNCURVE]  
                theInstance2 = fibre.GetDown().GetNext().GetDown()  
                theInstance2[c4d.INSTANCEOBJECT_LINK] = op[res.MS_FIBRE_SETUP_CONTOURNCURVE]

  • On 20/06/2018 at 08:41, xxxxxxxx wrote:


    I guess, we had a bad start. I did not intend to insult you in any way, just wanted to mention the rules here in this forum.

    In regards to our documentation, well, it's grown over a long time and only since the recent years, we have a team dedicated to SDK support. Still, we only have this much time and documentation is only one of our responsibilities and there we currently have some priority on our C++ docs. We do appreciate constructive critique, though. So, if you have any ideas or suggestions, please tell us (rather in a dedicated thread).

    Thanks for posting your code, this makes discussion and getting you on the right track a lot easier.
    As I had assumed, you are doing a few things, one shouldn't do (no insult intended, you are neither the first nor the last). I won't discuss, if this is good or bad API design, lets rather focus on getting you into the right direction.

    Here are two basic rules:

    1. NodeData derived plugins (as ObjectData for example) should not use GetActiveDocument(). Think of it this way: When the document gets rendered, it will be cloned for that purpose, so the user can continue to work in parallel. But now, your generator in the render context references the wrong document, as the active one is still the one the user is working in. Instead you'd use GetDocument() on the object passed to GetVirtualObjects().
      But wait a second, you won't need the document at all...

    2. See Threading Information, there's a bunch of stuff not allowed to be called in threaded contexts, among these every function that changes the structure of the scene (object or material tree,...).

    Now, the needed changes to your code really aren't that many or as hard as you may expect. Instead of setting theNull to op in initializeRig() just create a new Null object (c4d.BaseObject(c4d.Onull)) as parent for the created stuff and then InsertUnder() the rest below this Null object (note here, it's not completely forbidden to call InsertUnder() in GetVirtualObjects(), but it is forbidden to use InsertUnder() on an entity that is already part of the scene/document). Finally return the parenting Null object from initializeRig() and in the very end from GetVirtualObjects(). And in GetVirtualObjects() don't use op as start for your object reference (after all you are just creating the new cache containing these objects) but also the parenting Null object.

    I hope this helps.
    And furthermore I hope, I was able to settle down this discussion, which looks like to have started under a bad sign. We'll be eager to be supportive as long as we can approach it constructively.

  • On 20/06/2018 at 09:26, xxxxxxxx wrote:

    Hello Andreas,

    I apologize for my reaction. I didn't mean to be insulting too. And of course, it's your forum and your rules.

    Regarding the documentation: I certainly can imagine that it must be a hard task to create a doucmenation that is easy to read/understand and covers all the different aspects of your API. Specially for C++ and Python. And yes, sometimes things grow and get big and then it's hard to keep it simple to use and find things. I get it. Let me think about it more and I may have some ideas and suggestions later.

    Thank you for explaining things and linking me those pages. I will read thru them and try to understand and implement your suggestions. I'm a noob and I might have chewed off a piece that is to big. But hey, I like challenges :D

    No worries, let's do the old "Schwamm drüber" and keep going :D


  • On 20/06/2018 at 10:19, xxxxxxxx wrote:

    Quick update: I made it work. And no child object visible :) Thanks a lot Andreas!

  • On 21/06/2018 at 07:00, xxxxxxxx wrote:


    I'm glad you got it working.

    Just a few additions I forgot yesterday:

    You may already be aware, but we have a bunch of Python examples on GitHub, especially the ObjectData ones could be of interest in this context.

    Even for Python developers some of our work in the C++ SDK documentation could be interesting. There we write "overviews" and "manuals", which provide some more context on classes and workflows. Even though the links go into the C++ API reference and the code snippets are in C++, the latter ones should mostly be simple enough to be readable for Python developers as well.

    C4DCafe: While the forum is owned and driven by a MAXON employee, it's actually not an official MAXON forum. I'm just mentioning, because you asked, why there are two forums with similar focus.

  • On 22/06/2018 at 04:09, xxxxxxxx wrote:


    Alright, yes I know about those examples. Definitely worth a bookmark :)

    Thanks for the tip regarding the C++ documentation. Will definitely check it out! And thank you for explaining the reason behind the two forums.

    I learned a lot already, with your help.
    Rock on!

Log in to reply