Example for Polygon meshes
This example imports a partial scene containing definitions for the camera, a light, and some geometry (a ground plane and a yellow cube). It then creates via the API a red cylinder as a polygon mesh and tessellates a copy of it.
Detailed Description
- Creation of polygon meshes
-
To create a polygon mesh you need to at least specify the points (the position of the vertices) of the polygon mesh and the polygons (as point indices). In create_tetrahedron() we create a cylinder with a regular N-gon as base. The actual data (point coordinates, normal directions, etc). is not hardcoded, but computed by a helper class Cylinder based on few input parameters (radius, height, parameter n of the N-gon).
In contrast to the previous example, vertex normals are not specified per point, but per vertex (to get a sharp edge for the top and base face). Hence, we cannot use the mesh connectivity, but have to use a custom connectivity. The vertices are mapped to entries in the attribute vector as follows: All vertices of the top face are mapped to the same normal, same for the vertices of the base face. Each two vertices on the edge between two adjacent side faces share one normal.
The cylinder is included in the scene similar as the tetrahedron in the previous example.
- Using the tessellator
-
The tessellator is a functor which tessellates a polygon mesh and returns the tessellated mesh as a triangle mesh. In this example, 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.
Example Source
Source Code Location: examples/example_polygon_mesh.cpp
/****************************************************************************** * Copyright 1986, 2011 NVIDIA Corporation. All rights reserved. *****************************************************************************/ // examples/example_polygon_mesh.cpp // // Creates and tessellates a polygon mesh. #include <mi/neuraylib.h> // Include code shared by all examples. #include "example_shared.h" // Include an implementation of ITile, ICanvas, and IRender_target. #include "example_render_target.h" #include <iostream> // Helper class that represents a cylinder as a polygon mesh with a regular N-gon as base class Cylinder { public: Cylinder( mi::Uint32 n, mi::Float32 radius, mi::Float32 height) : m_n( n), m_radius( radius), m_height( height) { m_mesh_connectivity = new mi::Uint32[ 2*n + 4*n]; m_normal_connectivity = new mi::Uint32[ 2*n + 4*n]; m_offsets = new mi::Uint32[ n + 2 + 1]; // offsets to the first vertex index of each polygon m_offsets[0] = 0; // base (n vertices) m_offsets[1] = n; // top (n vertices) for( mi::Uint32 i = 0; i <= n; ++i) m_offsets[2+i] = 2*n + 4*i; // sides (4 vertices each) // the mesh connectivity mi::Uint32 index = 0; for( mi::Uint32 i = 0; i < n; ++i) // base (first n even indices) m_mesh_connectivity[index++] = 2*i; for( mi::Uint32 i = 0; i < n; ++i) // top (first n odd indices) m_mesh_connectivity[index++] = 2*i+1; for( mi::Uint32 i = 0; i < n; ++i) { // sides (four subsequent indices) m_mesh_connectivity[index++] = 2*i; m_mesh_connectivity[index++] = (2*i + 2) % (2*n); m_mesh_connectivity[index++] = (2*i + 3) % (2*n); m_mesh_connectivity[index++] = (2*i + 1) % (2*n); } // the custom connectivity for normals index = 0; for( mi::Uint32 i = 0; i < n; ++i) // base (one constant normal) m_normal_connectivity[index++] = 0; for( mi::Uint32 i = 0; i < n; ++i) // top (one constant normal) m_normal_connectivity[index++] = 1; for( mi::Uint32 i = 0; i < n; ++i) { // sides (two normals each, shared m_normal_connectivity[index++] = 2 + i; // with adjacent side face) m_normal_connectivity[index++] = 2 + (i+1) % n; m_normal_connectivity[index++] = 2 + (i+1) % n; m_normal_connectivity[index++] = 2 + i; } } ~Cylinder() { delete[] m_mesh_connectivity; delete[] m_normal_connectivity; delete[] m_offsets; } mi::Uint32 num_points() const { return m_n * 2; } mi::Uint32 num_normals() const { return m_n + 2; } mi::Uint32 num_polys() const { return m_n + 2; } mi::Uint32 polygon_size( mi::Uint32 p) const { return m_offsets[p+1] - m_offsets[p]; } mi::Uint32* polygon_indices( mi::Uint32 p) const { return &m_mesh_connectivity[m_offsets[p]]; } mi::Uint32* normal_indices( mi::Uint32 p) const { return &m_normal_connectivity[m_offsets[p]]; } mi::Float32_3 point( mi::Uint32 index) { mi::Uint32 i = index / 2; mi::Float32 angle = static_cast<mi::Float32>( 2.0f * MI_PI * i / m_n); if( index % 2 == 0) return mi::Float32_3( -m_height/2.0f, m_radius * sin( angle), m_radius * cos( angle)); else return mi::Float32_3( m_height/2.0f, m_radius * sin( angle), m_radius * cos( angle)); } mi::Float32_3 normal( mi::Uint32 index) { if( index == 0) return mi::Float32_3( -1.0f, 0.0f, 0.0f); if( index == 1) return mi::Float32_3( 1.0f, 0.0f, 0.0f); mi::Float32 angle = static_cast<mi::Float32>( 2.0f * MI_PI * (index-2) / m_n); return mi::Float32_3( 0.0f, sin( angle), cos( angle)); } private: mi::Uint32 m_n; mi::Float32 m_radius, m_height; mi::Uint32* m_mesh_connectivity; mi::Uint32* m_normal_connectivity; mi::Uint32* m_offsets; }; // Create a cylinder with a regular N-gon as base and normal vectors. INVALID_DOXYREFmi::IPolygon_mesh* create_cylinder( mi::neuraylib::ITransaction* transaction, mi::Uint32 n, mi::Float32 radius, mi::Float32 height) { Cylinder cylinder( n, radius, height); // Create an empty polygon mesh INVALID_DOXYREFmi::IPolygon_mesh* mesh = transaction->create<INVALID_DOXYREFmi::IPolygon_mesh>( "Polygon_mesh"); check_success( mesh); // Create a cylinder (points and polygons) mesh->INVALID_DOXYREF( cylinder.num_points()); for( mi::Uint32 i = 0; i < cylinder.num_points(); ++i) mesh->INVALID_DOXYREF( cylinder.point( i)); for( mi::Uint32 i = 0; i < cylinder.num_polys(); ++i) mesh->INVALID_DOXYREF( cylinder.polygon_size( i)); // Map vertices of the polygons to points mi::base::Handle< mi::IPolygon_connectivity> mesh_connectivity( mesh->INVALID_DOXYREF()); for( mi::Uint32 i = 0; i < cylinder.num_polys(); ++i) mesh_connectivity->set_polygon_indices( INVALID_DOXYREFmi::Polygon_handle( i), cylinder.polygon_indices( i)); check_success( mesh->INVALID_DOXYREF( mesh_connectivity.get()) == 0); // Create a custom connectivity for normal vectors and map vertices to entries in the // attribute vector mi::base::Handle< mi::IPolygon_connectivity> normal_connectivity( mesh->INVALID_DOXYREF()); for( mi::Uint32 i = 0; i < cylinder.num_polys(); ++i) normal_connectivity->set_polygon_indices( INVALID_DOXYREFmi::Polygon_handle( i), cylinder.normal_indices( i)); // Create an attribute vector for the normals mi::base::Handle< mi::IAttribute_vector> normals( normal_connectivity->create_attribute_vector( INVALID_DOXYREFmi::ATTR_NORMAL)); for( mi::Uint32 i = 0; i < cylinder.num_normals(); ++i) normals->append_vector3( cylinder.normal( i)); check_success( normals->is_valid_attribute()); check_success( normal_connectivity->attach_attribute_vector( normals.get()) == 0); check_success( !normals->is_valid_attribute()); check_success( mesh->INVALID_DOXYREF( normal_connectivity.get()) == 0); return mesh; } // Add a red cylinder as polygon mesh and a blue cylinder as tessellation of the red cylinder void setup_scene( mi::neuraylib::ITransaction* transaction, const char* rootgroup) { mi::Float32 cylinder_radius = 0.6f; mi::Float32 cylinder_height = 1.2f; // Create the red cylinder mi::base::Handle< mi::IPolygon_mesh> mesh_red( create_cylinder( transaction, 23, cylinder_radius, cylinder_height)); transaction->store( mesh_red.get(), "mesh_red"); // Create the instance for the red cylinder mi::base::Handle< mi::IInstance> instance( transaction->create<INVALID_DOXYREFmi::IInstance>( "Instance")); instance->attach( "mesh_red"); // Set the transformation matrix, the visible attribute, and the material mi::Float64_4_4 matrix( 1.0); matrix.translate( -0.7, 0.5-cylinder_radius, 0.8); matrix.rotate( 0.0, -0.5 * MI_PI_2, 0.0); instance->set_world_to_obj( matrix); mi::base::Handle< mi::IBoolean> visible( instance->create_attribute<mi::IBoolean>( "visible", "Boolean")); visible->set_value( true); mi::base::Handle< mi::IRef> material( instance->create_attribute<mi::IRef>( "material", "Ref")); material->set_reference( "redSG"); transaction->store( instance.get(), "instance_red"); // And attach the instance to the root group mi::base::Handle< mi::IGroup> group( transaction->edit<INVALID_DOXYREFmi::IGroup>( rootgroup)); group->attach( "instance_red"); // Tessellate the polygon mesh of the red cylinder. mi::base::Handle< mi::ITessellator> tessellator( transaction->create<INVALID_DOXYREFmi::ITessellator>( "Tessellator")); mi::base::Handle< const mi::IPolygon_mesh> c_mesh_red( transaction->access<INVALID_DOXYREFmi::IPolygon_mesh>( "mesh_red")); mi::base::Handle< mi::ITriangle_mesh> m_mesh_blue( tessellator->run( c_mesh_red.get())); transaction->store( m_mesh_blue.get(), "mesh_blue"); // Create the instance for the blue cylinder instance = transaction->create<INVALID_DOXYREFmi::IInstance>( "Instance"); instance->attach( "mesh_blue"); // Set the transformation matrix, the visible attribute, and the material matrix = mi::Float64_4_4( 1.0); matrix.translate( -1.7, 0.5-cylinder_radius, -0.5); instance->set_world_to_obj( matrix); visible = instance->create_attribute<mi::IBoolean>( "visible", "Boolean"); visible->set_value( true); mi::base::Handle< mi::IRef> ref( instance->create_attribute<mi::IRef>( "material", "Ref")); ref->set_reference( "blueSG"); transaction->store( instance.get(), "instance_blue"); // And attach the instance to the root group group->attach( "instance_blue"); } void configuration( mi::base::Handle< mi::neuraylib::INeuray> neuray, const char* shader_path) { // Configure the neuray library. Here we set some paths needed by the renderer. mi::base::Handle< mi::neuraylib::IRendering_configuration> rendering_configuration( neuray->get_api_component<mi::neuraylib::IRendering_configuration>()); check_success( rendering_configuration.is_valid_interface()); check_success( rendering_configuration->add_shader_path( shader_path) == 0); // Load the FreeImage image plugin and the LLVM backend for MetaSL. mi::base::Handle< mi::neuraylib::IPlugin_configuration> plugin_configuration( neuray->get_api_component<mi::neuraylib::IPlugin_configuration>()); #ifndef MI_PLATFORM_WINDOWS check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0); check_success( plugin_configuration->load_plugin_library( "gen_llvm.so") == 0); #else check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0); check_success( plugin_configuration->load_plugin_library( "gen_llvm.dll") == 0); #endif } void rendering( mi::base::Handle< mi::neuraylib::INeuray> neuray) { // Get the database, the global scope, which is the root for all transactions, // and create a transaction for importing the scene file and storing the scene. mi::base::Handle< mi::neuraylib::IDatabase> database( neuray->get_api_component<mi::neuraylib::IDatabase>()); check_success( database.is_valid_interface()); mi::base::Handle< mi::neuraylib::IScope> scope( database->get_global_scope()); mi::base::Handle< mi::neuraylib::ITransaction> transaction( scope->create_transaction()); check_success( transaction.is_valid_interface()); // Import the scene file mi::base::Handle< mi::neuraylib::IImport_api> import_api( neuray->get_api_component<mi::neuraylib::IImport_api>()); check_success( import_api.is_valid_interface()); mi::base::Handle< const mi::IImport_result> import_result( import_api->import_elements( transaction.get(), "main.mi")); check_success( import_result->get_error_number() == 0); // Add a polygon mesh and a tessellated blue mesh to the scene setup_scene( transaction.get(), import_result->get_rootgroup()); // Create the scene object mi::base::Handle< mi::neuraylib::IScene> scene( transaction->create<mi::neuraylib::IScene>( "Scene")); scene->set_rootgroup( import_result->get_rootgroup()); scene->set_options( import_result->get_options()); scene->set_camera_instance( import_result->get_camera_inst()); transaction->store( scene.get(), "the_scene"); // Create the render context using the default renderer scene = transaction->edit<mi::neuraylib::IScene>( "the_scene"); mi::base::Handle< mi::neuraylib::IRender_context> render_context( scene->get_render_context( transaction.get(), "rt_bsp")); scene = 0; // Create the render target and render the scene Render_target render_target( 512, 384); check_success( render_context->render( transaction.get(), &render_target, NULL) == 0); // Write the image to disk mi::base::Handle< mi::neuraylib::IExport_api> export_api( neuray->get_api_component<mi::neuraylib::IExport_api>()); check_success( export_api.is_valid_interface()); mi::base::Handle< mi::neuraylib::ICanvas> canvas( render_target.get_canvas( 0)); export_api->export_canvas( "example_polygon_mesh.png", canvas.get(), 100); // All transactions need to get committed or aborted, not really important in this example. transaction->commit(); } // The example takes the following command line arguments: // // example_polygon_mesh <shader_path> // // shader_path path to the shaders, e.g., neuray-<version>/shaders // // The rendered image is written to a file named "example_polygon_mesh.png". // int main( int argc, char* argv[]) { // Collect command line parameters if( argc != 2) { std::cerr << "Usage: example_polygon_mesh <shader_path>" << std::endl; return EXIT_FAILURE; } const char* shader_path = argv[1]; // Access the neuray library mi::base::Handle< mi::neuraylib::INeuray> neuray( load_and_get_ineuray()); check_success( neuray.is_valid_interface()); // Configure the neuray library configuration ( neuray, shader_path); // Start the neuray library check_success( neuray->start( true) == 0); // Do the actual rendering rendering( neuray); // Shut down the neuray library check_success( neuray->shutdown() == 0); neuray = 0; // Unload the neuray library check_success( unload()); return EXIT_SUCCESS; }