Creating a viewer with SFE

08 Jun Creating a viewer with SFE

When trying to use SOFA in your own application, one of the important problems you have to face is the visualisation of your simulation.

SFE brings you two ways to solve that problem:
– using the SOFA internal code for OpenGL rendering;
– using your own render engine, after retrieving in SOFA all the necessary information.

The second method is a more complex but more versatile, as it allows you to use your own render engine rather than the default OpenGL pipeline of SOFA.

Using SOFA OpenGL rendering

The first method can be used each time you have an OpenGL context opened and current. Just call the draw() method on sfe::Simulation and the scene will be displayed exactly as it is done in runSofa. Of course, you will have to set-up yourself the position and characteristics of the camera and the lights, which are not included in the simulation scene.

1
2
3
4
5

...
// Place this call somewhere where the target OpenGL context is current
sfe::Simulation simulation = sfe::getSimulation();
simulation->draw();
...

...
// Place this call somewhere where the target OpenGL context is current
sfe::Simulation simulation = sfe::getSimulation();
simulation->draw();
...

You will also be able to render all the inner objects and interaction, given you have set correctly the corresponding flags in the scene context. Of course, if you place several VisualStyle objects in your graph, you will be able to control things in a finer way.

Using your own render engine

The second method is a bit more complex but more versatile. The main idea is to use SFE to get the position and topology of all the visual models of the scene. We then transmit that information to the render engine.
One of its interests is that it allows you to use your own render engine: VTK, vgSDK, Horde3D, … You can even use it through the TCP server or on non OpenGL systems.

For every visual object in the scene, we will fill the following structure that contains all the information needed for the rendering (topology, normals, position, …).

...
std::vector< ObjectInfo > objectList;for(auto& visualModel : visualModels ){
ObjectInfo object;// Get the positions// Some objects use indices to construct the vertices and normals lists.// For those objects, vertices positions are stored in "vertices" and not in "positions"// To know in which case we are, we have to check whether the data "vertPosIdx" is empty or not
std::vector<int> vertPosIdx;auto posIdData = visualModel.data("vertPosIdx");if( posIdData )
posIdData.get( vertPosIdx );if( vertPosIdx.empty())
object.d_position= visualModel.data("position");else
object.d_vertices= visualModel.data("vertices");// Get the normals
object.d_normal= visualModel.data("normal");// Get the list of trianglesauto trianglesData = visualModel.data("triangles");if( trianglesData )
trianglesData.get( object.triangles);// Get the list of quadsauto quadsData = visualModel.data("quads");if( quadsData )
quadsData.get( object.quads);// Get the textures coordinatesauto textureData = visualModel.data("texcoords");if( textureData )
textureData.get( object.texCoords);// Get the name of the texture file// Note that getting the actual texture file name might be a bit more complex in some cases.auto textureNameData = visualModel.data("texturename");if( textureNameDataData )
textureNameData.get( object.textureName);// Get the diffuse colorauto matData = visualModel.data("material");
std::string material;
matData.get( material );
object.color= getColor( material );// If we have enough information, we can add the object it to the render listif( object.d_position&& object.d_normal&&(!object.triangles.empty()||!object.quads.empty()))
objectList.push_back(object);}
...

We now have all the information necessary to the render engine: positions, normals, topology, colour, texture, … If we do not have to deal with topological changes, the only things we have to update at each time step are the positions and normals of your objects:

1
2
3
4
5
6
7
8

void updatePositions(sfe::Node::ObjectList& objects){for(auto& object : objects ){
object.d_position.get( object.positions);// Directly get the new value of the vertices
object.d_normal.get(object.normals);// Directly get the new value of the normals}}

void updatePositions(sfe::Node::ObjectList& objects)
{
for ( auto& object : objects )
{
object.d_position.get( object.positions ); // Directly get the new value of the vertices
object.d_normal.get(object.normals); // Directly get the new value of the normals
}
}

With this method, displaying the internal objects of SOFA (collision models, interactions, …) is not straightforward. In case you want to display them, you will have to a code a similar process for each kind of object: find all the objects of the corresponding type in the scene graph, retrieve the topological and geometrical information and send them to the render engine.