Iray Programmer's Manual

Creating polygon meshes

This topic introduces:

  1. Core concepts about creating and tessellating polygon meshes:
  2. The program example_polygon_mesh.cpp, which serves as an example implementation of these concepts. This program imports a partial scene containing definitions for the camera, a light, and some geometry (a ground plane and a yellow cube). Using calls to the API, it creates a red cylinder as a polygon mesh and tessellates a copy of it.

Creating a polygon mesh

To create a polygon mesh, you need to specify at least the following:
  • Specify the points (the position of the vertices) of the polygon mesh
  • Polygons (as point indices)

In example_polygon_mesh.cpp, create_cylinder() is used to create a cylinder with a regular N-gon as base. The actual data such as point coordinates and normal directions is not hardcoded, but computed by the helper class Cylinder and based on a few input parameters: radius, height, and parameter n of the N-gon.

To get a sharp edge for the top and base face, vertex normals are not specified per point, but per vertex. Hence, the mesh connectivity cannot be used. A custom connectivity must be used instead.

Note: In the previous example, Creating triangle meshes, vertex normals are specified per point and the mesh connectivity is used to define vertex normals.

The vertices are mapped to entries in the attribute vector as follows:

  • All vertices of the top face are mapped to the same normal
  • All vertices of the base face are mapped to the same normal
  • Each two vertices on the edge between two adjacent side faces share one normal

Tessellating a polygon mesh

The tessellator is a functor which tessellates a polygon mesh and returns the tessellated mesh as a triangle mesh. In example_polygon_mesh.cpp, the red cylinder is tessellated and the result is added to the scene (with a blue material). Currently, the tessellator does not support any options to control its behavior.

Adding a polygon mesh to a scene

The program example_polygon_mesh.cpp demonstrates how to add a polygon mesh to a scene graph. For more information, see Adding geometry to a scene.

example_polygon_mesh.cpp

001 /******************************************************************************
002  * © 1986, 2015 NVIDIA ARC GmbH. All rights reserved.
003  *****************************************************************************/
004 
005 // examples/example_polygon_mesh.cpp
006 //
007 // Creates and tessellates a polygon mesh.
008 //
009 // The example expects the following command line arguments:
010 //
011 //   example_polygon_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_polygon_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 
026 // Helper class that represents a cylinder as a polygon mesh with a regular N-gon as base
027 class Cylinder
028 {
029 public:
030     Cylinder( mi::Uint32 n, mi::Float32 radius, mi::Float32 height)
031         : m_n( n), m_radius( radius), m_height( height)
032     {
033         m_mesh_connectivity = new mi::Uint32[ 2*n + 4*n];
034         m_normal_connectivity = new mi::Uint32[ 2*n + 4*n];
035         m_offsets = new mi::Uint32[ n + 2 + 1];
036 
037         // offsets to the first vertex index of each polygon
038         m_offsets[0] = 0;                                     // base (n vertices)
039         m_offsets[1] = n;                                     // top (n vertices)
040         for( mi::Uint32 i = 0; i <= n; ++i)
041             m_offsets[2+i] = 2*n + 4*i;                       // sides (4 vertices each)
042 
043         // the mesh connectivity
044         mi::Uint32 index = 0;
045         for( mi::Uint32 i = 0; i < n; ++i)                    // base (first n even indices)
046             m_mesh_connectivity[index++] = 2*i;
047         for( mi::Uint32 i = 0; i < n; ++i)                    // top (first n odd indices)
048             m_mesh_connectivity[index++] = 2*i+1;
049         for( mi::Uint32 i = 0; i < n; ++i) {                  // sides (four subsequent indices)
050             m_mesh_connectivity[index++] =  2*i;
051             m_mesh_connectivity[index++] = (2*i + 2) % (2*n);
052             m_mesh_connectivity[index++] = (2*i + 3) % (2*n);
053             m_mesh_connectivity[index++] = (2*i + 1) % (2*n);
054         }
055 
056         // the custom connectivity for normals
057         index = 0;
058         for( mi::Uint32 i = 0; i < n; ++i)                    // base (one constant normal)
059             m_normal_connectivity[index++] = 0;
060         for( mi::Uint32 i = 0; i < n; ++i)                    // top (one constant normal)
061             m_normal_connectivity[index++] = 1;
062         for( mi::Uint32 i = 0; i < n; ++i) {                  // sides (two normals each, shared
063             m_normal_connectivity[index++] = 2 + i;           // with adjacent side face)
064             m_normal_connectivity[index++] = 2 + (i+1) % n;
065             m_normal_connectivity[index++] = 2 + (i+1) % n;
066             m_normal_connectivity[index++] = 2 + i;
067         }
068     }
069 
070     ~Cylinder()
071     {
072         delete[] m_mesh_connectivity;
073         delete[] m_normal_connectivity;
074         delete[] m_offsets;
075     }
076 
077     mi::Uint32 num_points() const { return m_n * 2; }
078 
079     mi::Uint32 num_normals() const { return m_n + 2; }
080 
081     mi::Uint32 num_polys() const { return m_n + 2; }
082 
083     mi::Uint32 polygon_size( mi::Uint32 p) const { return m_offsets[p+1] - m_offsets[p]; }
084 
085     mi::Uint32* polygon_indices( mi::Uint32 p) const { return &m_mesh_connectivity[m_offsets[p]]; }
086 
087     mi::Uint32* normal_indices( mi::Uint32 p) const { return &m_normal_connectivity[m_offsets[p]]; }
088 
089     mi::Float32_3 point( mi::Uint32 index)
090     {
091         mi::Uint32 i = index / 2;
092         mi::Float32 angle = static_cast<mi::Float32>( 2.0f * MI_PI * i / m_n);
093 
094         if( index % 2 == 0)
095             return mi::Float32_3(
096                 -m_height/2.0f, m_radius * std::sin( angle), m_radius * std::cos( angle));
097         else
098             return mi::Float32_3(
099                  m_height/2.0f, m_radius * std::sin( angle), m_radius * std::cos( angle));
100     }
101 
102     mi::Float32_3 normal( mi::Uint32 index)
103     {
104         if( index == 0) return mi::Float32_3( -1.0f, 0.0f, 0.0f);
105         if( index == 1) return mi::Float32_3(  1.0f, 0.0f, 0.0f);
106 
107         mi::Float32 angle = static_cast<mi::Float32>( 2.0f * MI_PI * (index-2) / m_n);
108         return mi::Float32_3( 0.0f, std::sin( angle), std::cos( angle));
109     }
110 
111 private:
112     mi::Uint32 m_n;
113     mi::Float32 m_radius, m_height;
114     mi::Uint32* m_mesh_connectivity;
115     mi::Uint32* m_normal_connectivity;
116     mi::Uint32* m_offsets;
117 };
118 
119 // Create a cylinder with a regular N-gon as base and normal vectors.
120 mi::neuraylib::IPolygon_mesh* create_cylinder( mi::neuraylib::ITransaction* transaction,
121     mi::Uint32 n, mi::Float32 radius, mi::Float32 height)
122 {
123     Cylinder cylinder( n, radius, height);
124 
125     // Create an empty polygon mesh
126     mi::neuraylib::IPolygon_mesh* mesh
127        = transaction->create<mi::neuraylib::IPolygon_mesh>( "Polygon_mesh");
128     check_success( mesh);
129 
130     // Create a cylinder (points and polygons)
131     mesh->reserve_points( cylinder.num_points());
132     for( mi::Uint32 i = 0; i < cylinder.num_points(); ++i)
133         mesh->append_point( cylinder.point( i));
134     for( mi::Uint32 i = 0; i < cylinder.num_polys(); ++i)
135         mesh->add_polygon( cylinder.polygon_size( i));
136 
137     // Map vertices of the polygons to points
138     mi::base::Handle<mi::neuraylib::IPolygon_connectivity> mesh_connectivity(
139         mesh->edit_mesh_connectivity());
140     for( mi::Uint32 i = 0; i < cylinder.num_polys(); ++i)
141         mesh_connectivity->set_polygon_indices(
142             mi::neuraylib::Polygon_handle( i), cylinder.polygon_indices( i));
143     check_success( mesh->attach_mesh_connectivity( mesh_connectivity.get()) == 0);
144 
145     // Create a custom connectivity for normal vectors and map vertices to entries in the
146     // attribute vector
147     mi::base::Handle<mi::neuraylib::IPolygon_connectivity> normal_connectivity(
148         mesh->create_connectivity());
149     for( mi::Uint32 i = 0; i < cylinder.num_polys(); ++i)
150         normal_connectivity->set_polygon_indices(
151             mi::neuraylib::Polygon_handle( i), cylinder.normal_indices( i));
152 
153     // Create an attribute vector for the normals
154     mi::base::Handle<mi::neuraylib::IAttribute_vector> normals(
155         normal_connectivity->create_attribute_vector( mi::neuraylib::ATTR_NORMAL));
156     for( mi::Uint32 i = 0; i < cylinder.num_normals(); ++i)
157         normals->append_vector3( cylinder.normal( i));
158     check_success( normals->is_valid_attribute());
159     check_success( normal_connectivity->attach_attribute_vector( normals.get()) == 0);
160     check_success( !normals->is_valid_attribute());
161 
162     check_success( mesh->attach_connectivity( normal_connectivity.get()) == 0);
163 
164     return mesh;
165 }
166 
167 // Add a red cylinder as polygon mesh and a blue cylinder as tessellation of the red cylinder
168 void setup_scene( mi::neuraylib::ITransaction* transaction, const char* rootgroup)
169 {
170     mi::Float32 cylinder_radius = 0.6f;
171     mi::Float32 cylinder_height = 1.2f;
172 
173     // Create the red cylinder
174     mi::base::Handle<mi::neuraylib::IPolygon_mesh> mesh_red( create_cylinder(
175         transaction, 23, cylinder_radius, cylinder_height));
176     transaction->store( mesh_red.get(), "mesh_red");
177 
178     // Create the instance for the red cylinder
179     mi::base::Handle<mi::neuraylib::IInstance> instance(
180         transaction->create<mi::neuraylib::IInstance>( "Instance"));
181     instance->attach( "mesh_red");
182 
183     // Set the transformation matrix, the visible attribute, and the material
184     mi::Float64_4_4 matrix( 1.0);
185     matrix.translate( -0.1, -cylinder_radius, 0.2);
186     matrix.rotate( 0.0, -0.5 * MI_PI_2, 0.0);
187     instance->set_matrix( matrix);
188 
189     mi::base::Handle<mi::IBoolean> visible(
190         instance->create_attribute<mi::IBoolean>( "visible", "Boolean"));
191     visible->set_value( true);
192 
193     mi::base::Handle<mi::IRef> material( instance->create_attribute<mi::IRef>( "material", "Ref"));
194     material->set_reference( "red_material");
195 
196     transaction->store( instance.get(), "instance_red");
197 
198     // And attach the instance to the root group
199     mi::base::Handle<mi::neuraylib::IGroup> group(
200         transaction->edit<mi::neuraylib::IGroup>( rootgroup));
201     group->attach( "instance_red");
202 
203     // Tessellate the polygon mesh of the red cylinder.
204     mi::base::Handle<mi::neuraylib::ITessellator> tessellator(
205         transaction->create<mi::neuraylib::ITessellator>( "Tessellator"));
206     mi::base::Handle<const mi::neuraylib::IPolygon_mesh> c_mesh_red(
207         transaction->access<mi::neuraylib::IPolygon_mesh>( "mesh_red"));
208     mi::base::Handle<mi::neuraylib::ITriangle_mesh> m_mesh_blue(
209         tessellator->run( c_mesh_red.get()));
210     transaction->store( m_mesh_blue.get(), "mesh_blue");
211 
212     // Create the instance for the blue cylinder
213     instance = transaction->create<mi::neuraylib::IInstance>( "Instance");
214     instance->attach( "mesh_blue");
215 
216     // Set the transformation matrix, the visible attribute, and the material
217     matrix = mi::Float64_4_4( 1.0);
218     matrix.translate( -1.1, -cylinder_radius, -1.1);
219     instance->set_matrix( matrix);
220 
221     visible = instance->create_attribute<mi::IBoolean>( "visible", "Boolean");
222     visible->set_value( true);
223 
224     mi::base::Handle<mi::IRef> ref( instance->create_attribute<mi::IRef>( "material", "Ref"));
225     ref->set_reference( "blue_material");
226 
227     transaction->store( instance.get(), "instance_blue");
228 
229     // And attach the instance to the root group
230     group->attach( "instance_blue");
231 }
232 
233 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path)
234 {
235     // Configure the neuray library. Here we set the search path for .mdl files.
236     mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration(
237         neuray->get_api_component<mi::neuraylib::IRendering_configuration>());
238     check_success( rendering_configuration.is_valid_interface());
239     check_success( rendering_configuration->add_mdl_path( mdl_path) == 0);
240 
241     // Load the FreeImage, Iray Photoreal, and .mi importer plugins.
242     mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration(
243         neuray->get_api_component<mi::neuraylib::IPlugin_configuration>());
244 #ifndef MI_PLATFORM_WINDOWS
245     check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0);
246     check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0);
247     check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0);
248 #else
249     check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0);
250     check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0);
251     check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0);
252 #endif
253 }
254 
255 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray)
256 {
257     // Get the database, the global scope of the database, and create a transaction in the global
258     // scope for importing the scene file and storing the scene.
259     mi::base::Handle<mi::neuraylib::IDatabase> database(
260         neuray->get_api_component<mi::neuraylib::IDatabase>());
261     check_success( database.is_valid_interface());
262     mi::base::Handle<mi::neuraylib::IScope> scope(
263         database->get_global_scope());
264     mi::base::Handle<mi::neuraylib::ITransaction> transaction(
265         scope->create_transaction());
266     check_success( transaction.is_valid_interface());
267 
268     // Import the scene file
269     mi::base::Handle<mi::neuraylib::IImport_api> import_api(
270         neuray->get_api_component<mi::neuraylib::IImport_api>());
271     check_success( import_api.is_valid_interface());
272     mi::base::Handle<const mi::neuraylib::IImport_result> import_result(
273         import_api->import_elements( transaction.get(), "file:main.mi"));
274     check_success( import_result->get_error_number() == 0);
275 
276     // Add a polygon mesh and a tessellated blue mesh to the scene
277     setup_scene( transaction.get(), import_result->get_rootgroup());
278 
279     // Create the scene object
280     mi::base::Handle<mi::neuraylib::IScene> scene(
281         transaction->create<mi::neuraylib::IScene>( "Scene"));
282     scene->set_rootgroup(       import_result->get_rootgroup());
283     scene->set_options(         import_result->get_options());
284     scene->set_camera_instance( import_result->get_camera_inst());
285     transaction->store( scene.get(), "the_scene");
286 
287     // Create the render context using the Iray Photoreal render mode
288     scene = transaction->edit<mi::neuraylib::IScene>( "the_scene");
289     mi::base::Handle<mi::neuraylib::IRender_context> render_context(
290         scene->create_render_context( transaction.get(), "iray"));
291     check_success( render_context.is_valid_interface());
292     mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>());
293     scheduler_mode->set_c_str( "batch");
294     render_context->set_option( "scheduler_mode", scheduler_mode.get());
295     scene = 0;
296 
297     // Create the render target and render the scene
298     mi::base::Handle<mi::neuraylib::IImage_api> image_api(
299         neuray->get_api_component<mi::neuraylib::IImage_api>());
300     mi::base::Handle<mi::neuraylib::IRender_target> render_target(
301         new Render_target( image_api.get(), "Color", 512, 384));
302     check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0);
303 
304     // Write the image to disk
305     mi::base::Handle<mi::neuraylib::IExport_api> export_api(
306         neuray->get_api_component<mi::neuraylib::IExport_api>());
307     check_success( export_api.is_valid_interface());
308     mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0));
309     export_api->export_canvas( "file:example_polygon_mesh.png", canvas.get());
310 
311     transaction->commit();
312 }
313 
314 int main( int argc, char* argv[])
315 {
316     // Collect command line parameters
317     if( argc != 2) {
318         std::cerr << "Usage: example_polygon_mesh <mdl_path>" << std::endl;
319         keep_console_open();
320         return EXIT_FAILURE;
321     }
322     const char* mdl_path = argv[1];
323 
324     // Access the neuray library
325     mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
326     check_success( neuray.is_valid_interface());
327 
328     // Configure the neuray library
329     configuration( neuray, mdl_path);
330 
331     // Start the neuray library
332     check_success( neuray->start() == 0);
333 
334     // Do the actual rendering
335     rendering( neuray);
336 
337     // Shut down the neuray library
338     check_success( neuray->shutdown() == 0);
339     neuray = 0;
340 
341     // Unload the neuray library
342     check_success( unload());
343 
344     keep_console_open();
345     return EXIT_SUCCESS;
346 }