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::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::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::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::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::ISimple_mesh interface
with a single index array m_triangles.
example_on_demand_mesh.cpp
001 /****************************************************************************** 002 * © 1986, 2014 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::ISimple_mesh interface represents a unit cube centered at the 028 // origin with face normals. Texture spaces, derivatives, motion vectors, or material indices are 029 // 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::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::ISimple_mesh interface. In this implementation they simply return 041 // pointers to member arrays of the class (or 0 for not-present optional data arrays). 042 mi::Uint32 data_size() const { return m_data_size; } 043 const mi::Float32_3_struct* get_points() const { return &m_data_points[0]; } 044 const mi::Float32_3_struct* get_normals() const { return &m_data_normals[0]; } 045 mi::Uint32 get_texture_dimension( mi::Uint32 /*texture_space_id*/) const { return 0; } 046 const mi::Float32* get_texture_coordinates( mi::Uint32 /*texture_space_id*/) const { return 0; } 047 const mi::Float32_3_struct* get_derivatives() const { return 0; } 048 mi::Uint32 get_motion_vector_count() const { return 0; } 049 const mi::Float32_3_struct* get_motion_vectors() const { return 0; } 050 mi::Uint32 triangles_size() const { return m_triangles_size; } 051 const mi::Uint32_3_struct* get_triangles() const { return &m_triangles[0]; } 052 bool has_unique_material() const { return true; } 053 const mi::Uint32* get_material_indices() const { return 0; } 054 055 private: 056 // These arrays hold the actual data used during runtime. 057 // The first two are set up in the constructor from the arrays below. 058 static const mi::Uint32 m_data_size = 24; 059 static const mi::Uint32 m_triangles_size = 12; 060 mi::Float32_3 m_data_points[m_data_size]; 061 mi::Float32_3 m_data_normals[m_data_size]; 062 static mi::Uint32_3 m_triangles[m_triangles_size]; 063 064 // These arrays hold the original data using a multi-index format. 065 // They are used in the constructor to set up some of the arrays above. 066 static const mi::Uint32 m_n_points = 8; 067 static const mi::Uint32 m_n_normals = 6; 068 static mi::Float32_3 m_points[m_n_points]; 069 static mi::Float32_3 m_normals[m_n_normals]; 070 static mi::Uint32 m_point_indices[m_data_size]; 071 static mi::Uint32 m_normal_indices[m_data_size]; 072 }; 073 074 mi::Float32_3 Cube::m_points[m_n_points] = { 075 mi::Float32_3( -0.5, -0.5, -0.5 ), 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 }; 084 085 mi::Float32_3 Cube::m_normals[m_n_normals] = { 086 mi::Float32_3( 0, 0, -1 ), 087 mi::Float32_3( 0, -1, 0 ), 088 mi::Float32_3( -1, 0, 0 ), 089 mi::Float32_3( +1, 0, 0 ), 090 mi::Float32_3( 0, +1, 0 ), 091 mi::Float32_3( 0, 0, +1 ) 092 }; 093 094 mi::Uint32 Cube::m_point_indices[m_data_size] = { 095 0, 2, 3, 1, 0, 1, 5, 4, 0, 4, 6, 2, 1, 3, 7, 5, 2, 6, 7, 3, 4, 5, 7, 6 }; 096 097 mi::Uint32 Cube::m_normal_indices[m_data_size] = { 098 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; 099 100 mi::Uint32_3 Cube::m_triangles[m_triangles_size] = { 101 mi::Uint32_3( 0, 1, 2 ), 102 mi::Uint32_3( 0, 2, 3 ), 103 mi::Uint32_3( 4, 5, 6 ), 104 mi::Uint32_3( 4, 6, 7 ), 105 mi::Uint32_3( 8, 9, 10 ), 106 mi::Uint32_3( 8, 10, 11 ), 107 mi::Uint32_3( 12, 13, 14 ), 108 mi::Uint32_3( 12, 14, 15 ), 109 mi::Uint32_3( 16, 17, 18 ), 110 mi::Uint32_3( 16, 18, 19 ), 111 mi::Uint32_3( 20, 21, 22 ), 112 mi::Uint32_3( 20, 22, 23 ) 113 }; 114 115 Cube::Cube() 116 { 117 for( mi::Size i = 0; i < m_data_size; ++i) { 118 m_data_points[i] = m_points[m_point_indices[i]]; 119 m_data_normals[i] = m_normals[m_normal_indices[i]]; 120 } 121 } 122 123 // A callback that always returns a new instance of the Cube class. 124 class Cube_callback : public mi::base::Interface_implement<mi::IOn_demand_mesh_callback> 125 { 126 const mi::ISimple_mesh* call() const { return new Cube(); } 127 }; 128 129 // Replace the triangle mesh for the yellow cube with a equivalent on-demand mesh . 130 void setup_scene( mi::neuraylib::ITransaction* transaction) 131 { 132 mi::base::Handle<mi::IOn_demand_mesh> mesh( 133 transaction->create<mi::IOn_demand_mesh>( "On_demand_mesh")); 134 135 // Set the callback object that returns an instance of Cube. 136 mi::base::Handle<mi::IOn_demand_mesh_callback> callback( new Cube_callback()); 137 mesh->set_callback( callback.get()); 138 callback = 0; 139 140 // Set the remaining fields on the on-demand mesh. 141 mesh->set_bbox_min( mi::Float32_3( -0.5, -0.5, -0.5)); 142 mesh->set_bbox_max( mi::Float32_3( 0.5, 0.5, 0.5)); 143 mesh->set_maximum_displacement( 0); 144 145 // Set the visible attribute and the material. 146 mi::base::Handle<mi::IBoolean> visible( 147 mesh->create_attribute<mi::IBoolean>( "visible", "Boolean")); 148 visible->set_value( true); 149 visible = 0; 150 mi::base::Handle<mi::IRef> material( mesh->create_attribute<mi::IRef>( "material", "Ref")); 151 check_success( material->set_reference( "yellow_material") == 0); 152 material = 0; 153 154 // Store the on-demand mesh. 155 check_success( transaction->store( mesh.get(), "on_demand_mesh") == 0); 156 mesh = 0; 157 158 // Change the instance of the yellow cube to point to the on-demand mesh instead. 159 mi::base::Handle<mi::IInstance> instance( transaction->edit<mi::IInstance>( "cube_instance")); 160 check_success( instance->attach( "on_demand_mesh") == 0); 161 instance = 0; 162 } 163 164 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path) 165 { 166 // Configure the neuray library. Here we set the search path for .mdl files. 167 mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration( 168 neuray->get_api_component<mi::neuraylib::IRendering_configuration>()); 169 check_success( rendering_configuration.is_valid_interface()); 170 check_success( rendering_configuration->add_mdl_path( mdl_path) == 0); 171 172 // Load the FreeImage, Iray Photoreal, and .mi importer plugins. 173 mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration( 174 neuray->get_api_component<mi::neuraylib::IPlugin_configuration>()); 175 #ifndef MI_PLATFORM_WINDOWS 176 check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0); 177 check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0); 178 check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0); 179 #else 180 check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0); 181 check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0); 182 check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0); 183 #endif 184 } 185 186 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray) 187 { 188 // Get the database, the global scope, which is the root for all transactions, 189 // and create a transaction for importing the scene file and storing the scene. 190 mi::base::Handle<mi::neuraylib::IDatabase> database( 191 neuray->get_api_component<mi::neuraylib::IDatabase>()); 192 check_success( database.is_valid_interface()); 193 mi::base::Handle<mi::neuraylib::IScope> scope( 194 database->get_global_scope()); 195 mi::base::Handle<mi::neuraylib::ITransaction> transaction( 196 scope->create_transaction()); 197 check_success( transaction.is_valid_interface()); 198 199 // Import the scene file 200 mi::base::Handle<mi::neuraylib::IImport_api> import_api( 201 neuray->get_api_component<mi::neuraylib::IImport_api>()); 202 check_success( import_api.is_valid_interface()); 203 mi::base::Handle<const mi::IImport_result> import_result( 204 import_api->import_elements( transaction.get(), "file:main.mi")); 205 check_success( import_result->get_error_number() == 0); 206 207 // Replace the triangle mesh by a equivalent on-demand mesh. 208 setup_scene( transaction.get()); 209 210 // Create the scene object 211 mi::base::Handle<mi::neuraylib::IScene> scene( 212 transaction->create<mi::neuraylib::IScene>( "Scene")); 213 scene->set_rootgroup( import_result->get_rootgroup()); 214 scene->set_options( import_result->get_options()); 215 scene->set_camera_instance( import_result->get_camera_inst()); 216 transaction->store( scene.get(), "the_scene"); 217 218 // Create the render context using the Iray Photoreal render mode 219 scene = transaction->edit<mi::neuraylib::IScene>( "the_scene"); 220 mi::base::Handle<mi::neuraylib::IRender_context> render_context( 221 scene->create_render_context( transaction.get(), "iray")); 222 check_success( render_context.is_valid_interface()); 223 mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>()); 224 scheduler_mode->set_c_str( "batch"); 225 render_context->set_option( "scheduler_mode", scheduler_mode.get()); 226 scene = 0; 227 228 // Create the render target and render the scene 229 mi::base::Handle<mi::neuraylib::IImage_api> image_api( 230 neuray->get_api_component<mi::neuraylib::IImage_api>()); 231 mi::base::Handle<mi::neuraylib::IRender_target> render_target( 232 new Render_target( image_api.get(), "Color", 512, 384)); 233 check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0); 234 235 // Write the image to disk 236 mi::base::Handle<mi::neuraylib::IExport_api> export_api( 237 neuray->get_api_component<mi::neuraylib::IExport_api>()); 238 check_success( export_api.is_valid_interface()); 239 mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0)); 240 export_api->export_canvas( "file:example_on_demand_mesh.png", canvas.get()); 241 242 transaction->commit(); 243 } 244 245 int main( int argc, char* argv[]) 246 { 247 // Collect command line parameters 248 if( argc != 2) { 249 std::cerr << "Usage: example_on_demand_mesh <mdl_path>" << std::endl; 250 keep_console_open(); 251 return EXIT_FAILURE; 252 } 253 const char* mdl_path = argv[1]; 254 255 // Access the neuray library 256 mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray()); 257 check_success( neuray.is_valid_interface()); 258 259 // Configure the neuray library 260 configuration( neuray, mdl_path); 261 262 // Start the neuray library 263 check_success( neuray->start() == 0); 264 265 // Do the actual rendering 266 rendering( neuray); 267 268 // Shut down the neuray library 269 check_success( neuray->shutdown() == 0); 270 neuray = 0; 271 272 // Unload the neuray library 273 check_success( unload()); 274 275 keep_console_open(); 276 return EXIT_SUCCESS; 277 }