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, 2014 NVIDIA Corporation. 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( -m_height/2.0f, m_radius * std::sin( angle), m_radius * std::cos( angle));
096         else
097             return mi::Float32_3(  m_height/2.0f, m_radius * std::sin( angle), m_radius * std::cos( angle));
098     }
099 
100     mi::Float32_3 normal( mi::Uint32 index)
101     {
102         if( index == 0) return mi::Float32_3( -1.0f, 0.0f, 0.0f);
103         if( index == 1) return mi::Float32_3(  1.0f, 0.0f, 0.0f);
104 
105         mi::Float32 angle = static_cast<mi::Float32>( 2.0f * MI_PI * (index-2) / m_n);
106         return mi::Float32_3( 0.0f, std::sin( angle), std::cos( angle));
107     }
108 
109 private:
110     mi::Uint32 m_n;
111     mi::Float32 m_radius, m_height;
112     mi::Uint32* m_mesh_connectivity;
113     mi::Uint32* m_normal_connectivity;
114     mi::Uint32* m_offsets;
115 };
116 
117 // Create a cylinder with a regular N-gon as base and normal vectors.
118 mi::IPolygon_mesh* create_cylinder( mi::neuraylib::ITransaction* transaction,
119     mi::Uint32 n, mi::Float32 radius, mi::Float32 height)
120 {
121     Cylinder cylinder( n, radius, height);
122 
123     // Create an empty polygon mesh
124     mi::IPolygon_mesh* mesh = transaction->create<mi::IPolygon_mesh>( "Polygon_mesh");
125     check_success( mesh);
126 
127     // Create a cylinder (points and polygons)
128     mesh->reserve_points( cylinder.num_points());
129     for( mi::Uint32 i = 0; i < cylinder.num_points(); ++i)
130         mesh->append_point( cylinder.point( i));
131     for( mi::Uint32 i = 0; i < cylinder.num_polys(); ++i)
132         mesh->add_polygon( cylinder.polygon_size( i));
133 
134     // Map vertices of the polygons to points
135     mi::base::Handle<mi::IPolygon_connectivity> mesh_connectivity( mesh->edit_mesh_connectivity());
136     for( mi::Uint32 i = 0; i < cylinder.num_polys(); ++i)
137         mesh_connectivity->set_polygon_indices(
138             mi::Polygon_handle( i), cylinder.polygon_indices( i));
139     check_success( mesh->attach_mesh_connectivity( mesh_connectivity.get()) == 0);
140 
141     // Create a custom connectivity for normal vectors and map vertices to entries in the
142     // attribute vector
143     mi::base::Handle<mi::IPolygon_connectivity> normal_connectivity(
144         mesh->create_connectivity());
145     for( mi::Uint32 i = 0; i < cylinder.num_polys(); ++i)
146         normal_connectivity->set_polygon_indices(
147             mi::Polygon_handle( i), cylinder.normal_indices( i));
148 
149     // Create an attribute vector for the normals
150     mi::base::Handle<mi::IAttribute_vector> normals(
151         normal_connectivity->create_attribute_vector( mi::ATTR_NORMAL));
152     for( mi::Uint32 i = 0; i < cylinder.num_normals(); ++i)
153         normals->append_vector3( cylinder.normal( i));
154     check_success( normals->is_valid_attribute());
155     check_success( normal_connectivity->attach_attribute_vector( normals.get()) == 0);
156     check_success( !normals->is_valid_attribute());
157 
158     check_success( mesh->attach_connectivity( normal_connectivity.get()) == 0);
159 
160     return mesh;
161 }
162 
163 // Add a red cylinder as polygon mesh and a blue cylinder as tessellation of the red cylinder
164 void setup_scene( mi::neuraylib::ITransaction* transaction, const char* rootgroup)
165 {
166     mi::Float32 cylinder_radius = 0.6f;
167     mi::Float32 cylinder_height = 1.2f;
168 
169     // Create the red cylinder
170     mi::base::Handle<mi::IPolygon_mesh> mesh_red( create_cylinder(
171         transaction, 23, cylinder_radius, cylinder_height));
172     transaction->store( mesh_red.get(), "mesh_red");
173 
174     // Create the instance for the red cylinder
175     mi::base::Handle<mi::IInstance> instance( transaction->create<mi::IInstance>( "Instance"));
176     instance->attach( "mesh_red");
177 
178     // Set the transformation matrix, the visible attribute, and the material
179     mi::Float64_4_4 matrix( 1.0);
180     matrix.translate( -0.1, -cylinder_radius, 0.2);
181     matrix.rotate( 0.0, -0.5 * MI_PI_2, 0.0);
182     instance->set_matrix( matrix);
183 
184     mi::base::Handle<mi::IBoolean> visible(
185         instance->create_attribute<mi::IBoolean>( "visible", "Boolean"));
186     visible->set_value( true);
187 
188     mi::base::Handle<mi::IRef> material( instance->create_attribute<mi::IRef>( "material", "Ref"));
189     material->set_reference( "red_material");
190 
191     transaction->store( instance.get(), "instance_red");
192 
193     // And attach the instance to the root group
194     mi::base::Handle<mi::IGroup> group( transaction->edit<mi::IGroup>( rootgroup));
195     group->attach( "instance_red");
196 
197     // Tessellate the polygon mesh of the red cylinder.
198     mi::base::Handle<mi::ITessellator> tessellator(
199         transaction->create<mi::ITessellator>( "Tessellator"));
200     mi::base::Handle<const mi::IPolygon_mesh> c_mesh_red(
201         transaction->access<mi::IPolygon_mesh>( "mesh_red"));
202     mi::base::Handle<mi::ITriangle_mesh> m_mesh_blue( tessellator->run( c_mesh_red.get()));
203     transaction->store( m_mesh_blue.get(), "mesh_blue");
204 
205     // Create the instance for the blue cylinder
206     instance = transaction->create<mi::IInstance>( "Instance");
207     instance->attach( "mesh_blue");
208 
209     // Set the transformation matrix, the visible attribute, and the material
210     matrix = mi::Float64_4_4( 1.0);
211     matrix.translate( -1.1, -cylinder_radius, -1.1);
212     instance->set_matrix( matrix);
213 
214     visible = instance->create_attribute<mi::IBoolean>( "visible", "Boolean");
215     visible->set_value( true);
216 
217     mi::base::Handle<mi::IRef> ref( instance->create_attribute<mi::IRef>( "material", "Ref"));
218     ref->set_reference( "blue_material");
219 
220     transaction->store( instance.get(), "instance_blue");
221 
222     // And attach the instance to the root group
223     group->attach( "instance_blue");
224 }
225 
226 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path)
227 {
228     // Configure the neuray library. Here we set the search path for .mdl files.
229     mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration(
230         neuray->get_api_component<mi::neuraylib::IRendering_configuration>());
231     check_success( rendering_configuration.is_valid_interface());
232     check_success( rendering_configuration->add_mdl_path( mdl_path) == 0);
233 
234     // Load the FreeImage, Iray Photoreal, and .mi importer plugins.
235     mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration(
236         neuray->get_api_component<mi::neuraylib::IPlugin_configuration>());
237 #ifndef MI_PLATFORM_WINDOWS
238     check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0);
239     check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0);
240     check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0);
241 #else
242     check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0);
243     check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0);
244     check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0);
245 #endif
246 }
247 
248 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray)
249 {
250     // Get the database, the global scope, which is the root for all transactions,
251     // and create a transaction for importing the scene file and storing the scene.
252     mi::base::Handle<mi::neuraylib::IDatabase> database(
253         neuray->get_api_component<mi::neuraylib::IDatabase>());
254     check_success( database.is_valid_interface());
255     mi::base::Handle<mi::neuraylib::IScope> scope(
256         database->get_global_scope());
257     mi::base::Handle<mi::neuraylib::ITransaction> transaction(
258         scope->create_transaction());
259     check_success( transaction.is_valid_interface());
260 
261     // Import the scene file
262     mi::base::Handle<mi::neuraylib::IImport_api> import_api(
263         neuray->get_api_component<mi::neuraylib::IImport_api>());
264     check_success( import_api.is_valid_interface());
265     mi::base::Handle<const mi::IImport_result> import_result(
266         import_api->import_elements( transaction.get(), "file:main.mi"));
267     check_success( import_result->get_error_number() == 0);
268 
269     // Add a polygon mesh and a tessellated blue mesh to the scene
270     setup_scene( transaction.get(), import_result->get_rootgroup());
271 
272     // Create the scene object
273     mi::base::Handle<mi::neuraylib::IScene> scene(
274         transaction->create<mi::neuraylib::IScene>( "Scene"));
275     scene->set_rootgroup(       import_result->get_rootgroup());
276     scene->set_options(         import_result->get_options());
277     scene->set_camera_instance( import_result->get_camera_inst());
278     transaction->store( scene.get(), "the_scene");
279 
280     // Create the render context using the Iray Photoreal render mode
281     scene = transaction->edit<mi::neuraylib::IScene>( "the_scene");
282     mi::base::Handle<mi::neuraylib::IRender_context> render_context(
283         scene->create_render_context( transaction.get(), "iray"));
284     check_success( render_context.is_valid_interface());
285     mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>());
286     scheduler_mode->set_c_str( "batch");
287     render_context->set_option( "scheduler_mode", scheduler_mode.get());
288     scene = 0;
289 
290     // Create the render target and render the scene
291     mi::base::Handle<mi::neuraylib::IImage_api> image_api(
292         neuray->get_api_component<mi::neuraylib::IImage_api>());
293     mi::base::Handle<mi::neuraylib::IRender_target> render_target(
294         new Render_target( image_api.get(), "Color", 512, 384));
295     check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0);
296 
297     // Write the image to disk
298     mi::base::Handle<mi::neuraylib::IExport_api> export_api(
299         neuray->get_api_component<mi::neuraylib::IExport_api>());
300     check_success( export_api.is_valid_interface());
301     mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0));
302     export_api->export_canvas( "file:example_polygon_mesh.png", canvas.get());
303 
304     transaction->commit();
305 }
306 
307 int main( int argc, char* argv[])
308 {
309     // Collect command line parameters
310     if( argc != 2) {
311         std::cerr << "Usage: example_polygon_mesh <mdl_path>" << std::endl;
312         keep_console_open();
313         return EXIT_FAILURE;
314     }
315     const char* mdl_path = argv[1];
316 
317     // Access the neuray library
318     mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
319     check_success( neuray.is_valid_interface());
320 
321     // Configure the neuray library
322     configuration( neuray, mdl_path);
323 
324     // Start the neuray library
325     check_success( neuray->start() == 0);
326 
327     // Do the actual rendering
328     rendering( neuray);
329 
330     // Shut down the neuray library
331     check_success( neuray->shutdown() == 0);
332     neuray = 0;
333 
334     // Unload the neuray library
335     check_success( unload());
336 
337     keep_console_open();
338     return EXIT_SUCCESS;
339 }