Iray Programmer's Manual

Implementation of an exporter

This topic introduces:

  1. Basic concepts about the implementation and usage of custom exporters used in conjunction with the Iray API:
  2. An example exporter program, consisting of vanilla_exporter.h and example_exporter.cpp. The exporter is called Vanilla. For simplicity, it is defined in and used by the main application.

Implementing an exporter

The implementation of the Vanilla exporter in the example source is structured in three parts:

  1. Implementing the mi::IImpexp_state interface
  2. Implementing the mi::neuraylib::IExporter interface
  3. Implementing the mi::neuraylib::IExporter::export_scene() and the mi::neuraylib::IExporter::export_elements() methods

Instances of mi::IImpexp_state are used to pass information about the current exporter state, for example to recursive calls of exporters. The Vanilla exporter does not need to carry around any additional information besides what is required by the interface, therefore this simple implementation is fine. With the exception of the element list flag (which is not needed for exporters), it is the same implementation as for the Vanilla importer. Note that the simple derivation from mi::base::Interface_implement suffices here (in contrast to example_plugins.cpp).

The Vanilla exporter is given in the implementation of the mi::neuraylib::IExporter interface. Later, an instance of this class will be registered as exporter with the Iray API. Most of the methods implemented here are actually defined in the base interface mi::IImpexp_state, as they are common for importers and exporters. The Vanilla exporter claims to handle files with the extension .vnl and .van. It does not require specific capabilities of the writer to handle these formats.

The actual work of the Vanilla exporter occurs in the export_scene() and export_elements() methods. It is split into three parts:

  1. Creating the export result object
  2. Setting up some data structures
  3. Traversing the scene graph and writing the file, element by element

The map is used to perform the depth-first traversal of the scene graphs. As an example, the loop expands elements of type mi::IGroup and mi::IInstance and follows to the elements mentioned as their items. Other scene elements that need traversal are not handled in this example.

While performing these tasks the example demonstrates what type of errors to detect, how errors can be reported, and how to implement an depth-first traversal of the scene graph.

The export_elements() member function uses the same code fragments as the export_scene() member function above. In its overall structure, the export_elements() member function is just simpler in that it does not need to recursively follow any elements. It just exports all elements given in its parameter.

Registering an exporter

Registering exporters is similar to registering user-defined classes. However, since exporters are different from regular classes (for example, you cannot create instances of them using mi::neuraylib::ITransaction::create()); you need to use a registration method specific to exporters. This registration method expects a pointer to an instance of the custom exporter.

To run the example, you need to call it with an existing scene file for import. The exporter will create a file called test3.vnl, which contains the types and names of all elements in the scene.

vanilla_exporter.h

001 /******************************************************************************
002  * © 1986, 2014 NVIDIA Corporation. All rights reserved.
003  *****************************************************************************/
004 
005 #include <mi/neuraylib.h>
006 
007 #include <string>
008 #include <list>
009 #include <set>
010 #include <map>
011 #include <utility>      // for std::pair
012 #include <sstream>
013 
014 // Support function to handle mi::base::Message_severity to std::string conversion
015 static std::string enum_to_str( mi::base::Message_severity severity)
016 {
017     switch( severity) {
018         case mi::base::MESSAGE_SEVERITY_FATAL:
019             return "fatal";
020         case mi::base::MESSAGE_SEVERITY_ERROR:
021             return "error";
022         case mi::base::MESSAGE_SEVERITY_WARNING:
023             return "warning";
024         case mi::base::MESSAGE_SEVERITY_INFO:
025             return "information";
026         case mi::base::MESSAGE_SEVERITY_VERBOSE:
027             return "verbose";
028         case mi::base::MESSAGE_SEVERITY_DEBUG:
029             return "debug";
030         default:
031             return "unknown" ;
032     }
033 }
034 
035 // Define exporter state, which is used in the exporter function to carry additional data around
036 // for possible recursive invocations of exporters. It contains a URI for the exported resource,
037 // a name space, a line number, and a pointer to a parent state supporting recursive exports.
038 class Vanilla_export_state
039     : public mi::base::Interface_implement< mi::IImpexp_state>
040 {
041     // Member variables to keep all necessary data
042     std::string               m_uri;
043     mi::Uint32                m_line_number;
044     const mi::IImpexp_state*  m_parent_state;
045 
046 public:
047     // Definition of all interface functions.
048 
049     const char* get_uri() const { return m_uri.empty() ? 0 : m_uri.c_str(); }
050 
051     mi::Uint32  get_line_number() const { return m_line_number; }
052 
053     void set_line_number( mi::Uint32 number) { m_line_number = number; }
054 
055     void incr_line_number() { ++m_line_number; }
056 
057     const mi::IImpexp_state* get_parent_state() const { return m_parent_state; }
058 
059     // Definition of corresponding setters / constructors. They are not part of the interface.
060 
061     // Default constructor, initializes line number to 1.
062     Vanilla_export_state()
063         : m_line_number( 1),
064           m_parent_state( 0)
065     {}
066 
067     void set_uri( const char* uri) { m_uri = uri ? uri : ""; }
068 
069     void set_parent_state( const mi::IImpexp_state* parent_state)
070     {
071         m_parent_state = parent_state;
072     }
073 };
074 
075 // Define exporter. It defines all meta information, for example, author, version numbers,
076 // which formats it supports etc. The longer format detection functions and export_elements
077 // function are implemented outside of the class body.
078 class Vanilla_exporter
079     : public mi::base::Interface_implement< mi::neuraylib::IExporter>
080 {
081     // A map that maps type names to ID for efficient dispatch.
082     // It lists only those types that reference other types and need to be expanded during export.
083     // The map is built once at construction time.
084     std::map< std::string, mi::Uint32> map_type_name_to_id;
085 
086     typedef std::map< std::string, mi::Uint32>::const_iterator map_iterator;
087 
088 public:
089     // Returns a state suitable for passing it to an export call.
090     // The parameters are used to initialize the corresponding properties of the state.
091     // The line number is set to 1.
092     mi::IImpexp_state* create_impexp_state (
093         const char* uri,
094         const mi::IImpexp_state* parent_state) const
095     {
096         Vanilla_export_state* export_state = new Vanilla_export_state();
097         export_state->set_uri( uri);
098         export_state->set_parent_state( parent_state);
099         return export_state;
100     }
101 
102     // This exporter supports the file name extensions ".vnl" and ".van".
103     const char* get_supported_extensions( mi::Uint32 i) const
104     {
105         switch( i) {
106             case 0:  return ".vnl";
107             case 1:  return ".van";
108             default: return 0;
109         }
110     }
111 
112     // Returns the confidence of the exporter that its test_file_type() can identify the file and
113     // that the file format is fully supported.
114     mi::Impexp_priority get_priority () const
115     {
116         return mi::IMPEXP_PRIORITY_WELL_DEFINED;
117     }
118 
119     // Returns a concise single-line clear text description of the exporter.
120     const char* get_name () const
121     {
122         return "mental images example vanilla (v1) exporter";
123     }
124 
125     // Returns a concise single-line clear text description of the author of this exporter.
126     const char* get_author () const
127     {
128         return "mental images GmbH, Berlin, Germany";
129     }
130 
131     // Returns the unique identifier for the exporter.
132     mi::base::Uuid get_uuid() const
133     {
134         mi::base::Uuid uuid;
135         uuid.m_id1 = 0x7b0a3b59;
136         uuid.m_id2 = 0xbed44329;
137         uuid.m_id3 = 0xad1a2353;
138         uuid.m_id4 = 0xb0ab89a8;
139         return uuid;
140     }
141 
142     // Returns the major version number of the exporter.
143     mi::Uint32 get_major_version() const { return 1; }
144 
145     // Returns the minor version number of the exporter.
146     mi::Uint32 get_minor_version() const { return 0; }
147 
148     // Returns true if the exporter can handle the file type determined by the file name extension.
149     bool test_file_type ( const char* extension ) const;
150 
151     // Returns true if the exporter can handle the file type determined by the file name extension
152     // and if the writer has sufficient capabilities to export successfully.
153     bool test_file_type( const char* extension,
154                                  const mi::IWriter* writer ) const;
155     // Exports the scene identified by the rootgroup, camera instance, and options
156     // through the writer.
157     mi::IExport_result* export_scene (
158         mi::neuraylib::ITransaction* transaction,
159         const char*                  extension,
160         mi::IWriter*                 writer,
161         const char*                  rootgroup,
162         const char*                  caminst,
163         const char*                  options,
164         const mi::IMap*              exporter_options,
165         mi::IImpexp_state*           export_state) const;
166 
167     // Exports all scene elements contained in the elements array through the writer.
168     mi::IExport_result* export_elements (
169         mi::neuraylib::ITransaction* transaction,
170         const char*        extension,
171         mi::IWriter*       writer,
172         const mi::IArray*  elements,
173         const mi::IMap*    exporter_options,
174         mi::IImpexp_state* export_state) const;
175 
176     // Definition of constructors and support functions. They are not part of the interface.
177 
178     // Default constructor.
179     Vanilla_exporter()
180     {
181         // Build the map mapping type names to IDs. This example uses literal numbers as IDs,
182         // a more involved exporter uses symbolic constants.
183         map_type_name_to_id[ "Group"]    = 1;
184         map_type_name_to_id[ "Instance"] = 2;
185     }
186 
187     // Writes name to writer, writes the name without the leading prefix if it is equal to
188     // the prefix parameter.
189     static void write_name( const std::string& name,
190                             const std::string& prefix,
191                             mi::IWriter*       writer)
192     {
193         if ( prefix.size() > 0 && 0 == name.compare( 0, prefix.size(), prefix))
194             writer->writeline( name.c_str() + prefix.size());
195         else
196             writer->writeline( name.c_str());
197     }
198 
199     // Formats message with context and appends it to the messages in the result.
200     static mi::IExport_result_ext* report_message(
201         mi::neuraylib::ITransaction* /*transaction*/,
202         mi::IExport_result_ext*      result,
203         mi::Sint32                   message_number,
204         mi::base::Message_severity   message_severity,
205         std::string                  message,
206         const mi::IImpexp_state*     export_state) // not 0
207     {
208         std::ostringstream s;
209         s << export_state->get_uri()
210           << ":" << export_state->get_line_number() << ": "
211           << "Vanilla exporter message " << message_number << ", "
212           << "severity " << enum_to_str( message_severity) << ": "
213           << message;
214         // Report context of all parent export states from recursive
215         // invocations of export_elements in their own lines with indentation.
216         export_state = export_state->get_parent_state();
217         while( export_state) {
218             s << "\n    included from: " << export_state->get_uri()
219               << ":" << export_state->get_line_number();
220             export_state = export_state->get_parent_state();
221         }
222         result->message_push_back( message_number, message_severity, s.str().c_str());
223         return result;
224     }
225 
226     // Returns the element type of an element
227     static std::string get_element_type( const mi::base::IInterface* interface)
228     {
229         mi::base::Handle<const mi::IGroup> group( interface->get_interface<mi::IGroup>());
230         if( group.is_valid_interface())
231             return "Group";
232         mi::base::Handle<const mi::IInstance> instance( interface->get_interface<mi::IInstance>());
233         if( instance.is_valid_interface())
234             return "Instance";
235         return "Unknown";
236     }
237 };
238 
239 // Returns true if the exporter can handle the file type determined by the file name extension.
240 bool Vanilla_exporter::test_file_type ( const char* extension ) const
241 {
242     // This exporter supports the file name extensions ".vnl" and ".van".
243     mi::Size len = std::strlen( extension);
244     return (len > 3)
245         &&  (( 0 == strcmp( extension + len - 4, ".vnl"))
246           || ( 0 == strcmp( extension + len - 4, ".van")));
247 }
248 
249 // Returns true if the exporter can handle the file type determined by the file name extension
250 // and if the writer has sufficient capabilities to export successfully.
251 bool Vanilla_exporter::test_file_type( const char* extension,
252                                        const mi::IWriter* ) const
253 {
254     // The writer capabilities do not matter for this simple format.
255     // More involved formats might require random access from the writer.
256     return test_file_type( extension);
257 }
258 
259 // Exports the scene identified by the rootgroup, camera inst, and options through the writer.
260 mi::IExport_result* Vanilla_exporter::export_scene (
261     mi::neuraylib::ITransaction* transaction,
262     const char*,                 // extension
263     mi::IWriter*                 writer,    // not 0
264     const char*                  rootgroup, // not 0
265     const char*                  caminst,
266     const char*                  options,
267     const mi::IMap*              exporter_options,
268     mi::IImpexp_state*           export_state) const // not 0
269 {
270     // Create the exporter result instance for the return value.
271     // If that fails something is really wrong and we return 0.
272     mi::IExport_result_ext* result
273         = transaction->create<mi::IExport_result_ext>( "Export_result_ext");
274     if ( ! result)
275         return 0;
276 
277     // Get the 'strip_prefix' option.
278     std::string strip_prefix;
279     if( exporter_options && exporter_options->has_key( "strip_prefix")) {
280         mi::base::Handle<const mi::IString> option(
281             exporter_options->get_value<mi::IString>( "strip_prefix"));
282         if( !option.is_valid_interface())
283             return report_message(
284                 transaction, result, 6, mi::base::MESSAGE_SEVERITY_ERROR,
285                 "The option 'strip_prefix' has an invalid type.", export_state);
286         strip_prefix = option->get_c_str();
287     }
288 
289     // Two data structures maintain the information during export.
290     // The elements list keeps the names of all elements that we want to export and a bit if
291     // they have been expanded already. The order of elements follows the .mi requirements;
292     // elements that are referenced are first in the list and exported before the elements that
293     // reference them. The elements_exported map maintains a list of all exported elements. That
294     // allows us to handle objects that are referenced multiple times and export them only once.
295     std::list< std::pair< std::string, bool> > elements;
296     std::set< std::string>                     elements_exported;
297 
298     // Initialize the elements list with the three input parameters.
299     if ( options && options[0] != '\0')
300         elements.push_back( std::make_pair( std::string( options), false));
301     if ( caminst && caminst[0] != '\0')
302         elements.push_back( std::make_pair( std::string( caminst), false));
303     elements.push_back( std::make_pair( std::string( rootgroup), false));
304 
305     // Start file with magic header and use Windows line-ending convention with CR LF pairs.
306     writer->writeline( "VANILLA\r\n");
307 
308     // Main loop through all elements
309     // This is a simplified recursive directed acyclic graph traversal on the scene graph that
310     // performs a depth first search. The traversal is implemented as an iterative loop and a stack
311     // on the elements list data structure. The boolean value of entries in the elements list
312     // encodes whether we are descending or are ascending in the graph. This flag determines whether
313     // we need to expand into the element and put all its children on the stack, or whether we are
314     // done with the element and can write it out to file. Other exporters might need to manage more
315     // data during the traversal, such as a transformation stack.
316     while( (0 == writer->get_error_number()) && (! elements.empty())) {
317         if ( elements.front().second ) {
318 
319             // Traversal is ascending in the scene graph
320             // Keep element name and remove it from list
321             std::string name = elements.front().first;
322             elements.pop_front();
323             // Check if element has not been written yet
324             if ( elements_exported.find( name) == elements_exported.end()) {
325                 // Element can be written to file, mark it as written
326                 elements_exported.insert( name);
327                 // Access the element in the DB
328                 mi::base::Handle<const mi::base::IInterface> element(
329                     transaction->access( name.c_str()));
330                 if ( ! element.is_valid_interface()) {
331                     // The element is not in the DB. Export fails with customized message.
332                     std::string message( "Element '");
333                     message += name + "' does not exist in database, export failed.";
334                     // Error numbers from 6000 to 7999 are reserved for custom
335                     // exporter messages like this one
336                     return report_message( transaction, result,
337                         6001, mi::base::MESSAGE_SEVERITY_ERROR, message, export_state);
338                 }
339                 writer->writeline( get_element_type( element.get()).c_str());
340                 writer->writeline( " \"");
341                 write_name( name, strip_prefix, writer);
342                 writer->writeline( "\"\r\n");
343             }
344 
345         } else {
346 
347             // Traversal is descending in the scene graph, mark element as expanded
348             elements.front().second = true;
349             // Expand front element, but keep it in the list
350             std::string name = elements.front().first;
351             // Access the element in the DB
352             mi::base::Handle<const mi::base::IInterface> element(
353                 transaction->access( name.c_str()));
354             if ( ! element.is_valid_interface()) {
355                 // The element is not in the DB. Export fails with customized message.
356                 std::string message( "Element '");
357                 message += name + "' does not exist in database, export failed.";
358                 return report_message( transaction, result,
359                     6002, mi::base::MESSAGE_SEVERITY_ERROR, message, export_state);
360             }
361             // Dispatch on the type name of the element.
362             mi::Uint32 id = 0;
363             map_iterator iter = map_type_name_to_id.find( get_element_type( element.get()));
364             if ( iter != map_type_name_to_id.end())
365                 id = iter->second;
366             switch ( id) {
367             case 1:  // Group
368             {
369                 mi::base::Handle<const mi::IGroup> group( element->get_interface<mi::IGroup>());
370                 // Enumerate all elements in the group and push them in reverse order on the
371                 // elements list front
372                 mi::Uint32 group_size = group->get_length();
373                 for ( mi::Uint32 i = 0; i != group_size; ++i) {
374                     const char* item_name = group->get_element( group_size - i -1);
375                     mi::base::Handle<const mi::base::IInterface> interface(
376                         transaction->access( item_name));
377                     // Sanity check if item exists
378                     if ( ! interface.is_valid_interface()) {
379                         std::ostringstream message;
380                         message << "Item " << group_size - i -1 << " of group '" << name
381                             << "' does not exist in the database, export failed.";
382                         return report_message( transaction, result,
383                             6003, mi::base::MESSAGE_SEVERITY_ERROR, message.str(),
384                             export_state);
385                     }
386                     // Optimization: put name only in the elements list if it has not been exported
387                     // yet.
388                     if ( elements_exported.find( item_name) == elements_exported.end())
389                         elements.push_front( std::make_pair( std::string( item_name), false));
390                 }
391                 break;
392             }
393             case 2:  // Instance
394             {
395                 mi::base::Handle<const mi::IInstance> instance(
396                     element->get_interface<mi::IInstance>());
397                 // Get item in the instance and push it on the elements list front.
398                 const char* item_name = instance->get_item();
399                 mi::base::Handle<const mi::base::IInterface> interface(
400                     transaction->access( item_name));
401                 // Sanity check if item exists
402                 if ( ! interface.is_valid_interface()) {
403                     std::string message( "Item of instance '");
404                     message += name + " does not exist in the database, export failed.";
405                     return report_message( transaction, result,
406                         6005, mi::base::MESSAGE_SEVERITY_ERROR, message, export_state);
407                 }
408                 // Optimization: put name only in the elements list
409                 // if it has not been exported yet.
410                 if ( elements_exported.find( item_name) == elements_exported.end())
411                     elements.push_front( std::make_pair( std::string( item_name), false));
412                 break;
413             }
414             default:
415                 break;
416             }
417 
418         }
419     }
420     // Report message condition for a possibly failed writer call
421     if ( writer->get_error_number() != 0)
422         return report_message( transaction,
423                              result,
424                              writer->get_error_number(),
425                              mi::base::MESSAGE_SEVERITY_ERROR,
426                              writer->get_error_message() ? writer->get_error_message() : "",
427                              export_state);
428     return result;
429 }
430 
431 
432 // Exports all scene elements mentioned in the elements array through the writer.
433 mi::IExport_result* Vanilla_exporter::export_elements (
434     mi::neuraylib::ITransaction* transaction,
435     const char*,                 // extension
436     mi::IWriter*                 writer,   // not 0
437     const mi::IArray*            elements, // not 0
438     const mi::IMap*              exporter_options,
439     mi::IImpexp_state*           export_state) const // not 0
440 {
441     // Create the exporter result instance for the return value.
442     // If that fails something is really wrong and we return 0.
443     mi::IExport_result_ext* result
444         = transaction->create<mi::IExport_result_ext>( "Export_result_ext");
445     if ( ! result)
446         return 0;
447 
448     // Get the 'strip_prefix' option.
449     std::string strip_prefix;
450     if( exporter_options && exporter_options->has_key( "strip_prefix")) {
451         mi::base::Handle<const mi::IString> option(
452             exporter_options->get_value<mi::IString>( "strip_prefix"));
453         if( !option.is_valid_interface())
454             return report_message(
455                 transaction, result, 6, mi::base::MESSAGE_SEVERITY_ERROR,
456                 "The option 'strip_prefix' has an invalid type.", export_state);
457         strip_prefix = option->get_c_str();
458     }
459 
460     // Start file with magic header and use Windows line-ending convention with CR LF pairs.
461     writer->writeline( "VANILLA\x0D\x0A");
462 
463     // Iterate through the string array of element names
464     mi::Size size = elements->get_length();
465     for ( mi::Size i = 0; (0 == writer->get_error_number()) && i < size; ++i) {
466 
467         // Get string for element i from the array
468         mi::base::Handle<const mi::IString> name( elements->get_element<mi::IString>( i));
469         if ( ! name.is_valid_interface()) {
470             return report_message( transaction, result,
471                6007, mi::base::MESSAGE_SEVERITY_ERROR,
472                "element array contains an invalid object", export_state);
473         }
474         const char* element_name = name->get_c_str();
475 
476         // Access the element in the DB
477         mi::base::Handle<const mi::base::IInterface> element( transaction->access( element_name));
478         if ( ! element.is_valid_interface()) {
479             std::string message( "Element '");
480             message += element_name;
481             message += "' does not exist in database, export failed.";
482             return report_message( transaction, result,
483                 6008, mi::base::MESSAGE_SEVERITY_ERROR, message, export_state);
484         }
485 
486         // Write element to file
487         writer->writeline( get_element_type( element.get()).c_str());
488         writer->writeline( " \"");
489         write_name( element_name, strip_prefix, writer);
490         writer->writeline( "\"\x0D\x0A");
491     }
492 
493     return result;
494 }

example_exporter.cpp

001 /******************************************************************************
002  * © 1986, 2014 NVIDIA Corporation. All rights reserved.
003  *****************************************************************************/
004 
005 // examples/example_exporter.cpp
006 //
007 // Demonstrates the implementation of custom exporters
008 //
009 // The example expects the following command line arguments:
010 //
011 //   example_exporter <scene_file> <mdl_path>
012 //
013 // scene_file       some scene file
014 // mdl_path         path to the MDL modules, e.g., iray-<version>/mdl
015 
016 #include <iostream>
017 
018 #include <mi/neuraylib.h>
019 
020 // Include code shared by all examples.
021 #include "example_shared.h"
022 
023 // Include header file for the Vanilla exporter.
024 #include "vanilla_exporter.h"
025 
026 // The exporter.
027 mi::base::Handle<mi::neuraylib::IExporter> exporter;
028 
029 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path)
030 {
031     // Configure the neuray library. Here we set the search path for .mdl files.
032     mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration(
033         neuray->get_api_component<mi::neuraylib::IRendering_configuration>());
034     check_success( rendering_configuration.is_valid_interface());
035     check_success( rendering_configuration->add_mdl_path( mdl_path) == 0);
036 
037     // Register the Vanilla exporter.
038     mi::base::Handle<mi::neuraylib::IExtension_api> extension_api(
039         neuray->get_api_component<mi::neuraylib::IExtension_api>());
040     check_success( extension_api.is_valid_interface());
041     exporter = new Vanilla_exporter;
042     check_success( extension_api->register_exporter( exporter.get()) == 0);
043 
044    // Load the .mi importer plugin.
045     mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration(
046         neuray->get_api_component<mi::neuraylib::IPlugin_configuration>());
047 #ifndef MI_PLATFORM_WINDOWS
048     check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0);
049 #else
050     check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0);
051 #endif
052 }
053 
054 void test_exporter( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* scene_file)
055 {
056     // Get the database, the global scope, which is the root for all transactions,
057     // and create a transaction.
058     mi::base::Handle<mi::neuraylib::IDatabase> database(
059         neuray->get_api_component<mi::neuraylib::IDatabase>());
060     check_success( database.is_valid_interface());
061     mi::base::Handle<mi::neuraylib::IScope> scope(
062         database->get_global_scope());
063     mi::base::Handle<mi::neuraylib::ITransaction> transaction(
064         scope->create_transaction());
065     check_success( transaction.is_valid_interface());
066 
067     // Import the scene file
068     mi::base::Handle<mi::neuraylib::IImport_api> import_api(
069         neuray->get_api_component<mi::neuraylib::IImport_api>());
070     check_success( import_api.is_valid_interface());
071     mi::base::Handle<const mi::IString> uri( import_api->convert_filename_to_uri( scene_file));
072     mi::base::Handle<const mi::IImport_result> import_result(
073         import_api->import_elements( transaction.get(), uri->get_c_str()));
074     check_success( import_result->get_error_number() == 0);
075     const char* root_group = import_result->get_rootgroup();
076 
077     // Export the scene to a file test3.vnl (implicitly using the Vanilla exporter).
078     mi::base::Handle<mi::neuraylib::IExport_api> export_api(
079         neuray->get_api_component<mi::neuraylib::IExport_api>());
080     check_success( export_api.is_valid_interface());
081     mi::base::Handle<const mi::IExport_result> export_result(
082         export_api->export_scene( transaction.get(), "file:test3.vnl", root_group));
083     check_success( export_result.is_valid_interface());
084     check_success( export_result->get_error_number() == 0);
085 
086     transaction->commit();
087 }
088 
089 int main( int argc, char* argv[])
090 {
091     // Collect command line parameters
092     if( argc != 3) {
093         std::cerr << "Usage: example_exporter <scene_file> <mdl_path>" << std::endl;
094         keep_console_open();
095         return EXIT_FAILURE;
096     }
097     const char* scene_file = argv[1];
098     const char* mdl_path = argv[2];
099 
100     // Access the neuray library
101     mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
102     check_success( neuray.is_valid_interface());
103 
104     // Configure the neuray library
105     configuration( neuray, mdl_path);
106 
107     // Start the neuray library
108     check_success( neuray->start() == 0);
109 
110     // Test the Vanilla exporter
111     test_exporter( neuray, scene_file);
112 
113     // Shut down the neuray library
114     check_success( neuray->shutdown() == 0);
115 
116     // Unregister the Vanilla exporter.
117     mi::base::Handle<mi::neuraylib::IExtension_api> extension_api(
118         neuray->get_api_component<mi::neuraylib::IExtension_api>());
119     check_success( extension_api->unregister_exporter( exporter.get()) == 0);
120     exporter = 0;
121     extension_api = 0;
122     neuray = 0;
123 
124     // Unload the neuray library
125     check_success( unload());
126 
127     keep_console_open();
128     return EXIT_SUCCESS;
129 }