Tutorials

This page is an introduction to the practical usage of the Deform SDK. The tutorials below range from simple tasks such as defining a hanging cloth, to more involved topics such as animating clothes on top of animated characters. For an explanation on how to install the SDK and add it to your application, see Installation.

Getting started

This tutorial describes how to use the SDK to create a simple hanging cloth. The full code for this tutorial can be found in hanging_cloth.h inside the Demos folder.




Creating a cloth

A cloth of size 4 x 4, consisting of 41 x 41 vertices is added to the simulation by calling pVivace->add_obj_patch. This function returns the ID of the created object, which is used to modify the object below.

unsigned int res = 41;
size_t id_object = pVivace->add_obj_patch(4, 4, res, res);

Translating, rotating and scaling the cloth

To translate, rotate or move the cloth, the move_obj, rotate_obj and scale_obj functions are used. Rotations are done using the angle-axis representation, with angles in radians.

pVivace->move_obj(id_object, 0, 0, 2);
pVivace->rotate_obj(id_object, M_PI, 0, 1, 0);
pVivace->rotate_obj(id_object, M_PI, 0, 0, 1);

Fixing two corner vertices

In order for the patch not to fall indefinitely, two corner vertices are fixed by setting their inverted mass to 0 (or their mass to infinity):

pVivace->set_invmass_obj(id_object, 0, 0);
pVivace->set_invmass_obj(id_object, res - 1, 0);

Enabling self-collision

To enable self-collision, add_coll_self is used. The two scalar parameters determine the kinetic and static friction exerted in the self collisions.

pVivace->add_coll_self(0.01f, 0.02f);

Initializing the simulation

Finally, the simulation is initialized by calling:

pVivace->Init();

This function call "commits" the deformable objects to the physics engine which then initializes the solver constraints, collision data structures etc. In the Deform client, the simulation is automatically updated and rendered in app.cpp. If you are using the Deform SDK in your own application, it is up to you to call pVivace->Update() and render the results.

Creating a deformable object from a triangular mesh

This tutorial describes how to create a deformable object from an imported .obj file, and how to set constraint stiffnesses to change the simulated behavior. The complete code for this tutorial can be found in load_tri_mesh.h inside the Demos folder.




Loading an .obj file

The vertices and triangles are first loaded from an .obj file. The vertices are stored in an (x0, y0, z0, x1, y1, z1, ...) manner.

vector<Scalar> vert;
vector<int> tri;

// Load t-shirt mesh
bool res = LoadObj("data/t_shirt.obj", vert, tri);

if (!res)
    return;

Creating a deformable object from vertices and triangles

If the vertices and triangles were successfully loaded from the file, a deformable object is created by calling the add_obj_tri function:

size_t id_object = pVivace->add_obj_tri(vert.data(), vert.size(), tri.data(), tri.size());

Translating, rotating and scaling the deformable object

Just like with the patch from the tutorial above, objects created with add_obj_tri can be scaled, rotated and translated in the following way:

pVivace->scale_obj(id_object, 1.4f, 1.4f, 1.4f);
pVivace->rotate_obj(id_object, 0.75f, 0, 1, 0);
pVivace->move_obj(id_object, 0, -3, 0);

Setting distance and bending stiffness

The stiffness of the constraints that govern the dynamics of the cloth can be set on a per-object basis like this:

pVivace->set_stiffness_obj(id_object, DISTANCE, 1.0f);
pVivace->set_stiffness_obj(id_object, BENDING, 0.0012f);

The range of possible values for the constraint stiffnesses are between 0 and 1.

Adding self-collision

To enable self-collision, the following lines are used. The two float parameters given to add_coll_self determine the kinetic and static friction exerted in the self collisions. In order to avoid vibrations due to particles that intersect during initialization, remove_overlapping_particles_from_self_coll is used. If the triangles of the input mesh are more or less of the same size, this function call can usually be omitted.

size_t id_self = pVivace->add_coll_self(0.006f, 0.008f);
pVivace->remove_overlapping_particles_from_self_coll(id_self);

Setting gravity and air friction

The amount of gravity and air friction can be tuned by calling set_gravity and set_air_friction.

pVivace->set_gravity(0, 0, 0);
pVivace->set_air_friction(0.0015f);

Initializing the simulation

The simulation is initialized by calling pVivace->Init().

Using implicit colliders

This tutorial describes how to add implicitly defined colliders to the simulation. The Deform SDK includes plane, sphere, capsule, axis-aligned bounding box and object-oriented bounding box colliders (as well as mesh colliders), however in this demo we will stick to just plane and sphere colliders. For more information about all the available colliders, see Colliders.

For the complete code used to achieve the results in the below animation, see friction_sphere.h in the Demos folder.




Creating a cloth and moving it

A cloth of size 5.5 x 5.5, consisting of 41 x 41 vertices is instantiated by calling pVivace->add_obj_patch. The cloth is then translated to (0, 3, 0) by calling move_obj.

pVivace->add_obj_patch(5.5f, 5.5f, res, res);
pVivace->move_obj(id_object, 0.0f, 3.0f, 0.0f);

Creating a sphere collider and moving it

A sphere collider is created by calling add_coll_sphere, with a radius of 0.75. The kinetic and static friction are set to 0.7 and 1.4 respectively. This function returns the ID of the created sphere collider, which will be used later to rotate the collider at runtime. To move the collider, the move_coll function is used.

size_t id_sphere = pVivace->add_coll_sphere(0.75f, 0.7f, 4.8f);
pVivace->move_coll(id_sphere, 0.0f, 1.15f, 0.0f);

Creating a plane collider

Plane colliders are created by calling add_coll_plane. The first three scalar parameters indicate the normal of the plane. In this demo the plane normal should point upwards, and thus we set it to (x: 0, y: 1, z: 0). The kinetic and static friction are set to 0.95 and 1 respectively.

size_t id_plane = pVivace->add_coll_plane(0, 1, 0, 0.95f, 3.25f);

Initializing the simulation

As usual, the simulation is initialized by calling pVivace->Init().

Rotating the collider at runtime

To rotate the collider, the rotate_coll function is used. The rotation is described using an angle-axis representation, ie. how many radians to rotate and around which axis. The last three parameters define the center of rotation. Note that this function is called each time the simulation is updated.

pVivace->rotate_coll(id_sphere, ((M_PI * 2.f) / 200.f), 0.f, 1.f, 0.f, 0.0f, 0.0f, 0.0f);

Simulating clothes on top of an animated character

This tutorial describes how to simulate clothes on top of an animated character using an animated mesh collider. The full source code used to achieve the results in the animation below is available in characterm_walking_berkeley_pants_shirt.h in the Demos folder.




Initial configuration

For the best possible results the clothes should be placed in a configuration where they don't penetrate the mesh of the character when the simulation is started. This can be done in several ways. The most obvious way is to just use clothing meshes that don't penetrate the character mesh, and add them using the add_obj_tri function discussed in the tutorial above. When this is not possible, clothes that penetrate the character can first be simulated as usual, and after manually displacing them using mouse picking, the resulting configurations can be stored in a file. This file can then be loaded the next time the simulation is run, to ensure that the clothes are on the outside of the character when the simulation starts. This latter approach is used in the demo below, where manually displaced clothes are saved to a file using pVivace->SaveSimulationData and loaded using pVivace->LoadSimulationData and pVivace->add_serialized_objects:

pVivace->LoadSimulationData("data/scene_data/CharacterMWalkingBerkeleyPantsShirt.def");
pVivace->add_serialized_objects();

More information about how to save and load deformed configurations can be found under Serialization.

Setting the number of physics iterations

The number of physics iterations determines (among other things) the precision of the collision handling against animated mesh colliders. A higher value leads to better precision, at the cost of performance. If you notice that collisions against an animated mesh collider are missed, the following function can be called to set the number of physics iterations:

pVivace->set_physics_iterations(12);

Loading a skeletal animation

The AnimationImporter class included in the Deform client uses the Assimp library to import skeletal animations in .fbx format. Other formats supported by Assimp should work as well, but they haven't been tested. Given a path to an .fbx file containing a skeletal animation, the LoadAnimation function outputs vertices, normals and triangles of the animation in its default pose.

vector<Scalar> c_vert;
vector<Scalar> c_norm;
vector<int> c_tri;

anim_importer = new AnimationImporter();

anim_importer->LoadAnimation("data/male_walking.fbx", c_vert, c_norm, c_tri, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.2f, 0.2f, 0.2f);

The 10 scalar parameters at the end of the call to LoadAnimation defines the desired translation, rotation (angle-axis) and scale of the imported animation.

Creating the mesh collider

Mesh colliders can be created using two functions, either add_coll_mesh or add_coll_mesh_remeshed. 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 of the remeshed version is specified as a parameter to add_coll_mesh_remeshed. In the example below, 5000 vertices are used.

id_coll = pVivace->add_coll_mesh_remeshed(c_vert.data(), c_norm.data(), c_vert.size(), c_tri.data(), c_tri.size(), 5000, 0.0055f, 0.55f, 1.85f);

Both functions used for creating mesh colliders have a bias parameter, which is used to enlarge the mesh collider slightly to avoid Z-fighting with the mesh that is being rendered. The last two parameters are kinetic and static friction respectively.

Retrieving the first frame of the animation

When calling the LoadAnimation function as shown above, the retrieved vertices are in the default pose defined by the animation. For a character animation, this is usually a T-pose. In order to get the vertex configuration at the first frame of the animation, the GetAnimationAtTime is used, with time (t) set to zero:

Scalar t = 0;

anim_importer->GetAnimationAtTime(t, c_vert, c_norm);
pVivace->update_coll_mesh(id_coll, &(c_vert[0]), &(c_norm[0]));

To update the mesh collider with the new vertex (and normal) configuration, update_coll_mesh is called with the ID of the mesh collider that was given when creating it.

Initializing the simulation

As usual, the simulation is initialized by calling pVivace->Init().

Updating the animated mesh collider at runtime

To animate the character at runtime, and update the mesh collider, the functions used for retrieving the first frame of the animation are reused with an increasing value of t:

t += 0.012f;

anim_importer->GetAnimationAtTime(t, c_vert, c_norm);
pVivace->update_coll_mesh(id_coll, &(c_vert[0]), &(c_norm[0]));