Using Loader Options

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 (index) 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.

1
2
3
4
5
6
7
8
         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 Interface 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.

1
2
3
4
5
6
7
8
 class CLoadOptions :
                 public CLxImpl_StreamIO
 {
     public:
                 static void
         initialize ()
         {
                 CLxGenericPolymorph    *srv;
1
2
3
4
                 srv = new CLxPolymorph<CLoadOptions>;
                 srv->AddInterface (new CLxIfc_StreamIO<CLoadOptions>);
                 lx::AddSpawner (SPNAME_OPTIONS, srv);
         }
1
2
         bool           bool_option;
         int            int_option;
1
2
3
4
5
6
7
                 LxResult
         io_Write (
                 ILxUnknownID            stream)
                                                LXx_OVERRIDE
         {
                 CLxUser_BlockWrite      blk (stream);
                 unsigned                u4;
1
2
3
4
5
                 u4 = (bool_option ? 1 : 0);
                 blk.WriteU4 (&u4,         1);
                 blk.WriteI4 (&int_option, 1);
                 return LXe_OK;
         }
1
2
3
4
5
6
7
8
                 LxResult
         io_Read (
                 ILxUnknownID            stream)
                                                LXx_OVERRIDE
         {
                 CLxUser_BlockRead       blk (stream);
                 unsigned                u4;
                 int                     count;
1
2
3
4
5
6
                 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 ./IOService Interface. The spawned object has to be released since the IO service has added its own reference.

1
2
3
4
5
6
7
8
 class COptCommand :
                 public CLxBasicCommand
 {
     public:
                 static void
         initialize ()
         {
                 CLxGenericPolymorph    *srv;
1
2
3
4
5
6
                 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);
         }
1
         CLxSpawner<CLoadOptions>        opt_spawn;
1
2
3
4
5
6
         COptCommand () :
                 opt_spawn (SPNAME_OPTIONS)
         {
                 dyna_Add ("boolArg", LXtTYPE_BOOLEAN);
                 dyna_Add ("intArg", LXtTYPE_INTEGER);
         }
1
2
3
4
5
                 int
         basic_CmdFlags ()                LXx_OVERRIDE
         {
                 return LXfCMD_UI;
         }
1
2
3
4
5
6
7
                 void
         cmd_Execute (
                 unsigned         flags)  LXx_OVERRIDE
         {
                 CLxUser_IOService       ios;
                 CLoadOptions           *opt;
                 ILxUnknownID            obj;
1
                 opt = opt_spawn.Alloc (obj);
1
2
                 attr_GetBool (0, &opt->bool_option);
                 attr_GetInt  (1, &opt->int_option);
1
2
3
4
                 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.

1
2
3
4
5
6
7
8
         LxResult
 load_LoadObject (
         LXtLoadAccess         *load,
         ILxUnknownID           destination)     LXx_OVERRIDE
 {
         CLxUser_IOService       ios;
         ILxUnknownID            obj;
         CLoadOptions           *opt = 0;
1
2
3
         obj = ios.PeekOptions ();
         if (obj)
                 opt = opt_spawn.Cast (obj);
1
2
         ...
 }

Imported References

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

# load_Recognize() returns OK # cmd_Execute() fired for options command #* options object spawned #* set based on arguments #* stored in IO service # 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.

# parent scene saved #* io_Write() called to serialize options # parent scene loaded #* load_SpawnOptions() called to create empty options object #* io_Read() called to recalled stored options #* options stored in IO service # 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.

1
2
3
4
5
6
7
         LxResult
 load_SpawnOptions (
         void                **ppvObj)      LXx_OVERRIDE
 {
         opt_spawn.Alloc (ppvObj);
         return LXe_OK;
 }