Soft2D
A 2D multi-material continuum physics engine designed for real-time applications.
|
This documentation provides comprehensive details on the use and implementation of soft2d.
All physical quantity units in soft2d follow the international standard, unless specified otherwise. For instance, parameters related to length should be in 'meters', and those related to velocity in 'meters per second'.
World, body, collider, and trigger are the most important concepts in soft2d.
A world is a container that contains all simulation-related objects within a scene and simulates them over time under physical laws. See World section.
A body is a continuum to be simulated, which is composed of a group of particles. A body has properties such as shape
, center
, material
, etc. See Body section.
A collider is an obstacle within the world that blocks the motion of bodies. See Collider section.
A trigger is a spatial area with a specific shape, which is able to detect particles passing through it. See Trigger section.
The Github repository soft2d-release contains numerous examples demonstrating the usage of soft2d. Users can explore these examples to understand soft2d better.
A world is a container that contains all simulation-related objects within a scene and simulates them over time under physical laws.
Soft2D leverages the Taichi AOT system for GPU execution. Prior to creating a world, a taichi runtime (TiRuntime
) should be initialized to set up the necessary GPU codes and compute backends.
When creating a world, a range of parameters (S2WorldConfig) should be specified as part of its configuration. A typical world configuration can be found Default World Configuration Example, and a detailed explanation of each parameter is provided below.
max_allowed_particle_num
, max_allowed_body_num
, ..., etc.
These parameters specify the capabilities of the world, which affect GPU memory usage. For example, for every particle, we store a series of data, such as its position, velocity, tag, etc. These parameters significantly affect the GPU memory footprint.
grid_resolution
The resolution of the background grid. Users are only required to provide a single integer indicating the maximum resolution value along the x-axis and y-axis. The smaller one will be automatically calculated by soft2d. To obtain both two components of the background's resolution, use s2_get_world_grid_resolution().
offset
The offset of the world. An example of world offset can be found at World Offset Example.
extent
The extent of world. An example of world extent can be found at World Extent Example.
substep_dt
The time step of sub-steps. See Time Integration section.
gravity
World's gravity (meter per second squared).
out_world_boundary_policy
The policy that controls the behavior of a body when it leaves the world. See OutWorldBoundaryPolicy
enable_debugging
Enable the debugging mode. Some buffer exports require this option to be enabled. See S2BufferName.
enable_world_query
Enable world querying. See Trigger Events.
mesh_body_force_scale
A scale factor to adjust the magnitude of mesh bodies' internal force. See Mesh Body.
collision_penalty_force_scale_along_normal_dir
An adjustable parameter to avoid particle-collider penetration. See Collision Handling.
collision_penalty_force_scale_along_velocity_dir
An adjustable parameter to avoid particle-collider penetration. See Collision Handling.
fine_grid_scale
A scale factor to control the resolution of the fine grid. See Fine Grid.
When creating a body, a collider or a trigger, its shape should be specified. A shape describes an object's geometric appearance, such as a box
or a circle
. All supported shapes are listed in S2ShapeType.
Kinematics defines the motion state of a body, collider, or trigger. It includes properties like center
and linear_velocity
. Refer to S2Kinematics for details.
The mobility of an object (S2Body, S2Collider, or S2Trigger) could be static
, kinematic
or dynamic
. See S2Mobility for more details.
Currently, objects have different available mobilities in soft2d:
static
and dynamic
mobilities.static
and kinematic
mobilities.static
mobility.Object | Static | Kinematic | Dynamic |
---|---|---|---|
Body | Y | Y | |
Collider | Y | Y | |
Trigger | Y |
When creating a body, it is necessary to specify a physical material. This defines the object's physical characteristics. Materials Example demonstrates the behavior of different materials.
Currently, soft2d supports four types of materials: fluid, elastic body, snow and sand.
Soft2D is based on the continuum mechanics theory. The constitutive model used in soft2d is the fixed corotated model, which requires Young's modulus and Poisson's ratio as inputs.
Young's modulus describes the "stiffness" of a material. A detailed explanation can be found at Wiki - Young's modulus. In soft2d, we use "MPa" as the default unit for Young's modulus parameters.
Fluids technically do not have Young's modulus. In soft2d, it's used to indicate fluid's degree of incompressibility.
Poisson's ratio describes how a material stretches perpendicularly when compressed in a specific direction. More details are available in Wiki - Poisson's ratio.
A body is composed of a group of particles. Every particle has its own properties, such as position, velocity and tag, etc. These data can be accessed via a particle callback function (See Particle Callback Functions).
Every particle has a user-specified tag, which is useful for logic (See Trigger Events) or rendering. When creating a body, users are required to specify a tag which is then assigned to all particles in that body. The tag of each particle can be later modified in a particle callback function (See Particle Callback Functions) during the simulation.
Upon creation, each particle is automatically assigned a unique, persistent ID. The memory location of a particle in the GPU buffer might change during the simulation. For example, when some particles are removed, left particles in the buffer will be reordered. As a result, one should use a particle's ID to distinguish it from other particles.
A body is a continuum to be simulated, which is composed of a group of particles. A body has properties such as shape
, center
, material
, etc. When the shape of a body is specified, particles will automatically sampled within its area.
There are two kinds of special bodies: CustomBody and MeshBody. They provide more flexibility for users. CustomBody allows users to custom sample particles to form a body. MeshBody enables users to construct a body from a 2D mesh.
The ownership of a body belongs to the world.
When the shape of a body is specified, soft2d automatically samples particles to "fill" that shape. A Poisson's sampling method is employed in this process. The sampled particles' density is decided heuristically and automatically.
In a large world, it might not have the enough accuracy to represent a relatively small body. An example of such an occasion is shown in World Extent Example.
Mesh body is a special type of body. Particles in a mesh body are connected by edges. Edges are like constraints, which "drag" particles. In a mesh body, only particles interact with other bodies, while edges do not. The simulation pipeline of the mesh body is a little bit different from the body, one can adjust the magnitude of the mesh body's internal force by S2WorldConfig.mesh_body_force_scale.
A collider is an obstacle within the world that blocks the motion of bodies.
During the simulation, particles might penetrate into colliders. To avoid this, in soft2d, collision handling is performed after every sub-step. Soft2D uses the penalty method to prevent particle-collider penetration. One can adjust the magnitude of the penalty force via S2WorldConfig.collision_penalty_force_scale_along_normal_dir and S2WorldConfig.collision_penalty_force_scale_along_velocity_dir.
Another occasion when penetration might happen is when colliders overlap particles while using s2_set_collider_position(). In such occasions, particles can be stuck into the colliders. Users should try to avoid this happening.
All colliders are rasterized into a background grid during the simulation. A coarse grid may lead to inaccurate collision handling. To solve this problem, soft2d employs a fine grid alongside the standard background grid to store the discretized colliders (and triggers). The resolution of the fine grid is the product of S2WorldConfig.fine_grid_scale and the resolution of the standard background grid (S2WorldConfig.grid_resolution). See Background Grid for more details.
The figure below demonstrates an intuitive comparison of discretized colliders on grids with different resolutions (grid_resolution
=128, fine_grid_scale
=1, 2, 4, respectively).
Users are able to control collision behaviors by specifying a set of collision parameters (S2CollisionParameter) when creating a collider. These include collision type, friction coefficient and restitution coefficient. The following animations show the effects of different collision parameters.
A trigger is a spatial area with a specific shape, which is able to detect particles passing through it. Like colliders, triggers are also stored on the fine grid.
S2WorldConfig.enable_world_query must be enabled when using triggers.
Currently, world querying can be performed by trigger events.
Trigger events support several operations:
All the above operations support filtering particles with a specific tag. Such functions have a by_tag
suffix.
A trigger allows users to provide a user-defined callback function to access or manipulate particle data. Utilizing particle callback, one can retrieve particle data, such as position, velocity, etc., on the host side. Moreover, users can execute two kinds of manipulations in the callback:
An example of particle callback can be found at Trigger Callback Example.
Soft2D offers several methods to export internal data.
Exporting buffers on the device (GPU) side.
Soft2D allows access to its internal buffers via their names. All available buffer names can be found in S2BufferName. The handle of these buffers can be retrieved by s2_get_buffer() function. One can use utility functions such as ti_copy_memory_device_to_device()
(provided by taichi c-api) to copy the buffer data into their own GPU buffer. Soft2D also provides a s2_export_buffer_to_texture() function. This function copies the data into a user-specified TiTexture
. An example of exporting a buffer to a texture can be found at Debugging Example.
Currently, s2_export_buffer_to_texture() supports only S2_BUFFER_NAME_FINE_GRID_COLLIDER_NUM and S2_BUFFER_NAME_FINE_GRID_TRIGGER_ID.
Exporting particle data (in a trigger) on the host (CPU) side.
Particle callback functions provide a way to access particle data in a trigger. Using a particle callback, one can store particle data in the host memory. See Particle Callback Functions section for more details.
This section introduces the simulation framework of soft2d physics engine.
The fundamental logical flow of soft2d can be visualized with the following flow chart.
The soft2d engine executes several sequential phases during a step:
In soft2d, all simulation-related data is stored on the device (GPU) memory. Data retrieval from GPU to CPU takes place at the post-step stage. If S2WorldConfig.enable_world_query is enabled, particle data will be read back from GPU to CPU. Then, trigger events and user-defined callbacks will be performed on the host particle data. Any user modifications to the particle data will be sent to GPU again.
The detection of particles located in a trigger is performed on the host side.
Real-time applications often run with multiple threads. For instance, most mainstream game engines utilize a logic (game) thread and a rendering thread. The rendering thread is responsible for creating the graphics runtime and drawing the scene. To ensure thread safety within such an environment, users should initialize soft2d on the rendering thread using the game engine's graphics runtime. User operations involving GPU dispatching should also be executed on the rendering thread. To manage multi-threading issues, soft2d queues all user operations, such as object addition or removal, into the s2_step() function. As a result, user need run the s2_step() on the rendering thread.
Here lists all functions that need be invoked on the rendering thread in a multi-threading environment:
Soft2D organizes data across several levels.
This section provides detailed insights into the internal algorithms of the soft2d engine.
Soft2D is based on the continuum mechanics theory, and employs the updated Lagrangian method for physical simulation. The updated Lagrangian method discretizes the continuum using a Lagrangian (particle) method and calculates the force field on the grid.
In the updated Lagrangian method, a background grid is required for computing the force acting at every point in continua (bodies). The resolution of the background grid is a critical factor in determining the quality of the simulation. One can adjust the background grid resolution through S2WorldConfig.grid_resolution. Background Grid Example provides a visualization of the background grid. The following figure shows an intuitive comparison of grids with different resolutions (32x32, 64x64, 128x128).
Besides bodies, both colliders and triggers are discretized into a grid during the simulation. However, to improve the flexibility, soft2d uses a different grid to store colliders and triggers. To distinguish with the background grid, we call the grid storing colliders and triggers as "fine grid". The fine grid's resolution is determined by multiplying S2WorldConfig.fine_grid_scale by the resolution of the background grid. See Collider - Fine Grid section for more details.
As the updated Lagrangian method requires a grid for simulation, a body that leaves the world cannot be simulated. Soft2D will either deactivate or remove such bodies. One can specify this behavior via S2WorldConfig.out_world_boundary_policy.
We use Axis-Aligned Bounding Boxes (AABB) to check if a body leaves the world (The AABB of a body is the minimal axis-aligned box covering all its particles). When the condition
satisfies, we treat this body as it leaves the world. Or a more intuitive way: we treat a body out of the world if any of its particles move out of the scope of the world.
Unlike bodies, by contrast, colliders and triggers can work normally even if they are out of the world.
Despite the need for a background grid, soft2d offers methods for simulating a large world. This can be achieved by adjusting S2WorldConfig.offset and S2WorldConfig.extent.
One can move the world (background grid) by adjusting the world offset. As the result, a larger world is able to be simulated. If a body leaves the world after modifying the world offset, the body will be deactivated or removed according to S2WorldConfig.out_world_boundary_policy.
One can adjust S2WorldConfig.extent to modify the real extent of the grid, which would enable users to simulate physical scenes at different scales. A demonstration of world extent can be found at World Extent Exmaple.
In soft2d, we employ a uniform background grid for performance consideration. This means that all cells have the same size. As a result, simulating extremely small bodies and extremely large bodies in a same scene can be challenging.
Soft2D utilizes the semi-implicit Euler (a.k.a symplectic Euler) method for time integration. Each step (s2_step()) is further divided into several sub-steps, each with a smaller time step. A call to s2_step(dt)
leads to ceil(dt/substep_dt)
times of sub-steps.
Inherently, the semi-implicit Euler method could lead to numerical instability (a.k.a simulation "explosion") if substep_dt
or Young's modulus is too large. One can adjust S2WorldConfig.substep_dt or Young's modulus of the body to avoid the numerical divergence.
Q: Can soft2d contain an empty body?
A: Yes. Empty bodies may occur in the following instances:
The above occasions could lead to an empty body handle. A large number of empty bodies could degrade performance. Therefore, it is better to manually destroy these empty bodies (using s2_destroy_body()) afterward.