An introduction to soft-body physics
While rope, cloth, and foam rubber can be simulated using many small rigid bodies, it is more convenient and efficient to treat them as individual bodies that can be deformed. To this end, Libbulletjme supports simulation of soft bodies in a manner roughly analogous to rigid bodies:
-
In place of
PhysicsSpace
, usePhysicsSoftSpace
. -
In place of
PhysicsRigidBody
, usePhysicsSoftBody
.
PhysicsSoftSpace
is a subclass of PhysicsSpace
.
It implements soft-body physics in addition to all the
features of an ordinary PhysicsSpace
(such as rigid bodies).
The abstract class PhysicsBody
is a superclass of both PhysicsRigidBody
and PhysicsSoftBody
.
It provides access to properties that rigid bodies and soft bodies
have in common, such as gravity, location, mass, and joints.
Soft bodies can collide with both rigid bodies and other soft bodies.
They can also be joined to bodies of both types, using special subclasses
of PhysicsJoint
.
A comparison of soft bodies and rigid bodies
Unlike a rigid body, a soft body doesn’t have a CollisionShape
or
an orientation.
Instead, it is composed of point masses (called "nodes") whose locations
are specified in physics-space coordinates.
A soft body’s shape, structure, mass distribution, and position are all defined
by its mesh of nodes:
-
To simulate rope, nodes can be connected in pairs (called links).
-
To simulate cloth, nodes can be connected to form triangles (called faces).
-
To simulate foam rubber, nodes can be connected to form tetrahedra (also called tetras).
Unlike a rigid body, the physics location of a soft body is not its center of mass, but rather the center of its axis-aligned bounding box.
Like rigid bodies, soft bodies have collision margins.
However, since a soft body lacks a CollisionShape
,
different accessors are used:
float oldMargin = softBody.margin();
softBody.setMargin(0.1f);
Soft bodies lack many other features of rigid bodies, including:
-
motion state (for extrapolating between simulation steps),
-
deactivation/sleeping (for efficient simulation), and
-
continuous collision detection (CCD) (for fast-moving objects).
Constructing a soft body
To construct a soft body, start with the no-argument constructor:
PhysicsSoftBody softBody = new PhysicsSoftBody()
This produces an empty body (one without any nodes, links, faces, tetras, or joints) that isn’t added to any physics space.
Methods are provided to append nodes, links, and faces to a soft body.
However, it’s often more convenient to generate a
Mesh
with the desired shape and topology and append it to the body
using a utility method:
-
NativeSoftBodyUtil.appendFromTriMesh()
to append nodes and faces from a mesh composed of triangles -
NativeSoftBodyUtil.appendFromLineMesh()
to append nodes and links from a mesh composed of lines
Be aware that meshes intended for graphics rendering often prove unsuitable for soft-body simulation. For instance, they may define multiple vertices at the same position or their edges/faces may be insufficiently subdivided.
Soft-body configuration and pose matching
Each soft body has numerous properties that can affect its behavior.
Most of these are stored in its configuration object, which can be
accessed using getSoftConfig()
.
Soft bodies and configuration objects are one-to-one.
Configuration properties with float
values are enumerated
by the Sbcp
("soft-body configuration parameter") enum.
For instance, a soft body can have a preferred shape (called its "default pose")
that it tends to return to when deformed.
The strength of this tendency depends on the configuration object’s
"pose matching" parameter, which defaults to zero.
For a simple example of a soft body, see HelloSoftBody (also in Kotlin).
Soft-soft collisions
By default, collisions between soft bodies are not handled (ignored).
One way to handle soft-soft collisions for a specific body is to
set the VF_SS
collision flag in its configuration object:
SoftBodyConfig config = softBody.getSoftConfig();
int oldFlags = config.getCollisionFlags();
config.setCollisionFlags(oldFlags, ConfigFlag.VF_SS);
For a simple example of a collision between 2 soft bodies, see HelloSoftSoft (also in Kotlin).
Solver iterations
During each simulation step, Bullet applies a series of iterative solvers to each soft body:
-
a cluster solver
-
a drift solver
-
a position solver
-
a velocity solver
The number of iterations for each solver is stored in the body’s configuration object. When simulating collisions, you can often improve accuracy by increasing the number of position-solver iterations:
SoftBodyConfig config = softBody.getSoftConfig();
config.setPositionIterations(numIterations); // default=1
Stiffness coefficients
Each soft body has 3 stiffness coefficients.
These are stored in its "material" object,
which can be accessed using getSoftMaterial()
.
Soft bodies and their material objects are one-to-one.
To simulate an object that flexes easily (such as cloth), create a soft body with many faces and set its angular-stiffness coefficient to a small value (such as zero):
PhysicsSoftBody.Material softMaterial = softBody.getSoftMaterial();
softMaterial.setAngularStiffness(0f); // default=1
For a simple example of cloth simulation, see HelloCloth (also in Kotlin).
Mass distribution
When a node is appended to a soft body, it has mass=1.
To alter the mass of a pre-existing node, use the setNodeMass()
method:
softBody.setNodeMass(nodeIndex, desiredMass);
You can also alter the total mass of a soft body, distributing the mass across the pre-existing nodes in various ways:
-
in proportion to the current mass of each node, using
setMassByCurrent()
, -
in proportion to the area of adjacent faces, using
setMassByArea()
, or -
in a custom fashion, using
setMasses()
.
softBody.setMass()
is equivalent to setMassByCurrent()
.
If a soft-body node has mass=0, it becomes pinned (immovable, like a static rigid body).
For a simple example of a pinned node, see HelloPin.java (also in Kotlin).
Simulating a rope
HelloSoftRope (also in Kotlin) is a SPORT app that demonstrates one way to simulate rope using a soft body.
TODO: applying forces, anchors, soft joints, world info
Aerodynamics
HelloWind is a SPORT app that simulates the effect of wind on a flag.
Things notice while running the app:
-
The flag is a piece of cloth with 2 of its corners pinned.
-
Wind direction is indicated by the white arrow.
-
Initially, the wind blows from left to right.
-
Press kbd:[Left arrow] and kbd:[Right arrow] to alter the wind direction.
Clusters
By default, soft-body collisions are handled using nodes and faces.
As an alternative, they can be handled using groups of connected nodes
(called "clusters").
To enable cluster-based rigid-soft collisions for a specific soft body,
set its CL_RS
collision flag.
To enable cluster-based soft-soft collisions, set its CL_SS
flag.
Clusters can overlap, but they can’t span multiple bodies. In other words, a single node can belong to multiple clusters, but a single cluster can’t contain nodes from multiple bodies.
When a soft body is created, it doesn’t have any clusters. Once nodes are appended to a body, clusters can be generated automatically, using an iterative algorithm that’s built into Bullet:
softBody.generateClusters(k, numIterations);