Creating a scene programmatically
This topic introduces:
- Core concepts about creating a scene by using API calls. Alternatively, a scene can be created by importing a scene file.
-
example_scene.cpp, which demonstrates how to create a scene by using calls to the Iray API
Comparison to the provided scene file: main.mi
The example program example_scene.cpp is very similar to example_rendering.cpp. The key difference is that example_scene.cpp does not import a scene file; it calls the function create_scene() which constructs a scene programatically by using calls to the Iray API.
This programmatically created scene is almost identical to the scene which is created by importing main.mi. The key difference is that some features which are not needed in example_rendering.cpp have been ommitted in example_scene.cpp (some MDL material instances and the texture coordinates for the ground plane). The only file loaded from disk is main.mdl, which contains the MDL definitions for the materials used in example_scene.cpp.
Order of scene element creation
The various scene elements are created one-by-one in a bottom-up order (this is the same order as in the "main.mi" file). Order is important as soon as references between database elements are created.
A referenced DB element must already exist in the database when the reference is set. If a top-down order is needed for some reason, you must delay setting the reference until after the referenced database element is stored in the database.
Procedure for scene element creation
Creating a certain scene element is typically a three step procedure:
-
Create a scene element by using a mi::neuraylib::ITransaction::create() call
-
Set up its properties, including any attributes
-
Use mi::neuraylib::ITransaction::store() to store the scene element in the database under a name that can be used later to re-access or to reference it.
Categorization of scene elements
The different scene elements can be categorized as follows:
-
Structural elements. Examples: instances and groups
-
Leaf nodes. Examples: cameras, lights, geometry, and options
-
MDL-related elements. Examples: materials and functions
-
Miscellaneous elements. Examples: images, textures, light profiles, and BSDF measurements
The details of the various scene element types is beyond the scope of this example. For additional details, see the examples for MDL and triangle meshes.
example_scene.cpp
001 /****************************************************************************** 002 * © 1986, 2014 NVIDIA Corporation. All rights reserved. 003 *****************************************************************************/ 004 005 // examples/example_scene.cpp 006 // 007 // Creates a scene programmatically, renders the scene, and writes the image to disk. 008 // 009 // The example expects the following command line arguments: 010 // 011 // example_scene <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 // The rendered image is written to a file named "example_scene.png". 017 018 #include <iostream> 019 020 #include <mi/neuraylib.h> 021 022 // Include code shared by all examples. 023 #include "example_shared.h" 024 // Include an implementation of IRender_target. 025 #include "example_render_target_simple.h" 026 027 // Geometry of the yellow cube. 028 const mi::Uint32 cube_n_points = 24; 029 const mi::Uint32 cube_n_triangles = 12; 030 031 mi::Float32_3 cube_points[cube_n_points] = { 032 mi::Float32_3( -0.5, -0.5, -0.5), 033 mi::Float32_3( -0.5, 0.5, -0.5), 034 mi::Float32_3( 0.5, 0.5, -0.5), 035 mi::Float32_3( 0.5, -0.5, -0.5), 036 mi::Float32_3( 0.5, -0.5, 0.5), 037 mi::Float32_3( -0.5, -0.5, 0.5), 038 mi::Float32_3( -0.5, 0.5, 0.5), 039 mi::Float32_3( 0.5, 0.5, 0.5), 040 mi::Float32_3( -0.5, -0.5, -0.5), 041 mi::Float32_3( 0.5, -0.5, -0.5), 042 mi::Float32_3( -0.5, -0.5, -0.5), 043 mi::Float32_3( -0.5, -0.5, 0.5), 044 mi::Float32_3( -0.5, 0.5, -0.5), 045 mi::Float32_3( 0.5, -0.5, -0.5), 046 mi::Float32_3( 0.5, 0.5, -0.5), 047 mi::Float32_3( 0.5, -0.5, 0.5), 048 mi::Float32_3( -0.5, 0.5, -0.5), 049 mi::Float32_3( -0.5, 0.5, 0.5), 050 mi::Float32_3( 0.5, 0.5, 0.5), 051 mi::Float32_3( 0.5, 0.5, -0.5), 052 mi::Float32_3( -0.5, -0.5, 0.5), 053 mi::Float32_3( 0.5, -0.5, 0.5), 054 mi::Float32_3( 0.5, 0.5, 0.5), 055 mi::Float32_3( -0.5, 0.5, 0.5) }; 056 057 mi::Float32_3 cube_normals[cube_n_points] = { 058 mi::Float32_3( 0.0, 0.0, -1.0), 059 mi::Float32_3( 0.0, 0.0, -1.0), 060 mi::Float32_3( 0.0, 0.0, -1.0), 061 mi::Float32_3( 0.0, 0.0, -1.0), 062 mi::Float32_3( 0.0, -1.0, 0.0), 063 mi::Float32_3( 0.0, -1.0, 0.0), 064 mi::Float32_3( -1.0, 0.0, 0.0), 065 mi::Float32_3( 1.0, 0.0, 0.0), 066 mi::Float32_3( 0.0, -1.0, 0.0), 067 mi::Float32_3( 0.0, -1.0, 0.0), 068 mi::Float32_3( -1.0, 0.0, 0.0), 069 mi::Float32_3( -1.0, 0.0, 0.0), 070 mi::Float32_3( -1.0, 0.0, 0.0), 071 mi::Float32_3( 1.0, 0.0, 0.0), 072 mi::Float32_3( 1.0, 0.0, 0.0), 073 mi::Float32_3( 1.0, 0.0, 0.0), 074 mi::Float32_3( 0.0, 1.0, 0.0), 075 mi::Float32_3( 0.0, 1.0, 0.0), 076 mi::Float32_3( 0.0, 1.0, 0.0), 077 mi::Float32_3( 0.0, 1.0, 0.0), 078 mi::Float32_3( 0.0, 0.0, 1.0), 079 mi::Float32_3( 0.0, 0.0, 1.0), 080 mi::Float32_3( 0.0, 0.0, 1.0), 081 mi::Float32_3( 0.0, 0.0, 1.0) }; 082 083 mi::Triangle_point_indices cube_triangles[cube_n_triangles] = { 084 mi::Triangle_point_indices( 0, 1, 2), 085 mi::Triangle_point_indices( 2, 3, 0), 086 mi::Triangle_point_indices( 8, 9, 4), 087 mi::Triangle_point_indices( 4, 5, 8), 088 mi::Triangle_point_indices( 10, 11, 6), 089 mi::Triangle_point_indices( 6, 12, 10), 090 mi::Triangle_point_indices( 13, 14, 7), 091 mi::Triangle_point_indices( 7, 15, 13), 092 mi::Triangle_point_indices( 16, 17, 18), 093 mi::Triangle_point_indices( 18, 19, 16), 094 mi::Triangle_point_indices( 20, 21, 22), 095 mi::Triangle_point_indices( 22, 23, 20) }; 096 097 // Geometry of the grey ground plane. 098 const mi::Uint32 ground_n_points = 4; 099 const mi::Uint32 ground_n_triangles = 2; 100 101 mi::Float32_3 ground_points[ground_n_points] = { 102 mi::Float32_3( -2.0, 0.0, -2.0), 103 mi::Float32_3( -2.0, 0.0, 2.0), 104 mi::Float32_3( 2.0, 0.0, 2.0), 105 mi::Float32_3( 2.0, 0.0, -2.0) }; 106 107 mi::Float32_3 ground_normals[ground_n_points] = { 108 mi::Float32_3( 0.0, 1.0, 0.0), 109 mi::Float32_3( 0.0, 1.0, 0.0), 110 mi::Float32_3( 0.0, 1.0, 0.0), 111 mi::Float32_3( 0.0, 1.0, 0.0) }; 112 113 mi::Triangle_point_indices ground_triangles[ground_n_triangles] = { 114 mi::Triangle_point_indices( 0, 1, 2), 115 mi::Triangle_point_indices( 2, 3, 0) }; 116 117 // Creates an attribute "name" of type "Boolean" on "attribute_set" and sets its value to "value". 118 void create_flag( mi::IAttribute_set* attribute_set, const char* name, bool value) 119 { 120 mi::base::Handle<mi::IBoolean> attribute( 121 attribute_set->create_attribute<mi::IBoolean>( name, "Boolean")); 122 attribute->set_value( value); 123 } 124 125 // Programmatically creates a scene via API calls (the same scene as in "main.mi"). 126 mi::neuraylib::IScene* create_scene( 127 mi::base::Handle<mi::neuraylib::INeuray> neuray, 128 mi::neuraylib::ITransaction* transaction) 129 { 130 { 131 // Import the MDL module "main" 132 mi::base::Handle<mi::neuraylib::IImport_api> import_api( 133 neuray->get_api_component<mi::neuraylib::IImport_api>()); 134 check_success( import_api.is_valid_interface()); 135 mi::base::Handle<const mi::IImport_result> import_result( 136 import_api->import_elements( transaction, "file:main.mdl")); 137 check_success( import_result->get_error_number() == 0); 138 } 139 { 140 // Create the options "options" 141 mi::base::Handle<mi::IOptions> options( transaction->create<mi::IOptions>( "Options")); 142 mi::base::Handle<mi::ISint32> prs( 143 options->create_attribute<mi::ISint32>( "progressive_rendering_samples", "Sint32")); 144 prs->set_value( 25); 145 transaction->store( options.get(), "options"); 146 } 147 { 148 // Create the MDL material instance "yellow_material" used by "cube_instance" 149 mi::base::Handle<mi::IAttribute_container> args( 150 transaction->create<mi::IAttribute_container>( "Attribute_container")); 151 mi::base::Handle<mi::IAttachable> tint( 152 args->create_attribute<mi::IAttachable>( "tint", "Attachable<Spectrum>")); 153 mi::set_value( tint.get(), mi::Spectrum( 0.942f, 0.807216f, 0.33441f)); 154 mi::base::Handle<const mi::IMdl_material_definition> md( 155 transaction->access<mi::IMdl_material_definition>( "mdl::main::diffuse_material")); 156 mi::base::Handle<mi::IMdl_material_instance> mi( md->create_material_instance( args.get())); 157 check_success( mi.get()); 158 transaction->store( mi.get(), "yellow_material"); 159 } 160 { 161 // Create the MDL material instance "grey_material" used by "ground_instance" 162 mi::base::Handle<mi::IAttribute_container> args( 163 transaction->create<mi::IAttribute_container>( "Attribute_container")); 164 mi::base::Handle<mi::IAttachable> tint( 165 args->create_attribute<mi::IAttachable>( "tint", "Attachable<Spectrum>")); 166 mi::set_value( tint.get(), mi::Spectrum( 0.306959f, 0.306959f, 0.306959f)); 167 mi::base::Handle<const mi::IMdl_material_definition> md( 168 transaction->access<mi::IMdl_material_definition>( "mdl::main::diffuse_material")); 169 mi::base::Handle<mi::IMdl_material_instance> mi( md->create_material_instance( args.get())); 170 check_success( mi.get()); 171 transaction->store( mi.get(), "grey_material"); 172 } 173 { 174 // Create the MDL material instance "white_material" used by "light" 175 mi::base::Handle<mi::IAttribute_container> args( 176 transaction->create<mi::IAttribute_container>( "Attribute_container")); 177 mi::base::Handle<mi::IAttachable> tint( 178 args->create_attribute<mi::IAttachable>( "tint", "Attachable<Spectrum>")); 179 mi::set_value( tint.get(), mi::Spectrum( 1000.0f, 1000.0f, 1000.0f)); 180 mi::base::Handle<const mi::IMdl_material_definition> md( 181 transaction->access<mi::IMdl_material_definition>( "mdl::main::diffuse_light")); 182 mi::base::Handle<mi::IMdl_material_instance> mi( md->create_material_instance( args.get())); 183 check_success( mi.get()); 184 transaction->store( mi.get(), "white_light"); 185 } 186 { 187 // Create the triangle mesh "cube" 188 mi::base::Handle<mi::ITriangle_mesh> mesh( 189 transaction->create<mi::ITriangle_mesh>( "Triangle_mesh")); 190 create_flag( mesh.get(), "visible", true); 191 create_flag( mesh.get(), "reflection_cast", true); 192 create_flag( mesh.get(), "reflection_recv", true); 193 create_flag( mesh.get(), "refraction_cast", true); 194 create_flag( mesh.get(), "refraction_recv", true); 195 create_flag( mesh.get(), "shadow_cast", true); 196 create_flag( mesh.get(), "shadow_recv", true); 197 mesh->reserve_points( cube_n_points); 198 for( mi::Uint32 i = 0; i < cube_n_points; ++i) 199 mesh->append_point( cube_points[i]); 200 mesh->reserve_triangles( cube_n_triangles); 201 for( mi::Uint32 i = 0; i < cube_n_triangles; ++i) 202 mesh->append_triangle( cube_triangles[i]); 203 mi::base::Handle<mi::ITriangle_connectivity> mesh_connectivity( 204 mesh->edit_mesh_connectivity()); 205 mi::base::Handle<mi::IAttribute_vector> normals( 206 mesh_connectivity->create_attribute_vector( mi::ATTR_NORMAL)); 207 for( mi::Uint32 i = 0; i < cube_n_points; ++i) 208 normals->append_vector3( cube_normals[i]); 209 check_success( mesh_connectivity->attach_attribute_vector( normals.get()) == 0); 210 check_success( mesh->attach_mesh_connectivity( mesh_connectivity.get()) == 0); 211 transaction->store( mesh.get(), "cube"); 212 } 213 { 214 // Create the instance "cube_instance" referencing "cube" 215 mi::base::Handle<mi::IInstance> instance( transaction->create<mi::IInstance>( "Instance")); 216 check_success( instance->attach( "cube") == 0); 217 mi::Float64_4_4 matrix( 1.0f); 218 matrix.translate( 1.3f, -0.5f, 1.0f); 219 instance->set_matrix( matrix); 220 mi::base::Handle<mi::IRef> material( 221 instance->create_attribute<mi::IRef>( "material", "Ref")); 222 check_success( material->set_reference( "yellow_material") == 0); 223 transaction->store( instance.get(), "cube_instance"); 224 } 225 { 226 // Create the triangle mesh "ground" 227 mi::base::Handle<mi::ITriangle_mesh> mesh( 228 transaction->create<mi::ITriangle_mesh>( "Triangle_mesh")); 229 create_flag( mesh.get(), "visible", true); 230 create_flag( mesh.get(), "reflection_cast", true); 231 create_flag( mesh.get(), "reflection_recv", true); 232 create_flag( mesh.get(), "refraction_cast", true); 233 create_flag( mesh.get(), "refraction_recv", true); 234 create_flag( mesh.get(), "shadow_cast", true); 235 create_flag( mesh.get(), "shadow_recv", true); 236 mesh->reserve_points( ground_n_points); 237 for( mi::Uint32 i = 0; i < ground_n_points; ++i) 238 mesh->append_point( ground_points[i]); 239 mesh->reserve_triangles( ground_n_triangles); 240 for( mi::Uint32 i = 0; i < ground_n_triangles; ++i) 241 mesh->append_triangle( ground_triangles[i]); 242 mi::base::Handle<mi::ITriangle_connectivity> mesh_connectivity( 243 mesh->edit_mesh_connectivity()); 244 mi::base::Handle<mi::IAttribute_vector> normals( 245 mesh_connectivity->create_attribute_vector( mi::ATTR_NORMAL)); 246 for( mi::Uint32 i = 0; i < ground_n_points; ++i) 247 normals->append_vector3( ground_normals[i]); 248 check_success( mesh_connectivity->attach_attribute_vector( normals.get()) == 0); 249 check_success( mesh->attach_mesh_connectivity( mesh_connectivity.get()) == 0); 250 transaction->store( mesh.get(), "ground"); 251 } 252 { 253 // Create the instance "ground_instance" referencing "ground" 254 mi::base::Handle<mi::IInstance> instance( transaction->create<mi::IInstance>( "Instance")); 255 check_success( instance->attach( "ground") == 0); 256 mi::base::Handle<mi::IRef> material( 257 instance->create_attribute<mi::IRef>( "material", "Ref")); 258 check_success( material->set_reference( "grey_material") == 0); 259 transaction->store( instance.get(), "ground_instance"); 260 } 261 { 262 // Create the light "light" 263 mi::base::Handle<mi::ILight> light( transaction->create<mi::ILight>( "Light")); 264 create_flag( light.get(), "shadow_cast", true); 265 mi::base::Handle<mi::IRef> material( 266 light->create_attribute<mi::IRef>( "material", "Ref")); 267 check_success( material->set_reference( "white_light") == 0); 268 transaction->store( light.get(), "light"); 269 } 270 { 271 // Create the instance "light_instance" referencing "light" 272 mi::base::Handle<mi::IInstance> instance( transaction->create<mi::IInstance>( "Instance")); 273 check_success( instance->attach( "light") == 0); 274 mi::Float64_4_4 matrix( 1.0f); 275 matrix.translate( 5.1f, -7.3f, -1.6f); 276 instance->set_matrix( matrix); 277 transaction->store( instance.get(), "light_instance"); 278 } 279 { 280 // Create the camera "camera" 281 mi::base::Handle<mi::ICamera> camera( transaction->create<mi::ICamera>( "Camera")); 282 camera->set_focal( 50.0f); 283 camera->set_aperture( 44.0f); 284 camera->set_aspect( 1.33333f); 285 camera->set_resolution_x( 512); 286 camera->set_resolution_y( 384); 287 camera->set_clip_min( 0.1); 288 camera->set_clip_max( 1000); 289 transaction->store( camera.get(), "camera"); 290 } 291 { 292 // Create the instance "camera_instance" referencing "camera" 293 mi::base::Handle<mi::IInstance> instance( transaction->create<mi::IInstance>( "Instance")); 294 check_success( instance->attach( "camera") == 0); 295 mi::Float64_4_4 matrix( 296 0.68826f, 0.37107f, -0.623382f, 0.0f, 297 0.00000f, 0.85929f, 0.511493f, 0.0f, 298 0.72546f, -0.35204f, 0.591414f, 0.0f, 299 0.00000f, 0.00000f, -6.256200f, 1.0f); 300 instance->set_matrix( matrix); 301 transaction->store( instance.get(), "camera_instance"); 302 } 303 { 304 // Create the group "rootgroup" containing all instances 305 mi::base::Handle<mi::IGroup> group( transaction->create<mi::IGroup>( "Group")); 306 check_success( group->attach( "cube_instance" ) == 0); 307 check_success( group->attach( "ground_instance") == 0); 308 check_success( group->attach( "light_instance" ) == 0); 309 check_success( group->attach( "camera_instance") == 0); 310 transaction->store( group.get(), "rootgroup"); 311 } 312 313 // Create the scene object itself and return it. 314 mi::neuraylib::IScene* scene = transaction->create<mi::neuraylib::IScene>( "Scene"); 315 scene->set_rootgroup( "rootgroup"); 316 scene->set_camera_instance( "camera_instance"); 317 scene->set_options( "options"); 318 return scene; 319 } 320 321 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path) 322 { 323 // Configure the neuray library. Here we set the search path for .mdl files. 324 mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration( 325 neuray->get_api_component<mi::neuraylib::IRendering_configuration>()); 326 check_success( rendering_configuration.is_valid_interface()); 327 check_success( rendering_configuration->add_mdl_path( mdl_path) == 0); 328 329 // Load the FreeImage, Iray Photoreal, and .mi importer plugins. 330 mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration( 331 neuray->get_api_component<mi::neuraylib::IPlugin_configuration>()); 332 #ifndef MI_PLATFORM_WINDOWS 333 check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0); 334 check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0); 335 check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0); 336 #else 337 check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0); 338 check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0); 339 check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0); 340 #endif 341 } 342 343 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray) 344 { 345 // Get the database, the global scope, which is the root for all transactions, 346 // and create a transaction for importing the scene file and storing the scene. 347 mi::base::Handle<mi::neuraylib::IDatabase> database( 348 neuray->get_api_component<mi::neuraylib::IDatabase>()); 349 check_success( database.is_valid_interface()); 350 mi::base::Handle<mi::neuraylib::IScope> scope( 351 database->get_global_scope()); 352 mi::base::Handle<mi::neuraylib::ITransaction> transaction( 353 scope->create_transaction()); 354 check_success( transaction.is_valid_interface()); 355 356 // Create the scene 357 mi::base::Handle<mi::neuraylib::IScene> scene( create_scene( neuray, transaction.get())); 358 transaction->store( scene.get(), "the_scene"); 359 360 // Create the render context using the Iray Photoreal render mode 361 scene = transaction->edit<mi::neuraylib::IScene>( "the_scene"); 362 mi::base::Handle<mi::neuraylib::IRender_context> render_context( 363 scene->create_render_context( transaction.get(), "iray")); 364 check_success( render_context.is_valid_interface()); 365 mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>()); 366 scheduler_mode->set_c_str( "batch"); 367 render_context->set_option( "scheduler_mode", scheduler_mode.get()); 368 scene = 0; 369 370 // Create the render target and render the scene 371 mi::base::Handle<mi::neuraylib::IImage_api> image_api( 372 neuray->get_api_component<mi::neuraylib::IImage_api>()); 373 mi::base::Handle<mi::neuraylib::IRender_target> render_target( 374 new Render_target( image_api.get(), "Color", 512, 384)); 375 check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0); 376 377 // Write the image to disk 378 mi::base::Handle<mi::neuraylib::IExport_api> export_api( 379 neuray->get_api_component<mi::neuraylib::IExport_api>()); 380 check_success( export_api.is_valid_interface()); 381 mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0)); 382 export_api->export_canvas( "file:example_scene.png", canvas.get()); 383 384 // All transactions need to get committed or aborted. 385 transaction->commit(); 386 } 387 388 int main( int argc, char* argv[]) 389 { 390 // Collect command line parameters 391 if( argc != 2) { 392 std::cerr << "Usage: example_scene <mdl_path>" << std::endl; 393 keep_console_open(); 394 return EXIT_FAILURE; 395 } 396 const char* mdl_path = argv[1]; 397 398 // Access the neuray library 399 mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray()); 400 check_success( neuray.is_valid_interface()); 401 402 // Configure the neuray library 403 configuration( neuray, mdl_path); 404 405 // Start the neuray library 406 check_success( neuray->start() == 0); 407 408 // Do the actual rendering 409 rendering( neuray); 410 411 // Shut down the neuray library 412 check_success( neuray->shutdown() == 0); 413 neuray = 0; 414 415 // Unload the neuray library 416 check_success( unload()); 417 418 keep_console_open(); 419 return EXIT_SUCCESS; 420 }