New gravity mechanism and design
The PR #2988 opens this discussion.
Today
- gravity is defined though a data in the Nodes (by default
gravity="0 9.81 0") - this gravity is propagated down from the root node to the child nodes
- gravity computation is performed by the mass (if any) in the node
- nowhere appears the gravity as a ForceField (mechanical load) within the node
Typical scene
<Node name="root" gravity="0 -9.81 0" dt="0.01">
<DefaultAnimationLoop name="animationLoop" solveVelocityConstraintFirst="true"
<Node name="Object1">
<EulerImplicitSolver name="ODE0" rayleighMass="0.1" rayleighStiffness="0.1" />
<SparseLDLSolver template="CompressedRowSparseMatrixMat3x3d" name="Linear0" />
<TetrahedronSetTopologyContainer name="Torus0Topo" position="@/Mesh/stuffing.outputPoints" tetrahedra="@/Mesh/stuffing.outputTetrahedra"/>
<MechanicalObject name="Torus0State" template="Vec3d" velocity="@/Mesh/TorusVVel.output_position" rest_position="@/Mesh/stuffing.outputPoints" position="@Torus0VXForm.output_position" />
<TetrahedronSetGeometryAlgorithms name="Torus0Algo" />
<MeshMatrixMass name="MMass" massDensity="2.0" />
<TetrahedronFEMForceField name="FEM" youngModulus="50" poissonRatio="0.45" />
</Node>
<Node name="Object2" >
<EulerImplicitSolver />
<CGLinearSolver iterations="200" tolerance="1e-09" threshold="1e-09"/>
<MechanicalObject template="Rigid3d" name="myParticle" position="0 0 0 0 0 0 1" showObject="1" />
<UniformMass name="UMass" totalMass="1" />
<ConstantForceField totalForce="1 0 0 0 0 0" />
</Node>
</Node>
Advantages
- easy to write a scene with a global gravity
- light nodes (no additional ForceField appearing)
Drawbacks
- prone to error for beginners not seeing that a gravity (external force) is acting
Proposal
- gravity can still be defined though a data in the Nodes (but default value changed
gravity="0 0 0") - using this data gravity generates a warning (not the default/advised way of designing a scene)
- if this gravity data is not null, mass components automatically create a GravityForceField in the node
- GravityForceField have a link towards the mass to avoid recomputing the M matrix
- gravity contribution is added in the matrix system by a ForceField
- GravityForceField must be in the node to compute the gravity
Typical scene
old scene works and now you also can write:
<Node name="root" dt="0.01">
<DefaultAnimationLoop name="animationLoop" solveVelocityConstraintFirst="true"
<Node name="Object1">
<EulerImplicitSolver name="ODE0" rayleighMass="0.1" rayleighStiffness="0.1" />
<SparseLDLSolver template="CompressedRowSparseMatrixMat3x3d" name="Linear0" />
<TetrahedronSetTopologyContainer name="Torus0Topo" position="@/Mesh/stuffing.outputPoints" tetrahedra="@/Mesh/stuffing.outputTetrahedra"/>
<MechanicalObject name="Torus0State" template="Vec3d" velocity="@/Mesh/TorusVVel.output_position" rest_position="@/Mesh/stuffing.outputPoints" position="@Torus0VXForm.output_position" />
<TetrahedronSetGeometryAlgorithms name="Torus0Algo" />
<MeshMatrixMass name="MMass" massDensity="2.0" />
<TetrahedronFEMForceField name="FEM" youngModulus="50" poissonRatio="0.45" />
<GravityForceField name="gravity" gravitationalAcceleration="0 -9.81 0" mass="@MMass" />
</Node>
<Node name="Object2" >
<EulerImplicitSolver />
<CGLinearSolver iterations="200" tolerance="1e-09" threshold="1e-09"/>
<MechanicalObject template="Rigid3d" name="myParticle" position="0 0 0 0 0 0 1" showObject="1" />
<UniformMass name="UMass" totalMass="1" />
<ConstantForceField totalForce="1 0 0 0 0 0" />
<GravityForceField name="gravity" gravitationalAcceleration="0 -9.81 0" mass="@UMass" />
</Node>
</Node>
Advantages
- easy to write a scene with a global gravity
- explicit application of the gravity as an external force
- cleans the API (seperate the mass and FF API)
Drawbacks
- makes the nodes heavier
Questions
- What do you think about it @sofa-framework/reviewers ?
- Should the use of slaves (GravityForceField as a Slave of Mass) be considered ? (I do not think so)
In order to work as expected the automatic addition of the GravityForceField by the node need to be implemented for the two following events:
- react when the "gravity" field is changed to add the GravityForceField not only done at parse/init... and if gravity is set back to zero... should we remove the GravityForceFIeld ?
- have a hook in the addObject(BaseObject*) so that any addition of a Mass Object trigger on a node that had a gravity set ... the function adding the GravityForceField is called.
The underlying logic is complex and looks very hack to me but needed to provide a consistant behavior between two deeply inter-connected component (mass & gravityforcefield). I may be wrong but using a "slave" for the "automatic" use-case would cut that complexity.
@damienmarchal regarding the hook in the addObject(BaseObject*) so that any addition of a Mass Object, should it not simply warn the user that no gravity will be applied if no GravityForceField is added ?