Creating on-demand meshes
This topic introduces:
- Creating an on-demand mesh, which specifies the requirements for creating an on-demand mesh using the mi::neuraylib::ISimple_mesh interface.
- The program example_on_demand_mesh.cpp, which serves as an example implementation. This program imports a partial scene containing definitions for the camera, a light, and some geometry (a ground plane and a yellow cube). It then replaces the triangle mesh of the yellow cube by an equivalent on-demand mesh.
Creating an on-demand mesh
To create an on-demand mesh:
- Set the callback object that returns an instance of mi::neuraylib::ISimple_mesh. This instance holds the actual geometry data.
- Set the bounding box and the maximum displacement.
The program example_on_demand_mesh.cpp provides an example implementation of the abstract mi::neuraylib::ISimple_mesh interface. This particular implementation represents fixed geometry: a unit cube centered at the origin with face normals. In a typical application, this would probably be an adaptor that adapts the application-specific geometry format to the format expected by mi::neuraylib::ISimple_mesh.
Note:
The Cube class is such an adaptor:
it adapts the original data stored in m_points and m_normals
with their own index arrays to the data arrays expected by the mi::neuraylib::ISimple_mesh interface
with a single index array m_triangles.
example_on_demand_mesh.cpp
001 /****************************************************************************** 002 * © 1986, 2016 NVIDIA Corporation. All rights reserved. 003 *****************************************************************************/ 004 005 // examples/example_on_demand_mesh.cpp 006 // 007 // Creates an on-demand mesh. 008 // 009 // The example expects the following command line arguments: 010 // 011 // example_on_demand_mesh <mdl_path> 012 // 013 // mdl_path path to the MDL modules, e.g., iray-<version>/mdl 014 // 015 // The rendered image is written to a file named "example_on_demand_mesh.png". 016 017 #include <mi/neuraylib.h> 018 019 // Include code shared by all examples. 020 #include "example_shared.h" 021 // Include an implementation of IRender_target. 022 #include "example_render_target_simple.h" 023 024 #include <iostream> 025 #include <vector> 026 027 // This implementation of the mi::neuraylib::ISimple_mesh interface represents a unit cube centered 028 // at the origin with face normals. Texture spaces, derivatives, motion vectors, or material indices 029 // are not present. 030 // 031 // Each of the six faces is represented by two triangles, so the mesh consists of 12 triangles. 032 // Eight vertices would be sufficient for the cube, but since each vertex has different normals for 033 // each of the three associated faces the length of the data arrays is 24. 034 class Cube : public mi::base::Interface_implement<mi::neuraylib::ISimple_mesh> 035 { 036 public: 037 // Constructor. Sets up some of the member arrays of this class from a different representation. 038 Cube(); 039 040 // Methods required by the mi::neuraylib::ISimple_mesh interface. In this implementation they 041 // simply return pointers to member arrays of the class (or 0 for not-present optional data 042 // arrays). 043 mi::Uint32 data_size() const { return m_data_size; } 044 const mi::Float32_3_struct* get_points() const { return &m_data_points[0]; } 045 const mi::Float32_3_struct* get_normals() const { return &m_data_normals[0]; } 046 mi::Uint32 get_texture_dimension( mi::Uint32 /*texture_space_id*/) const { return 0; } 047 const mi::Float32* get_texture_coordinates( mi::Uint32 /*texture_space_id*/) const { return 0; } 048 const mi::Float32_3_struct* get_derivatives() const { return 0; } 049 mi::Uint32 get_motion_vector_count() const { return 0; } 050 const mi::Float32_3_struct* get_motion_vectors() const { return 0; } 051 mi::Uint32 triangles_size() const { return m_triangles_size; } 052 const mi::Uint32_3_struct* get_triangles() const { return &m_triangles[0]; } 053 bool has_unique_material() const { return true; } 054 const mi::Uint32* get_material_indices() const { return 0; } 055 056 private: 057 // These arrays hold the actual data used during runtime. 058 // The first two are set up in the constructor from the arrays below. 059 static const mi::Uint32 m_data_size = 24; 060 static const mi::Uint32 m_triangles_size = 12; 061 mi::Float32_3 m_data_points[m_data_size]; 062 mi::Float32_3 m_data_normals[m_data_size]; 063 static mi::Uint32_3 m_triangles[m_triangles_size]; 064 065 // These arrays hold the original data using a multi-index format. 066 // They are used in the constructor to set up some of the arrays above. 067 static const mi::Uint32 m_n_points = 8; 068 static const mi::Uint32 m_n_normals = 6; 069 static mi::Float32_3 m_points[m_n_points]; 070 static mi::Float32_3 m_normals[m_n_normals]; 071 static mi::Uint32 m_point_indices[m_data_size]; 072 static mi::Uint32 m_normal_indices[m_data_size]; 073 }; 074 075 mi::Float32_3 Cube::m_points[m_n_points] = { 076 mi::Float32_3( -0.5, -0.5, -0.5 ), 077 mi::Float32_3( 0.5, -0.5, -0.5 ), 078 mi::Float32_3( -0.5, 0.5, -0.5 ), 079 mi::Float32_3( 0.5, 0.5, -0.5 ), 080 mi::Float32_3( -0.5, -0.5, 0.5 ), 081 mi::Float32_3( 0.5, -0.5, 0.5 ), 082 mi::Float32_3( -0.5, 0.5, 0.5 ), 083 mi::Float32_3( 0.5, 0.5, 0.5 ) 084 }; 085 086 mi::Float32_3 Cube::m_normals[m_n_normals] = { 087 mi::Float32_3( 0, 0, -1 ), 088 mi::Float32_3( 0, -1, 0 ), 089 mi::Float32_3( -1, 0, 0 ), 090 mi::Float32_3( +1, 0, 0 ), 091 mi::Float32_3( 0, +1, 0 ), 092 mi::Float32_3( 0, 0, +1 ) 093 }; 094 095 mi::Uint32 Cube::m_point_indices[m_data_size] = { 096 0, 2, 3, 1, 0, 1, 5, 4, 0, 4, 6, 2, 1, 3, 7, 5, 2, 6, 7, 3, 4, 5, 7, 6 }; 097 098 mi::Uint32 Cube::m_normal_indices[m_data_size] = { 099 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; 100 101 mi::Uint32_3 Cube::m_triangles[m_triangles_size] = { 102 mi::Uint32_3( 0, 1, 2 ), 103 mi::Uint32_3( 0, 2, 3 ), 104 mi::Uint32_3( 4, 5, 6 ), 105 mi::Uint32_3( 4, 6, 7 ), 106 mi::Uint32_3( 8, 9, 10 ), 107 mi::Uint32_3( 8, 10, 11 ), 108 mi::Uint32_3( 12, 13, 14 ), 109 mi::Uint32_3( 12, 14, 15 ), 110 mi::Uint32_3( 16, 17, 18 ), 111 mi::Uint32_3( 16, 18, 19 ), 112 mi::Uint32_3( 20, 21, 22 ), 113 mi::Uint32_3( 20, 22, 23 ) 114 }; 115 116 Cube::Cube() 117 { 118 for( mi::Size i = 0; i < m_data_size; ++i) { 119 m_data_points[i] = m_points[m_point_indices[i]]; 120 m_data_normals[i] = m_normals[m_normal_indices[i]]; 121 } 122 } 123 124 // A callback that always returns a new instance of the Cube class. 125 class Cube_callback : public mi::base::Interface_implement<mi::neuraylib::IOn_demand_mesh_callback> 126 { 127 const mi::neuraylib::ISimple_mesh* call() const { return new Cube(); } 128 }; 129 130 // Replace the triangle mesh for the yellow cube with a equivalent on-demand mesh . 131 void setup_scene( mi::neuraylib::ITransaction* transaction) 132 { 133 mi::base::Handle<mi::neuraylib::IOn_demand_mesh> mesh( 134 transaction->create<mi::neuraylib::IOn_demand_mesh>( "On_demand_mesh")); 135 136 // Set the callback object that returns an instance of Cube. 137 mi::base::Handle<mi::neuraylib::IOn_demand_mesh_callback> callback( new Cube_callback()); 138 mesh->set_callback( callback.get()); 139 callback = 0; 140 141 // Set the remaining fields on the on-demand mesh. 142 mesh->set_bbox_min( mi::Float32_3( -0.5, -0.5, -0.5)); 143 mesh->set_bbox_max( mi::Float32_3( 0.5, 0.5, 0.5)); 144 mesh->set_maximum_displacement( 0); 145 146 // Set the visible attribute and the material. 147 mi::base::Handle<mi::IBoolean> visible( 148 mesh->create_attribute<mi::IBoolean>( "visible", "Boolean")); 149 visible->set_value( true); 150 visible = 0; 151 mi::base::Handle<mi::IRef> material( mesh->create_attribute<mi::IRef>( "material", "Ref")); 152 check_success( material->set_reference( "yellow_material") == 0); 153 material = 0; 154 155 // Store the on-demand mesh. 156 check_success( transaction->store( mesh.get(), "on_demand_mesh") == 0); 157 mesh = 0; 158 159 // Change the instance of the yellow cube to point to the on-demand mesh instead. 160 mi::base::Handle<mi::neuraylib::IInstance> instance( 161 transaction->edit<mi::neuraylib::IInstance>( "cube_instance")); 162 check_success( instance->attach( "on_demand_mesh") == 0); 163 instance = 0; 164 } 165 166 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path) 167 { 168 // Configure the neuray library. Here we set the search path for .mdl files. 169 mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration( 170 neuray->get_api_component<mi::neuraylib::IRendering_configuration>()); 171 check_success( rendering_configuration.is_valid_interface()); 172 check_success( rendering_configuration->add_mdl_path( mdl_path) == 0); 173 174 // Load the FreeImage, Iray Photoreal, and .mi importer plugins. 175 mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration( 176 neuray->get_api_component<mi::neuraylib::IPlugin_configuration>()); 177 #ifndef MI_PLATFORM_WINDOWS 178 check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0); 179 check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0); 180 check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0); 181 #else 182 check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0); 183 check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0); 184 check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0); 185 #endif 186 } 187 188 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray) 189 { 190 // Get the database, the global scope of the database, and create a transaction in the global 191 // scope for importing the scene file and storing the scene. 192 mi::base::Handle<mi::neuraylib::IDatabase> database( 193 neuray->get_api_component<mi::neuraylib::IDatabase>()); 194 check_success( database.is_valid_interface()); 195 mi::base::Handle<mi::neuraylib::IScope> scope( 196 database->get_global_scope()); 197 mi::base::Handle<mi::neuraylib::ITransaction> transaction( 198 scope->create_transaction()); 199 check_success( transaction.is_valid_interface()); 200 201 // Import the scene file 202 mi::base::Handle<mi::neuraylib::IImport_api> import_api( 203 neuray->get_api_component<mi::neuraylib::IImport_api>()); 204 check_success( import_api.is_valid_interface()); 205 mi::base::Handle<const mi::neuraylib::IImport_result> import_result( 206 import_api->import_elements( transaction.get(), "file:main.mi")); 207 check_success( import_result->get_error_number() == 0); 208 209 // Replace the triangle mesh by a equivalent on-demand mesh. 210 setup_scene( transaction.get()); 211 212 // Create the scene object 213 mi::base::Handle<mi::neuraylib::IScene> scene( 214 transaction->create<mi::neuraylib::IScene>( "Scene")); 215 scene->set_rootgroup( import_result->get_rootgroup()); 216 scene->set_options( import_result->get_options()); 217 scene->set_camera_instance( import_result->get_camera_inst()); 218 transaction->store( scene.get(), "the_scene"); 219 220 // Create the render context using the Iray Photoreal render mode 221 scene = transaction->edit<mi::neuraylib::IScene>( "the_scene"); 222 mi::base::Handle<mi::neuraylib::IRender_context> render_context( 223 scene->create_render_context( transaction.get(), "iray")); 224 check_success( render_context.is_valid_interface()); 225 mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>()); 226 scheduler_mode->set_c_str( "batch"); 227 render_context->set_option( "scheduler_mode", scheduler_mode.get()); 228 scene = 0; 229 230 // Create the render target and render the scene 231 mi::base::Handle<mi::neuraylib::IImage_api> image_api( 232 neuray->get_api_component<mi::neuraylib::IImage_api>()); 233 mi::base::Handle<mi::neuraylib::IRender_target> render_target( 234 new Render_target( image_api.get(), "Color", 512, 384)); 235 check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0); 236 237 // Write the image to disk 238 mi::base::Handle<mi::neuraylib::IExport_api> export_api( 239 neuray->get_api_component<mi::neuraylib::IExport_api>()); 240 check_success( export_api.is_valid_interface()); 241 mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0)); 242 export_api->export_canvas( "file:example_on_demand_mesh.png", canvas.get()); 243 244 transaction->commit(); 245 } 246 247 int main( int argc, char* argv[]) 248 { 249 // Collect command line parameters 250 if( argc != 2) { 251 std::cerr << "Usage: example_on_demand_mesh <mdl_path>" << std::endl; 252 keep_console_open(); 253 return EXIT_FAILURE; 254 } 255 const char* mdl_path = argv[1]; 256 257 // Access the neuray library 258 mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray()); 259 check_success( neuray.is_valid_interface()); 260 261 // Configure the neuray library 262 configuration( neuray, mdl_path); 263 264 // Start the neuray library 265 mi::Sint32 result = neuray->start(); 266 check_start_success( result); 267 268 // Do the actual rendering 269 rendering( neuray); 270 271 // Shut down the neuray library 272 check_success( neuray->shutdown() == 0); 273 neuray = 0; 274 275 // Unload the neuray library 276 check_success( unload()); 277 278 keep_console_open(); 279 return EXIT_SUCCESS; 280 }