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

example_exporter.cpp

001 /******************************************************************************
002  * © 1986, 2015 NVIDIA ARC GmbH. 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 of the database, and create a transaction in the global
057     // scope.
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::neuraylib::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::neuraylib::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 }