Multiple instances of ObjectData plugin?
-
Hi all, my first post here. Thank you for the opportunity.
I'm developing a C++ ObjectData plugin. During testing it seems that straight from the start multiple instances of the plugin are created. Why is that and can it be avoided?
I've cobbled together a bare bones plugin (called Minst, plugin ID=1111111) which displays the same behaviour as my actual plugin.
Here's the output to the Console:
I believe the Console shows that multiple instances of Minst are created by calling Init() more than once. (My more complex plugin also calls GetDDescription() more than once, so this multiple calling of the same functions can become quite involved very quickly. That's why I would very much like to know the reasoning behind it.)
The Console also shows that GetVirtualObjects() is only called for the first instance of the plugin, not the other two, which seems a good thing. (However, it also shows that GetVirtualObjects() is called twice in a row, one right after the other, which seems a bit superfluous.)
Here 's the code:
main.h
#ifndef MAIN_H #define MAIN_H Bool RegisterMinst(); #endif
main.cpp
#include "c4d.h" #include "main.h" Bool PluginStart() { return RegisterMinst() != 0; } void PluginEnd() { } Bool PluginMessage(Int32 id, void* data) { switch (id) { case C4DPL_INIT_SYS: { return g_resource.Init() != 0; } case C4DMSG_PRIORITY: { return true; } default: return false; } }
minst.cpp
#include "c4d.h" #include "main.h" #define ID_MINST 1111111 // Temporary plugin id. Replace this with a proper ID. class Minst : public ObjectData { INSTANCEOF(Minst, ObjectData) public: Bool Init(GeListNode* node) final; BaseObject* GetVirtualObjects(BaseObject* op, HierarchyHelp* hh) final; Bool Message(GeListNode* node, Int32 type, void* t_data) final; static NodeData* Alloc() { return NewObjClear(Minst); } }; Bool Minst::Init(GeListNode* node) { maxon::String message = FormatString("Init: Plugin Instance=0x@, Plugin ID=@", (void*)this, node->GetType()); ApplicationOutput(message); return true; } Bool Minst::Message(GeListNode* node, Int32 type, void* t_data) { return true; } BaseObject* Minst::GetVirtualObjects(BaseObject* op, HierarchyHelp* hh) { maxon::String message = FormatString("GetVirtualObjects: Plugin Instance=0x@, Plugin ID=@", (void*)this, op->GetType()); ApplicationOutput(message); return nullptr; } Bool RegisterMinst() { ApplicationOutput("RegisterMinst"); return RegisterObjectPlugin(ID_MINST, "Minst"_s, OBJECT_GENERATOR, Minst::Alloc, ""_s, nullptr, 0); }
Can someone please shed some light on this?
Thank you.
-
Hello @wnoyce,
Thank you for reaching out to us and welcome to the Plugin Café.
Why are methods being called multiple times?
What you are writing there is called an (object) generator in the terminology of Cinema 4D. The idea is to drive some form of dynamic geometry. All plugin hooks that implement classic API scene elements such as objects, tags, materials, shaders, etc. derive from
NodeData
. A plugin comes in two layers, the interface layer and the plugin layer.- Interface Layer: All "tangible" scene elements (objects, tags, ...) in a document are derived from
C4DAtom
and are at least represented by aBaseList2D
or more specific types such asBaseObject
,BaseTag
, etc. The entities are the front end of the (public) Cinema 4D API. An object in the Object Manager is aBaseObject
, a material in the Material Manger aBaseMaterial
, and when aBaseDocument
(which is itself aBaseList2D
) is traversed, it will yield scene elements with these types. - Plugin Layer: This is the backend of Cinema 4D, what you are implementing with your plugin. An
ObjectData
plugin is the backend to drive aBaseObject
, aTagData
the backend for aBaseTag
, etc. Cinema 4D uses the plugin layer to drive its classic API scene graph. The plugin layer can fully access the interface layer, but the interface layer only has limited access to the plugin layer. There are also restrictions in place regarding modifying the interface layer from the plugin layer, because the plugin layer is mostly executed in parallel.
It is important to understand that you more or less are only providing a 'cooking recipe' for an object instance, not the object instance itself. Cinema 4D will reallocate, reinitialize, and rebuild things all the time when managing a scene.
ObjectData::GetVirtualObjects()
will be called every time Cinema 4D must generate the cache for the associatedBaseObject
. This will happen for example when the user changes a parameter, but also on other occasions.NodeData::GetDDescription
will be called whenever Cinema 4D must evalute the description of the node (which can be quite often).NodeData::Init
will also be called more the once, because Cinema 4D is reallocating nodes quite often in the background. As pointed out in the docs:
The parameters of a node must be initialized in this method. NodeData::Init is being called frequently due to nodes being reallocated in the background by Cinema 4D. One should therefore be careful with carrying out computationally complex tasks in this function.
So, it is normal that these methods are being called more than once. It might not always be obvious for an outside observer why a node is being reallocated and reinitialized; an example would be Asset API preset assets, which for example are reallocating nodes multiple times when invoked.
Why is my
GetVirtualObjects
only called twice for three object instances?You return the null pointer in your
GetVirtualObjects
method, indicating a memory error to Cinema 4D. This can cause Cinema 4D to cease to call this plugin hook when executing the passes on a scene. So, it seems likeGVO
is being called twice for the first instance of your plugin object and then zero times for the remaining two.Also, the pointer to the plugin hook is a poor way of identifying plugin instances, as the plugin hook is just the driving interface, and not the actual entity. You get passed in the actual entity in most methods (
Init, Message: node
,GVO: op
) and can also always retrieve it yourself withNodeData::Get
, which will yield theGeListNode
which is (currently) representing your plugin instance.Cheers,
Ferdinand - Interface Layer: All "tangible" scene elements (objects, tags, ...) in a document are derived from
-
@ferdinand Thank you very much for your elaborate answer!
Explaining the difference between the interface layer and the plugin layer was very enlightening. I now understand that calling functions more than once is all part of the package and that
this
does not refer to the actual plugin instance. Thank you again.