Iray Programmer's Manual

Creating on-demand meshes

This topic introduces:

  1. Creating an on-demand mesh, which specifies the requirements for creating an on-demand mesh using the mi::neuraylib::ISimple_mesh interface.
  2. 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:

  1. Set the callback object that returns an instance of mi::neuraylib::ISimple_mesh. This instance holds the actual geometry data.
  2. 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, 2015 NVIDIA ARC GmbH. 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     check_success( neuray->start() == 0);
266 
267     // Do the actual rendering
268     rendering( neuray);
269 
270     // Shut down the neuray library
271     check_success( neuray->shutdown() == 0);
272     neuray = 0;
273 
274     // Unload the neuray library
275     check_success( unload());
276 
277     keep_console_open();
278     return EXIT_SUCCESS;
279 }