Implementation of an exporter
This topic introduces:
- Basic concepts about the implementation and usage of custom exporters used in conjunction with the Iray API:
- 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:
- Implementing the mi::IImpexp_state interface
- Implementing the mi::neuraylib::IExporter interface
- 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:
- Creating the export result object
- Setting up some data structures
- 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 }