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, 2016 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, e.g., main.mi 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 = 8; 029 const mi::Uint32 cube_n_normals = 6; 030 const mi::Uint32 cube_n_triangles = 12; 031 032 mi::Float32_3 cube_points[cube_n_points] = { 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 042 mi::Float32_3 cube_normals[cube_n_normals] = { 043 mi::Float32_3( 0.0, 0.0, -1.0), 044 mi::Float32_3( 0.0, -1.0, 0.0), 045 mi::Float32_3( -1.0, 0.0, 0.0), 046 mi::Float32_3( 0.0, 0.0, 1.0), 047 mi::Float32_3( 0.0, 1.0, 0.0), 048 mi::Float32_3( 1.0, 0.0, 0.0) }; 049 050 mi::neuraylib::Triangle_point_indices cube_mesh_connectivity[cube_n_triangles] = { 051 mi::neuraylib::Triangle_point_indices( 0, 2, 6), 052 mi::neuraylib::Triangle_point_indices( 6, 4, 0), 053 mi::neuraylib::Triangle_point_indices( 0, 4, 5), 054 mi::neuraylib::Triangle_point_indices( 5, 1, 0), 055 mi::neuraylib::Triangle_point_indices( 0, 1, 3), 056 mi::neuraylib::Triangle_point_indices( 3, 2, 0), 057 mi::neuraylib::Triangle_point_indices( 1, 5, 7), 058 mi::neuraylib::Triangle_point_indices( 7, 3, 1), 059 mi::neuraylib::Triangle_point_indices( 2, 3, 7), 060 mi::neuraylib::Triangle_point_indices( 7, 6, 2), 061 mi::neuraylib::Triangle_point_indices( 4, 6, 7), 062 mi::neuraylib::Triangle_point_indices( 7, 5, 4) }; 063 064 mi::neuraylib::Triangle_point_indices cube_normal_connectivity[cube_n_triangles] = { 065 mi::neuraylib::Triangle_point_indices( 0, 0, 0), 066 mi::neuraylib::Triangle_point_indices( 0, 0, 0), 067 mi::neuraylib::Triangle_point_indices( 1, 1, 1), 068 mi::neuraylib::Triangle_point_indices( 1, 1, 1), 069 mi::neuraylib::Triangle_point_indices( 2, 2, 2), 070 mi::neuraylib::Triangle_point_indices( 2, 2, 2), 071 mi::neuraylib::Triangle_point_indices( 3, 3, 3), 072 mi::neuraylib::Triangle_point_indices( 3, 3, 3), 073 mi::neuraylib::Triangle_point_indices( 4, 4, 4), 074 mi::neuraylib::Triangle_point_indices( 4, 4, 4), 075 mi::neuraylib::Triangle_point_indices( 5, 5, 5), 076 mi::neuraylib::Triangle_point_indices( 5, 5, 5) }; 077 078 // Geometry of the grey ground plane. 079 const mi::Uint32 ground_n_points = 4; 080 const mi::Uint32 ground_n_normals = 1; 081 const mi::Uint32 ground_n_triangles = 2; 082 083 mi::Float32_3 ground_points[ground_n_points] = { 084 mi::Float32_3( -2.0, 0.0, -2.0), 085 mi::Float32_3( -2.0, 0.0, 2.0), 086 mi::Float32_3( 2.0, 0.0, -2.0), 087 mi::Float32_3( 2.0, 0.0, 2.0) }; 088 089 mi::Float32_3 ground_normals[ground_n_normals] = { 090 mi::Float32_3( 0.0, 1.0, 0.0) }; 091 092 mi::neuraylib::Triangle_point_indices ground_mesh_connectivity[ground_n_triangles] = { 093 mi::neuraylib::Triangle_point_indices( 0, 1, 3), 094 mi::neuraylib::Triangle_point_indices( 3, 2, 0) }; 095 096 mi::neuraylib::Triangle_point_indices ground_normal_connectivity[ground_n_triangles] = { 097 mi::neuraylib::Triangle_point_indices( 0, 0, 0), 098 mi::neuraylib::Triangle_point_indices( 0, 0, 0) }; 099 100 // Creates an attribute "name" of type "Boolean" on "attribute_set" and sets its value to "value". 101 void create_flag( mi::neuraylib::IAttribute_set* attribute_set, const char* name, bool value) 102 { 103 mi::base::Handle<mi::IBoolean> attribute( 104 attribute_set->create_attribute<mi::IBoolean>( name, "Boolean")); 105 attribute->set_value( value); 106 } 107 108 // Programmatically creates a scene via API calls (the same scene as in "main.mi"). 109 mi::neuraylib::IScene* create_scene( 110 mi::base::Handle<mi::neuraylib::INeuray> neuray, 111 mi::neuraylib::ITransaction* transaction) 112 { 113 { 114 // Import the MDL module "main" 115 mi::base::Handle<mi::neuraylib::IImport_api> import_api( 116 neuray->get_api_component<mi::neuraylib::IImport_api>()); 117 check_success( import_api.is_valid_interface()); 118 mi::base::Handle<const mi::neuraylib::IImport_result> import_result( 119 import_api->import_elements( transaction, "file:main.mdl")); 120 check_success( import_result->get_error_number() == 0); 121 } 122 { 123 // Create the options "options" 124 mi::base::Handle<mi::neuraylib::IOptions> options( 125 transaction->create<mi::neuraylib::IOptions>( "Options")); 126 transaction->store( options.get(), "options"); 127 } 128 { 129 // Create the MDL material instance "yellow_material" used by "cube_instance" 130 mi::base::Handle<mi::neuraylib::IAttribute_container> args( 131 transaction->create<mi::neuraylib::IAttribute_container>( "Attribute_container")); 132 mi::base::Handle<mi::IAttachable> tint( 133 args->create_attribute<mi::IAttachable>( "tint", "Attachable<Spectrum>")); 134 mi::set_value( tint.get(), mi::Spectrum( 0.942f, 0.807216f, 0.33441f)); 135 mi::base::Handle<const mi::neuraylib::IMdl_material_definition> md( 136 transaction->access<mi::neuraylib::IMdl_material_definition>( 137 "mdl::main::diffuse_material")); 138 mi::base::Handle<mi::neuraylib::IMdl_material_instance> mi( 139 md->create_material_instance( args.get())); 140 check_success( mi.get()); 141 transaction->store( mi.get(), "yellow_material"); 142 } 143 { 144 // Create the MDL material instance "grey_material" used by "ground_instance" 145 mi::base::Handle<mi::neuraylib::IAttribute_container> args( 146 transaction->create<mi::neuraylib::IAttribute_container>( "Attribute_container")); 147 mi::base::Handle<mi::IAttachable> tint( 148 args->create_attribute<mi::IAttachable>( "tint", "Attachable<Spectrum>")); 149 mi::set_value( tint.get(), mi::Spectrum( 0.306959f, 0.306959f, 0.306959f)); 150 mi::base::Handle<const mi::neuraylib::IMdl_material_definition> md( 151 transaction->access<mi::neuraylib::IMdl_material_definition>( 152 "mdl::main::diffuse_material")); 153 mi::base::Handle<mi::neuraylib::IMdl_material_instance> mi( 154 md->create_material_instance( args.get())); 155 check_success( mi.get()); 156 transaction->store( mi.get(), "grey_material"); 157 } 158 { 159 // Create the MDL material instance "white_material" used by "light" 160 mi::base::Handle<mi::neuraylib::IAttribute_container> args( 161 transaction->create<mi::neuraylib::IAttribute_container>( "Attribute_container")); 162 mi::base::Handle<mi::IAttachable> tint( 163 args->create_attribute<mi::IAttachable>( "tint", "Attachable<Spectrum>")); 164 mi::set_value( tint.get(), mi::Spectrum( 1000.0f, 1000.0f, 1000.0f)); 165 mi::base::Handle<const mi::neuraylib::IMdl_material_definition> md( 166 transaction->access<mi::neuraylib::IMdl_material_definition>( 167 "mdl::main::diffuse_light")); 168 mi::base::Handle<mi::neuraylib::IMdl_material_instance> mi( 169 md->create_material_instance( args.get())); 170 check_success( mi.get()); 171 transaction->store( mi.get(), "white_light"); 172 } 173 { 174 // Create the triangle mesh "cube" 175 mi::base::Handle<mi::neuraylib::ITriangle_mesh> mesh( 176 transaction->create<mi::neuraylib::ITriangle_mesh>( "Triangle_mesh")); 177 create_flag( mesh.get(), "visible", true); 178 create_flag( mesh.get(), "reflection_cast", true); 179 create_flag( mesh.get(), "reflection_recv", true); 180 create_flag( mesh.get(), "refraction_cast", true); 181 create_flag( mesh.get(), "refraction_recv", true); 182 create_flag( mesh.get(), "shadow_cast", true); 183 create_flag( mesh.get(), "shadow_recv", true); 184 185 // Set point data and mesh connectivity 186 mesh->reserve_points( cube_n_points); 187 for( mi::Uint32 i = 0; i < cube_n_points; ++i) 188 mesh->append_point( cube_points[i]); 189 mesh->reserve_triangles( cube_n_triangles); 190 for( mi::Uint32 i = 0; i < cube_n_triangles; ++i) 191 mesh->append_triangle( cube_mesh_connectivity[i]); 192 193 // Set normal data and normal connectivity 194 mi::base::Handle<mi::neuraylib::ITriangle_connectivity> connectivity( 195 mesh->create_connectivity()); 196 for( mi::Uint32 i = 0; i < cube_n_triangles; ++i) 197 connectivity->set_triangle_indices( 198 mi::neuraylib::Triangle_handle( i), cube_normal_connectivity[i]); 199 mi::base::Handle<mi::neuraylib::IAttribute_vector> normals( 200 connectivity->create_attribute_vector( mi::neuraylib::ATTR_NORMAL)); 201 for( mi::Uint32 i = 0; i < cube_n_normals; ++i) 202 normals->append_vector3( cube_normals[i]); 203 check_success( connectivity->attach_attribute_vector( normals.get()) == 0); 204 check_success( mesh->attach_connectivity( connectivity.get()) == 0); 205 transaction->store( mesh.get(), "cube"); 206 } 207 { 208 // Create the instance "cube_instance" referencing "cube" 209 mi::base::Handle<mi::neuraylib::IInstance> instance( 210 transaction->create<mi::neuraylib::IInstance>( "Instance")); 211 check_success( instance->attach( "cube") == 0); 212 mi::Float64_4_4 matrix( 1.0f); 213 matrix.translate( 1.3f, -0.5f, 1.0f); 214 instance->set_matrix( matrix); 215 mi::base::Handle<mi::IRef> material( 216 instance->create_attribute<mi::IRef>( "material", "Ref")); 217 check_success( material->set_reference( "yellow_material") == 0); 218 transaction->store( instance.get(), "cube_instance"); 219 } 220 { 221 // Create the triangle mesh "ground" 222 mi::base::Handle<mi::neuraylib::ITriangle_mesh> mesh( 223 transaction->create<mi::neuraylib::ITriangle_mesh>( "Triangle_mesh")); 224 create_flag( mesh.get(), "visible", true); 225 create_flag( mesh.get(), "reflection_cast", true); 226 create_flag( mesh.get(), "reflection_recv", true); 227 create_flag( mesh.get(), "refraction_cast", true); 228 create_flag( mesh.get(), "refraction_recv", true); 229 create_flag( mesh.get(), "shadow_cast", true); 230 create_flag( mesh.get(), "shadow_recv", true); 231 232 // Set point data and mesh connectivity 233 mesh->reserve_points( ground_n_points); 234 for( mi::Uint32 i = 0; i < ground_n_points; ++i) 235 mesh->append_point( ground_points[i]); 236 mesh->reserve_triangles( ground_n_triangles); 237 for( mi::Uint32 i = 0; i < ground_n_triangles; ++i) 238 mesh->append_triangle( ground_mesh_connectivity[i]); 239 240 // Set normal data and normal connectivity 241 mi::base::Handle<mi::neuraylib::ITriangle_connectivity> connectivity( 242 mesh->create_connectivity()); 243 for( mi::Uint32 i = 0; i < ground_n_triangles; ++i) 244 connectivity->set_triangle_indices( 245 mi::neuraylib::Triangle_handle( i), ground_normal_connectivity[i]); 246 mi::base::Handle<mi::neuraylib::IAttribute_vector> normals( 247 connectivity->create_attribute_vector( mi::neuraylib::ATTR_NORMAL)); 248 for( mi::Uint32 i = 0; i < ground_n_normals; ++i) 249 normals->append_vector3( ground_normals[i]); 250 check_success( connectivity->attach_attribute_vector( normals.get()) == 0); 251 check_success( mesh->attach_connectivity( connectivity.get()) == 0); 252 transaction->store( mesh.get(), "ground"); 253 } 254 { 255 // Create the instance "ground_instance" referencing "ground" 256 mi::base::Handle<mi::neuraylib::IInstance> instance( 257 transaction->create<mi::neuraylib::IInstance>( "Instance")); 258 check_success( instance->attach( "ground") == 0); 259 mi::base::Handle<mi::IRef> material( 260 instance->create_attribute<mi::IRef>( "material", "Ref")); 261 check_success( material->set_reference( "grey_material") == 0); 262 transaction->store( instance.get(), "ground_instance"); 263 } 264 { 265 // Create the light "light" 266 mi::base::Handle<mi::neuraylib::ILight> light( 267 transaction->create<mi::neuraylib::ILight>( "Light")); 268 create_flag( light.get(), "shadow_cast", true); 269 mi::base::Handle<mi::IRef> material( 270 light->create_attribute<mi::IRef>( "material", "Ref")); 271 check_success( material->set_reference( "white_light") == 0); 272 transaction->store( light.get(), "light"); 273 } 274 { 275 // Create the instance "light_instance" referencing "light" 276 mi::base::Handle<mi::neuraylib::IInstance> instance( 277 transaction->create<mi::neuraylib::IInstance>( "Instance")); 278 check_success( instance->attach( "light") == 0); 279 mi::Float64_4_4 matrix( 1.0f); 280 matrix.translate( 5.1f, -7.3f, -1.6f); 281 instance->set_matrix( matrix); 282 transaction->store( instance.get(), "light_instance"); 283 } 284 { 285 // Create the camera "camera" 286 mi::base::Handle<mi::neuraylib::ICamera> camera( 287 transaction->create<mi::neuraylib::ICamera>( "Camera")); 288 camera->set_focal( 50.0f); 289 camera->set_aperture( 44.0f); 290 camera->set_aspect( 1.33333f); 291 camera->set_resolution_x( 512); 292 camera->set_resolution_y( 384); 293 camera->set_clip_min( 0.1); 294 camera->set_clip_max( 1000); 295 transaction->store( camera.get(), "camera"); 296 } 297 { 298 // Create the instance "camera_instance" referencing "camera" 299 mi::base::Handle<mi::neuraylib::IInstance> instance( 300 transaction->create<mi::neuraylib::IInstance>( "Instance")); 301 check_success( instance->attach( "camera") == 0); 302 mi::Float64_4_4 matrix( 303 0.68826f, 0.37107f, -0.623382f, 0.0f, 304 0.00000f, 0.85929f, 0.511493f, 0.0f, 305 0.72546f, -0.35204f, 0.591414f, 0.0f, 306 0.00000f, 0.00000f, -6.256200f, 1.0f); 307 instance->set_matrix( matrix); 308 transaction->store( instance.get(), "camera_instance"); 309 } 310 { 311 // Create the group "rootgroup" containing all instances 312 mi::base::Handle<mi::neuraylib::IGroup> group( 313 transaction->create<mi::neuraylib::IGroup>( "Group")); 314 check_success( group->attach( "cube_instance" ) == 0); 315 check_success( group->attach( "ground_instance") == 0); 316 check_success( group->attach( "light_instance" ) == 0); 317 check_success( group->attach( "camera_instance") == 0); 318 transaction->store( group.get(), "rootgroup"); 319 } 320 321 // Create the scene object itself and return it. 322 mi::neuraylib::IScene* scene = transaction->create<mi::neuraylib::IScene>( "Scene"); 323 scene->set_rootgroup( "rootgroup"); 324 scene->set_camera_instance( "camera_instance"); 325 scene->set_options( "options"); 326 return scene; 327 } 328 329 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path) 330 { 331 // Configure the neuray library. Here we set the search path for .mdl files. 332 mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration( 333 neuray->get_api_component<mi::neuraylib::IRendering_configuration>()); 334 check_success( rendering_configuration.is_valid_interface()); 335 check_success( rendering_configuration->add_mdl_path( mdl_path) == 0); 336 337 // Load the FreeImage, Iray Photoreal, and .mi importer plugins. 338 mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration( 339 neuray->get_api_component<mi::neuraylib::IPlugin_configuration>()); 340 #ifndef MI_PLATFORM_WINDOWS 341 check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0); 342 check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0); 343 check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0); 344 #else 345 check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0); 346 check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0); 347 check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0); 348 #endif 349 } 350 351 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray) 352 { 353 // Get the database, the global scope of the database, and create a transaction in the global 354 // scope for importing the scene file and storing the scene. 355 mi::base::Handle<mi::neuraylib::IDatabase> database( 356 neuray->get_api_component<mi::neuraylib::IDatabase>()); 357 check_success( database.is_valid_interface()); 358 mi::base::Handle<mi::neuraylib::IScope> scope( 359 database->get_global_scope()); 360 mi::base::Handle<mi::neuraylib::ITransaction> transaction( 361 scope->create_transaction()); 362 check_success( transaction.is_valid_interface()); 363 364 // Create the scene 365 mi::base::Handle<mi::neuraylib::IScene> scene( create_scene( neuray, transaction.get())); 366 transaction->store( scene.get(), "the_scene"); 367 368 // Create the render context using the Iray Photoreal render mode 369 scene = transaction->edit<mi::neuraylib::IScene>( "the_scene"); 370 mi::base::Handle<mi::neuraylib::IRender_context> render_context( 371 scene->create_render_context( transaction.get(), "iray")); 372 check_success( render_context.is_valid_interface()); 373 mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>()); 374 scheduler_mode->set_c_str( "batch"); 375 render_context->set_option( "scheduler_mode", scheduler_mode.get()); 376 scene = 0; 377 378 // Create the render target and render the scene 379 mi::base::Handle<mi::neuraylib::IImage_api> image_api( 380 neuray->get_api_component<mi::neuraylib::IImage_api>()); 381 mi::base::Handle<mi::neuraylib::IRender_target> render_target( 382 new Render_target( image_api.get(), "Color", 512, 384)); 383 check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0); 384 385 // Write the image to disk 386 mi::base::Handle<mi::neuraylib::IExport_api> export_api( 387 neuray->get_api_component<mi::neuraylib::IExport_api>()); 388 check_success( export_api.is_valid_interface()); 389 mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0)); 390 export_api->export_canvas( "file:example_scene.png", canvas.get()); 391 392 // All transactions need to get committed or aborted. 393 transaction->commit(); 394 } 395 396 int main( int argc, char* argv[]) 397 { 398 // Collect command line parameters 399 if( argc != 2) { 400 std::cerr << "Usage: example_scene <mdl_path>" << std::endl; 401 keep_console_open(); 402 return EXIT_FAILURE; 403 } 404 const char* mdl_path = argv[1]; 405 406 // Access the neuray library 407 mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray()); 408 check_success( neuray.is_valid_interface()); 409 410 // Configure the neuray library 411 configuration( neuray, mdl_path); 412 413 // Start the neuray library 414 mi::Sint32 result = neuray->start(); 415 check_start_success( result); 416 417 // Do the actual rendering 418 rendering( neuray); 419 420 // Shut down the neuray library 421 check_success( neuray->shutdown() == 0); 422 neuray = 0; 423 424 // Unload the neuray library 425 check_success( unload()); 426 427 keep_console_open(); 428 return EXIT_SUCCESS; 429 }