SOFA Rendering

Specificity

The SSD project provides a compatible Factory with SOFA framework. As in the storage package, there are several advantages with this specialized Factory:

  • the recording can be launched whether the simulation is driven by the SOFA GUI or by a Python interpreter;

  • callbacks can be defined to record automatically Data fields of SOFA components;

  • the Visualizer provides a way to visualize SOFA simulations even if the GUI is not used.

Note

The SOFA version of the Factory is able to record data at each time step since it inherits from Sofa.Core.Controller. The event corresponding to the end of the time step is therefore caught and triggers access to the recorded SOFA Data fields. Only these data will be updated, the other visual objects / data fields should be manually updated. The call to factory.render is also done automatically.

Record Data fields

The way to create these Factory and Visualizer is slightly different than in the Core version. In fact, the Factory is considered as a SOFA component in your scene graph. Only the root node of the scene graph should be given, the Factory will then create its own child node and add itself to the graph.

Adding callbacks is very simple, since you only have to specify:

  • the adding method to use from the Factory;

  • the absolute path to the SOFA component in the scene graph and possibly the name of the Data field to record (depending on the object type).

  • option animated=True (by default)

It is also possible to create and update objects manually such as in the Core version.

import Sofa
from SSD.SOFA import UserAPI

# Create the root node
root = Sofa.Core.Node('root')
root.addObject('RequiredPlugin', pluginName=['SofaOpenglVisual', 'SofaNonUniformFem', 'SofaLoader', 'SofaConstraint',
                                             'SofaImplicitOdeSolver', 'SofaMeshCollision', 'SofaSimpleFem'])

# Create a falling ball
root.addChild('ball')
root.ball.addObject('EulerImplicitSolver')
root.ball.addObject('CGLinearSolver')
root.ball.addObject('MechanicalObject', name='BallMO', template='Rigid3')
root.ball.addObject('UniformMass', totalMass=1.)

# Add a visual model
root.ball.addChild('visual')
root.ball.visual.addObject('MeshObjLoader', name='Loader', filename='mesh/ball.obj')
root.ball.visual.addObject('OglModel', name='BallOGL', src='@Loader')
root.ball.visual.addObject('RigidMapping', input='@..', output='@.')

# Create a new Factory
factory = UserAPI(root=root, database_name='ball', remove_existing=True)
factory.add_points_callback(position_object='@ball.BallMO', animated=False,
                            at=0, c='red4', point_size=8)
factory.add_mesh_callback(position_object='@ball.visual.BallOGL', cell_type='triangles', animated=True,
                          at=0, c='green', alpha=0.8)
factory.add_mesh_callback(position_object='@ball.visual.BallOGL', cell_type='quads', animated=True,
                          at=0, c='green', alpha=0.8)
factory.launch_visualizer(backend='vedo')

# Init the scene graph and run some step of the simulation
Sofa.Simulation.init(root)
for _ in range(10):
    Sofa.Simulation.animate(root, root.dt.value)
factory.close()

Hint

Only SOFA Data fields can be recorded with such a method, style variables will be constant by default. If you write your scene as a Sofa.Core.Controller, you will be able to update other data fields with event handlers (such as onAnimateBeginEvent or onAnimateEndEvent).