Package: Server basics

From The Foundry MODO SDK wiki
Jump to: navigation, search

A Package is a plug-in server that defines a collection of channels for an item, or a new item type. The capabilities of these new packages or types are determined by the interfaces presented by the package instance.

Overview

Items are made from a collection of packages. Some of those packages come from the item type, specifically the package defining the type and all the super-type packages. Other packages can be added dynamically to give existing items new capabilities. Each package creates a package instance which provides the implementation for the item's features. For example, interfaces allow an item to draw in the 3D view, or act as a channel modifier, or act as a 3D surface. Some can even be combined in the same item.

Headers

Tags

  • LXsPKG_SUPERTYPE -- the name of the item type to act as super-type (e.g. "LXsITYPE_LOCATOR") or "." for a root item type.
  • LXsPKG_GRAPHS --
  • LXsPKG_CREATECMD -- LXs_PKG_NODIRECTCREATE
  • LXsPKG_CONVERTCMD -- LXs_PKG_DIRECTCONVERTOK
  • LXsPKG_IS_MASK --
  • LXsPKG_SHADER_CONTEXT --
  • LXsPKG_CREATE_INDIRECT --

Sample Methods

For a plug-in package we need to define two classes, one for the package and one for the instance.

Package Instance

The instance class at minimum derives from CLxImpl_PackageInstance, and exports the ILxPackageInstance interface. It can also derive from other possible interfaces which allow the item to perform functions in the scene. The instance will be created by a spawner.

PackageInstance::Initialize()

The Initialize() method is called when the instance is associated with the item. It gets the Item Object which it can store, plus the PackageInstance Object of the super-type.

        LxResult
pins_Initialize (
        ILxUnknownID            item,
        ILxUnknownID            super)        LXx_OVERRIDE
{
        m_item.set (item);
        return LXe_OK;
}
PackageInstance::Cleanup()

Cleanup() is called before the item is destroyed.

        void
pins_Cleanup (void)                           LXx_OVERRIDE
{
        m_item.clear ();
}

Package

The plug-in package server class derives from CLxImpl_Package and exports the ILxPackage interface. Since the package will need to spawn the instances, the spawner will be a member variable.

class CMyPackage :
        public CLxImpl_Package
{
    public:
        CLxSpawner<CInstance>   inst_spawn;

        CMyPackage () : inst_spawn ("myInstSpawner") {}
        ...
};
Package::SetupChannels()

The SetupChannels() method is called the first time and instance of this package is to be created, allowing the package to define the set of channels that it wants. Channels are given by name and type, and optional settings may be applied to each one.

        LxResult
pkg_SetupChannels (
        ILxUnknownID		 addChan)     LXx_OVERRIDE
{
        CLxUser_AddChannel	 ac (addChan);

        ac.NewChannel  ("range",	LXsTYPE_DISTANCE);
        ac.SetDefault  (1.0, 0);

        ac.NewChannel  ("mode",	LXsTYPE_INTEGER);
        ac.SetDefault  (0.0, MODE_LINEAR);
        ac.SetHint     (hint_Mode);
        return LXe_OK;
}
Package::TestInterface()

The TestInterface() method is required. It just calls the same method on the spawner.

        LxResult
pkg_TestInterface (
        const LXtGUID		*guid)        LXx_OVERRIDE
{
        return inst_spawn.TestInterfaceRC (guid);
}
Package::Attach()

Attach() is called to create the instance for a new item. We use the spawner to create a new default instance which can then be customized for this object. We have to wait until the instance's own Initialize() method to know the item that's being created.

        LxResult
pkg_Attach (
        void		       **ppvObj)      LXx_OVERRIDE
{
        CMyInstance		*inst = inst_spawn.Alloc (ppvObj);

        < intialize inst >
        return LXe_OK;
}

Adding Interfaces

Additional capability, such as drawing in the 3D view, or rendering, are enabled by presenting more interfaces on the instance. The package instance for a minimal package will be declared using code similar to this.

class CMyPInstance :
        public CLxImpl_PackageInstance
{
    public:
                static void
        initialize ()
        {
        	CLxGenericPolymorph	*srv;

        	srv = new CLxPolymorph<CMyInstance>;
        	srv->AddInterface (new CLxIfc_PackageInstance<CInstance>);
        	lx::AddSpawner ("myInstance", srv);
        }

        ...
};

Adding a new interface is just a matter of adding a new parent implementation class, and presenting a new interface.

class CMyPInstance :
        public CLxImpl_PackageInstance,
        public CLxImpl_ViewItem3D
{
    public:
                static void
        initialize ()
        {
        	CLxGenericPolymorph	*srv;

        	srv = new CLxPolymorph<CMyPInstance>;
        	srv->AddInterface (new CLxIfc_PackageInstance<CMyPInstance>);
        	srv->AddInterface (new CLxIfc_ViewItem3D<CMyPInstance>);
        	lx::AddSpawner ("myInstance", srv);
        }

        ...
};

Resources

A package with a supertype tag will show up in modo as an item type that can be created and selected, and its channels can be viewed in the item list. This can be sufficient for initial testings, but to really integrate the item into the UI it requires resources to define user names, tooltips, forms, and icons.

Item Help

Information about item types and their channels are stored in a CommandHelp/Item hash. The channels are keyed by internal name and allow for properties like the user name and tooltip. These are used to display the channel list and property sheets. Messages can also be stored in the item help info.

   <atom type="CommandHelp">

       <hash type="Item" key="myItemType@en_US">
           <atom type="UserName">My Item Type</atom>

           <hash type="Channel" key="enable">
               <atom type="UserName">Enable</atom>
               <atom type="Tooltip">When enabled this item performs its function.</atom>
           </hash>
           <hash type="Channel" key="color">
               <atom type="UserName">Color</atom>
               <atom type="Tooltip">Sets the display color of the item.</atom>
           </hash>

           <hash type="Message" key="isDisabled">This item is disabled.</hash>
       </hash>

   </atom>

Properties

The properties are displayed based on a selection filter. This filter tests true when an item of your type is selected. Filters are best treated as black boxes; simply replace myItemType and My Item Type with those of your item type or package.

   <atom type="Filters">

       <hash type="Preset" key="myItemType.item:filterPreset">
           <atom type="Name">My Item Type</atom>
           <atom type="Enable">1</atom>
           <list type="Node">1 .group 0 ""</list>
           <list type="Node">1 itemtype 0 1 "myItemType"</list>
           <list type="Node">-1 .endgroup </list>
       </hash>

   </atom>

The properties form (also known as an attribute sheet) is defined by an Attribute/Sheet hash. This sheet is for an item type derived from locator, and inserts itself into the general item properties via a form category, with the ordinal determining the order of its tab relative to the others in that category. For the most part sheets like this consist of item.channel commands for the item's channels. Note that the filter is also set to the myItemType filter that was defined above.

   <atom type="Attributes">

       <hash type="Sheet" key="myItemType.item:sheet">
           <atom type="Label">My Item Type</atom>
           <atom type="Filter">myItemType.item:filterPreset</atom>
           <atom type="Group">itemprops/render</atom>

           <hash type="InCategory" key="itemprops:general#head">
               <atom type="Ordinal">129</atom>
           </hash>

           <list type="Control" val="ref 11145884454:sheet"></list>
           <list type="Control" val="ref 80746857872:sheet"></list>

           <list type="Control" val="div ">
               <atom type="Label">My Item Type</atom>
               <atom type="Alignment">full</atom>
           </list>

           <list type="Control" val="cmd item.channel myItemType$enable ?"/>
           <list type="Control" val="cmd item.channel myItemType$color ?"/>
       </hash>

   </atom>

Icons & Categories

Categories determine where your item type shows up in various "add item" popups in the UI.

   <atom type="Categories">

       <hash type="Category" key="itemtype:locator">
           <hash type="C" key="myItemType">locators</hash>
       </hash>

   </atom>

Icons are define in the UIElements root-level atom. You specify a resource image with a unique key string, and then reference that to pick the icons out of it, usually based on a grid.

   <atom type="UIElements">

       <hash type="Image" key="my_items">my_items.tga</hash>

       <hash type="Icon" key="item.myItemType">
           <atom type="Source">my_items</atom>
           <atom type="Grid">0 0 13 13</atom>
       </hash>

   </atom>