Concepts

This page describes the concepts involved in working with the Deform SDK.

The Vivace interface

The Vivace interface acts as the communication channel between your application and the Deform SDK. All functionality related to the physics engine is accessed via this interface. To create and initialize an instance of Vivace, the following functions are used:

IVivace* pVivace = CreateVivace();
pVivace->Reset();
pVivace->Load("./deform_config.xml");

After these functions have been called, it is possible to set simulation parameters, add deformable objects and colliders, and run the simulation. After all deformable objects and colliders have been added, the simulation is started by calling:

pVivace->Init();

To forward the simulation by one frame, call:

pVivace->Update();

Before the application exits, the following functions are called to destroy the Vivace instance:

pVivace->Shutdown();
DestroyVivace();

The following image shows the life cycle of the Deform SDK:




Platforms

The Deform SDK currently supports Windows 10, either using an x86 CPU or a NVIDIA CUDA-capable GPU. It is possible to select whether to simulate using the CPU or GPU by changing computation_type in the provided deform_config.xml.

For a performance comparison between the available computation platforms, see Benchmarks.

Simulation parameters

The Deform SDK provides several global simulation parameters that can be used to modify the behavior of the deformable objects. The default values for these parameters can be seen in deform_config.xml.

Physics iterations

The number of physics iterations performed each frame and the size of the time step together determine how much time is simulated between each frame, and in how large increments. For example, when using 8 physics iterations and a time step of 2 milliseconds, each call to pVivace->Update() will forward the simulation by 8 * 2 = 16 milliseconds.

The reason for dividing the frame into several physics iterations is to guarantee that the discrete parts of the simulation (eg. collision detection) maintain an acceptable quality. If the time step is too high, the simulated particles will move too much between collision detection passes and collisions will be missed. On the other hand, lowering the time step and increasing the number of physics iterations will result in higher quality collision detection. In order to set the number of physics iterations, use:

pVivace->set_physics_iterations(int num_iterations);

Solver iterations

The number of iterations performed by the solver affects the accuracy of the simulation, as well as the performance. Generally, a higher number of iterations results in a more accurate simulation at the cost of performance. To set the number of solver iterations, use:

pVivace->set_solver_iterations(int num_iterations);

Gravity

Gravity is defined by 3 scalar values, containing the amount of gravity in the X, Y and Z axes. To set it, use:

pVivace->set_gravity(Scalar x, Scalar y, Scalar z);

Wind

Wind is defined by 3 scalar values containing the amount of wind in the X, Y, and Z axes. To set it, use:

pVivace->set_wind(Scalar x, Scalar y, Scalar z);

Air friction

Air friction is defined by a single scalar value between 0 and 1. To set it, use:

pVivace->set_air_friction(Scalar air_friction);

Deformable objects

The Deform SDK supports several types of deformable objects that can all be manipulated using a common API. The functions available to all deformable objects are described below. For the exact function definitions, see the API.

Translating, rotating and scaling

Translating, rotating and scaling the deformable objects is performed by calling move_obj, rotate_obj and scale_obj respectively.

Geometry

A large number of functions exist for retrieving and manipulating the geometrical data of the deformable objects, ie. the vertices, normals and faces. These functions are used in the Deform client to perform tasks such as rendering, mouse picking etc.

Constraint stiffnesses

The stiffness values dictate how strict the constraints should be enforced. These parameters are tunable by the user, using the function

set_stiffness_obj(size_t id_object, ConstraintType ct, Scalar value);

Where the ConstraintType is an enum:

enum ConstraintType { DISTANCE, BENDING, TETRA, DAMPER, CONSTR_NONE }

These constraint stiffness values can be tuned to produce the wanted behaviour of the deformable object. Note that the stiffness is applied to all constraints of the object. Currently there is no way to set the stiffness of individual constraints.

The distance constraint governs how far apart two particles can be. With a lower distance stiffness, the cloth appears more stretchy.


On the left: Hanging cloth with distance stiffness of 1.0. On the right: Hanging cloth with distance stiffness of 0.25.



Bending is an essential property to simulate, when aiming to create believable cloth simulations. This constraint governs the angle between two particles in relation to two other particles.

The bending stiffness specifies how strict the bending constraints are enforced. With a low bending stiffness value, the cloth appears wrinklier.


On the left: Ribbon with bending stiffness of 0.5. On the right: Ribbon with bending stiffness of 0.005.



Inverted mass

Each particle in the simulation has an inverted mass value associated with it. By setting this value to 0, particles can be made impossible to move by external forces. To set the inverted mass of a particle belonging to a deformable object, use:

pVivace->set_invmass_obj(size_t id_object, size_t id_particle, Scalar value);

Deleting a deformable object

To delete a deformable object from the simulation, use:

pVivace->del_obj(size_t id_object);

Patches




Patches are flat, rectangular pieces of cloth with a predefined topology that is suitable for simulation. To add a patch to the simulation, use:

pVivace->add_obj_patch(Scalar size_x, Scalar size_y, unsigned int resolution_x, unsigned int resolution_y)

The size parameters determines the size of the patch along its two dimensions, and the resolution parameters determine the number of vertices along each dimension. For the best results, the resolution parameters should be divisible by their respective size parameters.

Triangular meshes




Deformable objects can be created from lists of vertices and triangles. To create this kind of deformable object, use:

pVivace->add_obj_tri((Scalar * vert, size_t vert_size, int * tri, size_t tri_size);

The size parameters define the number of elements in the preceding lists.

Volumetric meshes




The Deform SDK has limited support for volumetric (tetrahedral) meshes. To create a deformable object using a volumetric mesh, use:

add_obj_tetra(const char * filename);

The filename parameter should refer to a .mesh file.

Colliders

There are currently six different types of colliders:

All these colliders are defined in different ways, although all colliders except the self-collider have a position and a rotation. The position and rotation are in relation to the transform point, which is in the center of the collider.

Self collision

The self-collision utilizes sphere-sphere collision handling between the particles the cloth consists of. The self-collisions are enabled by adding a self collider to the simulation:

add_coll_self(Scalar mu_k, Scalar mu_s);

The self collider works best if the triangles of the simulated meshes have a uniform size. If it does not, there are a couple of options which might improve the quality of the self-collisions.

enable_surface_sampling(size_t id_coll) can be called on the self collider. With the surface sampling enabled, the self collider will not use the physics particles, but instead a set of particles uniformly sampled across the surface of the cloth. Note that this function only works with flat two-dimensional meshes. An example of this functionality can be seen in the surface_vertex_collision.h demo.

If the self collider produces a lot of vibrations, it could be that two or more particles have ended up in a configuration where the physics particles overlapped in initialization. To prevent this from happening, the function remove_overlapping_particles_from_self_coll(size_t id_coll) can be called. This will remove the overlapping particles from the self-collisions, all together.

Plane collider

A plane collider can be added to the simulation using:

add_coll_plane(Scalar nor_x, Scalar nor_y, Scalar nor_z, Scalar mu_k, Scalar mu_s);

The plane collider is infinitely large. It is defined by its normal. When adding the plane collider, it is located at the origin. To move it, use move_coll. The plane collider is being used in the demo ribbon.h.




Sphere collider

A sphere collider can be added to the simulation using:

add_coll_sphere(Scalar radius, Scalar mu_k, Scalar mu_s);

The sphere collider is defined by its radius, which needs to be larger than 0. To set the position of the sphere collider, use move_coll.




Capsule collider

A capsule collider can be added to the simulation using:

add_coll_capsule(Scalar radius0, Scalar radius1,
                 Scalar p0_x, Scalar p0_y, Scalar p0_z,
                 Scalar p1_x, Scalar p1_y, Scalar p1_z,
                 Scalar mu_k, Scalar mu_s);

The capsule collider has two radii, which defines how large the end spheres of the capsule should be. The radii need to be larger than 0. Furthermore, it has two points between which the capsule is defined. To see an example of the capsule collider being used, see the demo stitching.h.




Box collider

The box collider can be created using two different functions:
add_coll_box
add_coll_obbox

The difference between these two boxes is that the obbox is object-oriented, meaning it can be rotated. However, it is slightly slower for this reason. These two colliders are defined in different ways.

add_coll_box(Scalar bb_min_x,
             Scalar bb_min_y,
             Scalar bb_min_z,
             Scalar bb_max_x,
             Scalar bb_max_y,
             Scalar bb_max_z,
             Scalar mu_k, Scalar mu_s);

The axis-aligned box is defined by the minimal and maximal bounds of the box, which can be seen in the picture below. The axis-aligned box is being used in collision_box.h. Note that the values of bb_min must be less than the values of bb_max.




add_coll_obbox(Scalar cx,  Scalar cy,  Scalar cz,
               Scalar x_x, Scalar x_y, Scalar x_z,
               Scalar y_x, Scalar y_y, Scalar y_z,
               Scalar z_x, Scalar z_y, Scalar z_z,
               Scalar ex,  Scalar ey,  Scalar ez,
               Scalar mu_k, Scalar mu_s);

The object oriented box is defined by:

The object-oriented box is being used in collision_obbox.h.




Mesh collider

Mesh colliders are can be created from lists of vertices and triangles. A mesh collider can be added to the simulation using either

add_coll_mesh(Scalar * vert,
              size_t vert_size,
              int * tri,
              size_t tri_size,
              Scalar bias,
              Scalar mu_k,
              Scalar mu_s);

or

add_coll_mesh_remeshed(Scalar * vert,
                       size_t vert_size,
                       int * tri,
                       size_t tri_size,
                       size_t num_remeshed_vertices,
                       Scalar bias,
                       Scalar mu_k,
                       Scalar mu_s);

The former uses the input mesh as it is, while the latter creates a new mesh with the same shape as the input mesh, but with a topology better suited to the internal collision handling data structure. The number of vertices used in the remeshed version is defined by the num_remeshed_vertices parameter.




On the left: A mesh collider created using add_coll_mesh. On the right: A mesh collider created using add_coll_mesh_remeshed.

To update a mesh collider with a new vertex configuration, use:

update_coll_mesh(Scalar * vert, size_t vert_size);

Anchoring




By calling anchor_to_coll, a deformable object can be anchored to a sphere or capsule collider as seen in the animation above. When the collider is translated or rotated, the anchored vertices follow.

Sticky colliders




Colliders can become "sticky" by calling set_coll_sticky. If a collider is sticky, any part of a deformable object that collides with it will be stuck to the collider.

Sewing




Sewing can be used to attach two deformable objects to each other. To sew two objects, call sew_objs with the ID's of the objects, a list of vertex pairs to sew together and the size of that list. An optional parameter called priority can be passed to define in which order the seams will be activated. A lower value means a higher priority.

pVivace->sew_objs(id0, id1, seam0.data(), seam0.size(), 0);
pVivace->sew_objs(id0, id1, seam1.data(), seam1.size(), 100);

In the example above, seam1 will be activated 100 frames after the seam0 has been completed.

Serialization

Serialization can be used to store and load the deformed configuration of deformable objects created using add_obj_tri. By serializing objects this way, the original rest values of the distance and bending constraints will be preserved, along with the vertices and triangles that define the initial shape of the deformable object. To save the deformed state of all objects in the scene, use SaveSimulationData:

SaveSimulationData(const char* path);

To load the previously stored file and add the objects to the simulation, call:

LoadSimulationData(const char* path);
add_serialized_objects();

Serialization is currently an experimental feature, and the API calls to perform these and similar tasks may change substantially in the future.