Start a new topic
Answered

Implementing IObject Interface on an Extension

I'm trying to understand how the IObject interface works with extensions. The goal for my extension is to have the ability to add other extensions to it, and have the parent extension handle logic based on them. I know that this vague structure is possible based on the Generic Bucket extension, where additional bucket faces can be added by right-clicking the parent extension:

image


 

My parent extension is initialized with the IObject, IMechanismAddOn, and ISceneAddOn interfaces. The child extension is initialized with the IExtension and IMobile interfaces. In the parent extension's constructor, I've tried to add a child extension the following two ways:

 

this->add(VxSim::VxExtensionFactory::create(VxSim::VxFactoryKey(VxSim::VxUuid("{<our child extension's uuid>}"), "", "")));
this->getProxy()->add(VxSim::VxExtensionFactory::create(VxSim::VxFactoryKey(VxSim::VxUuid("{<our child extension's uuid>}"), "", "")));

 


With both methods, the add method returns True. In Vortex, when our parent extension is added, the child extensions are not visible in the sidebar. We can access them through a Python script:

 

def on_add_to_universe(self, universe):
    print([x.getName() for x in self.getParameter('parameter').value.toObject().getExtensions()])

 

 

The child extensions do correctly appear in the list of extensions, and we can access them through a script. However, the goal is for these child extensions to be visible in the editor.

 

It is my understanding that all elements visible in the Vortex Editor's Explorer panel are added to a HierarchyView object. Adding these is simple enough in a folder extension through a Python script:

 

folder_uuid = '{01977b1e-2e11-4d7e-9ca9-bc675e7e04d0}'
hierarchyView_uuid = '{0d8917b9-8f87-4c7d-ba11-a384fd8ce2ed}'
myFolder = VxExtensionFactory.create(VxFactoryKey.createFromUuid(folder_uuid)).toObject()
myFolder.add(VxExtensionFactory.create(VxFactoryKey.createFromUuid(hierarchyView_uuid)))


At that point, the HierarchyView's items parameter can be accessed and manipulated normally. However, our parent extension refuses to add the HierarchyView extension with no error message. The add operation returns False in both a python script and the C++ extension. In fact, our parent extension can't add any extension that implements the IObject interface, but can add any extension that only implements IExtension. None of these extensions appear in the sidebar.


My assumption at this time is that we must specify at some point which extensions can be added by their UUID, but I haven't been able to find any mention of this in the documentation.

 

From what I can tell by looking at other extensions, everything that Vortex can recognize as a VxObject comes packaged with HierarchyView by default, if that extension is initialized in the editor. However, a HierarchyView is not included by default if created through code. The HierarchyView can, however, be added to extensions created in code.

 

My questions are:

 

1) What Interface must be implemented for an extension to have children that implement IObject?

2) What must the constructor of an extension include to have a HierarchyView by default?

3) What is necessary for an extension to add child extensions through the editor similar to the way a Generic Bucket can add faces? 


Best Answer

Hi, 


What version of Vortex Studio are you using? I've tried it in the latest release 2017c and it works fine. Maybe you need to upgrade to the latest version. I know that several bugs in this area have been fixed for that build.


However, adding an extension in the constructor of an IObject will not work well with serialization. When you save your mechanism or scene in a file, all extensions and objects are serialized. So when you load the file, all objects in the file are recreated as they were when they were saved.The object has its child extensions automatically added, but the constructor will also create one, so there will be two extensions. One will be added every time the file is loaded.... Not that good.


One way to make it work is to make the IObjet remember the extension it has created. A good place is in overriding IExtension::onActive

    

class MyObject : public IObject
{
   // the object needs to remember the auto generated extention
    VxSim::VxSmartInterface<MyExtension> mAutoCreatedExtension ;

    // onActive is called when the extension is picked up by a ISimulatorModule
    // it is a good place to check if the required extension is there
    virtual void onActive() override
    {
        mAutoCreatedExtension = getProxy()->findExtensionByName("AutoCreatedExtension");
        if (!mAutoCreatedExtension.valid())
        {
            mAutoCreatedExtension = VxSim::VxSmartInterface<MyExtension>::create();
            mAutoCreatedExtension->setName("AutoCreatedExtension");
            add(mAutoCreatedExtension);
        }
    }
}; 

 

So, to reply to your questions directly

1) If IObject::isCompatible(VxExtension* extension) return true when extension is of a valid type, the extension will be added the object.

 

class MyObject : public VxSim::IObject
{
    virtual bool isCompatible(VxSim::VxExtension* extension) const override
    { 
        return VxSim::VxSmartInterface<MyExtension>(extension).valid();
    }
};

 


2) HierarchyView is an internal extension and it is created as needed by the editor if necessary. 


3) It is not currently possible to extend the editor to add custom extensions to custom objects. You could add this behavior directly in the object by added an input boolean that would trigger the creation of a new child extension.

 

class MyObject : public VxSim::IObject
{
protected:
    // declare an input that will trigger adding an extension
    VxData::Field<bool> inputAddExtension;

public:
    MyObject(VxSim::VxObject* proxy) 
        : IObject(proxy)
        , inputAdd(false, "Add Extension", &proxy->getInputContainer())
    {
        // an observer on the input will trigger creating and adding a new extension
        // when the input is true
        inputAdd.addObserver([this](bool add)
        {
            if (add)
            {
                auto ext = VxSim::VxSmartInterface<MyExtension>::create();
                this->add(ext);
            }
        });
    }

};

 



That's really strange and it looks like a bug in the Editor. However, I do not get that bug when using Vortex Studio 2017c.


You may want to open your Mechanism in Vortex Studio Player and have a look at the content debugger. You will see the raw content of the your mechanism.

Answer

Hi, 


What version of Vortex Studio are you using? I've tried it in the latest release 2017c and it works fine. Maybe you need to upgrade to the latest version. I know that several bugs in this area have been fixed for that build.


However, adding an extension in the constructor of an IObject will not work well with serialization. When you save your mechanism or scene in a file, all extensions and objects are serialized. So when you load the file, all objects in the file are recreated as they were when they were saved.The object has its child extensions automatically added, but the constructor will also create one, so there will be two extensions. One will be added every time the file is loaded.... Not that good.


One way to make it work is to make the IObjet remember the extension it has created. A good place is in overriding IExtension::onActive

    

class MyObject : public IObject
{
   // the object needs to remember the auto generated extention
    VxSim::VxSmartInterface<MyExtension> mAutoCreatedExtension ;

    // onActive is called when the extension is picked up by a ISimulatorModule
    // it is a good place to check if the required extension is there
    virtual void onActive() override
    {
        mAutoCreatedExtension = getProxy()->findExtensionByName("AutoCreatedExtension");
        if (!mAutoCreatedExtension.valid())
        {
            mAutoCreatedExtension = VxSim::VxSmartInterface<MyExtension>::create();
            mAutoCreatedExtension->setName("AutoCreatedExtension");
            add(mAutoCreatedExtension);
        }
    }
}; 

 

So, to reply to your questions directly

1) If IObject::isCompatible(VxExtension* extension) return true when extension is of a valid type, the extension will be added the object.

 

class MyObject : public VxSim::IObject
{
    virtual bool isCompatible(VxSim::VxExtension* extension) const override
    { 
        return VxSim::VxSmartInterface<MyExtension>(extension).valid();
    }
};

 


2) HierarchyView is an internal extension and it is created as needed by the editor if necessary. 


3) It is not currently possible to extend the editor to add custom extensions to custom objects. You could add this behavior directly in the object by added an input boolean that would trigger the creation of a new child extension.

 

class MyObject : public VxSim::IObject
{
protected:
    // declare an input that will trigger adding an extension
    VxData::Field<bool> inputAddExtension;

public:
    MyObject(VxSim::VxObject* proxy) 
        : IObject(proxy)
        , inputAdd(false, "Add Extension", &proxy->getInputContainer())
    {
        // an observer on the input will trigger creating and adding a new extension
        // when the input is true
        inputAdd.addObserver([this](bool add)
        {
            if (add)
            {
                auto ext = VxSim::VxSmartInterface<MyExtension>::create();
                this->add(ext);
            }
        });
    }

};

 


That was very informative - thank you! However, there is one issue I'm having with this approach:


When a file is saved and exited, the child extensions of the IObject disappear from the sidebar. They are still present in the extension as we can access them from a script, but are not visible as nested extensions. For example, we can set the object to add an extension:


image


Save that file, and then re-open it:

image


That Extension is still present in the Object, but is not accessible without a script to expose it. The child extensions we're using are implementing IMobile so they can be transformed and have those transforms associated with graphics - if they are not visible in the sidebar, we can't easily select them to move them in position if we decide they need to be moved. Is there a way of preserving them in the sidebar as child extensions?


Login to post a comment