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::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::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::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 }