Using Loader Options

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

A loader can present an options dialog allowing the user to configure how the data in the file will be loaded into the scene. This is done by implementing a command called loaderOptions.<format>, where <format> is the name of the loader server. Setting a flag in the LXtLoadAccess struct enables the feature, and the command is fired to present the user with a dialog. Once set, the option settings are stored in an option object.

For an introduction to loaders see Loader: Server basics.

Requesting Load Options

A loader indicates that it wants to allow user options on load by setting LXtLoadAccess::options to 1 in the Recognize() method.

        LxResult
load_Recognize (
        LXtLoadAccess         *load)     LXx_OVERRIDE
{
        ...
        load->options = 1;
        return LXe_OK;
}

Options Object

The values of the options for a loader are stored in an object. Typically the only interface need is StreamIO to allow it to be written and read back. In this case our object stores a boolean and integer option which it writes to the block stream using ints (error-checking omitted for clarity). Instances of the options object are created with a spawner.

class CLoadOptions :
                public CLxImpl_StreamIO
{
    public:
                static void
        initialize ()
        {
                CLxGenericPolymorph	*srv;

                srv = new CLxPolymorph<CLoadOptions>;
                srv->AddInterface (new CLxIfc_StreamIO<CLoadOptions>);
                lx::AddSpawner (SPNAME_OPTIONS, srv);
        }

        bool           bool_option;
        int            int_option;

                LxResult
        io_Write (
                ILxUnknownID		 stream)
                                               LXx_OVERRIDE
        {
                CLxUser_BlockWrite	 blk (stream);
                unsigned		 u4;

                u4 = (bool_option ? 1 : 0); 
                blk.WriteU4 (&u4,         1);
                blk.WriteI4 (&int_option, 1);
                return LXe_OK;
        }

                LxResult
        io_Read (
                ILxUnknownID		 stream)
                                               LXx_OVERRIDE
        {
                CLxUser_BlockRead	 blk (stream);
                unsigned		 u4;
                int			 count;

                blk.ReadU4 (&u4, 1, &count);
                bool_option = (u4 != 0);
                blk.ReadI4 (&int_option, 1, &count);
                return LXe_OK;
        }
};

Options Command

Opening the options dialog is done with a command. The command name is derived from the name of the server implementing the loader. The system fires the command without any arguments, which forces a dialog for required arguments -- in this case our boolean and int. The command execution just spawns a new options object, sets the state of the object from the command's arguments, and stores the object using the IO Service global. The spawned object has to be released since the IO service has added its own reference.

class COptCommand :
                public CLxBasicCommand
{
    public:
                static void
        initialize ()
        {
                CLxGenericPolymorph	*srv;

                srv = new CLxPolymorph<COptCommand>;
                srv->AddInterface (new CLxIfc_Command     <COptCommand>);
                srv->AddInterface (new CLxIfc_Attributes  <COptCommand>);
                srv->AddInterface (new CLxIfc_AttributesUI<COptCommand>);
                lx::AddServer ("loaderOptions." LOADER_NAME, srv);
        }

        CLxSpawner<CLoadOptions>        opt_spawn;

        COptCommand () :
                opt_spawn (SPNAME_OPTIONS)
        {
                dyna_Add ("boolArg", LXtTYPE_BOOLEAN);
                dyna_Add ("intArg", LXtTYPE_INTEGER);
        }

                int
        basic_CmdFlags	()                LXx_OVERRIDE
        {
                return LXfCMD_UI;
        }

                void
        cmd_Execute (
                unsigned         flags)  LXx_OVERRIDE
        {
                CLxUser_IOService	 ios;
                CLoadOptions		*opt;
                ILxUnknownID		 obj;

                opt = opt_spawn.Alloc (obj);

                attr_GetBool (0, &opt->bool_option);
                attr_GetInt  (1, &opt->int_option);

                ios.SetOptions (obj);
                lx::ObjRelease (obj);
        }
};

Accessing Options on Load

Finally, after the file is recognized and the options command has fired, the loader is called to load the actual file. This uses the IO service to access the current options object -- which if set will be from this loader -- and unpacks it by doing a cast.

        LxResult
load_LoadObject (
        LXtLoadAccess         *load,
        ILxUnknownID           destination)     LXx_OVERRIDE
{
        CLxUser_IOService	 ios;
        ILxUnknownID		 obj;
        CLoadOptions           *opt = 0;

        obj = ios.PeekOptions ();
        if (obj)
                opt = opt_spawn.Cast (obj);

        ...
}

Imported References

The normal sequence for options is that described above when loading or importing a file from scratch.

  1. load_Recognize() returns OK
  2. cmd_Execute() fired for options command
    • options object spawned
    • set based on arguments
    • stored in IO service
  3. load_LoadObject() called
    • options object read from IO service

When a file is imported by reference, however, its load options are cached in the parent scene file. When the reference scene is loaded again the same options that were used to load it initially are presented to the loader again.

  1. parent scene saved
    • io_Write() called to serialize options
  2. parent scene loaded
    • load_SpawnOptions() called to create empty options object
    • io_Read() called to recalled stored options
    • options stored in IO service
  3. load_LoadObject() called
    • options object read from IO service

This allows user options to be retained when reference scenes are loaded again. An uninitialized options object is created using a simple method on the loader.

        LxResult
load_SpawnOptions (
        void                **ppvObj)      LXx_OVERRIDE
{
        opt_spawn.Alloc (ppvObj);
        return LXe_OK;
}