xissburg / Edyn
Projects that are alternatives of or similar to Edyn
Edyn (pronounced like "eh-dyin'") stands for Entity Dynamics and it is a real-time physics engine organized as an ECS (Entity-Component System) using the amazing EnTT library. The main goals of this library is to be multi-threaded and to support networked and distributed physics simulation of large dynamic worlds.
It is still in an early stage of development and is not yet ready for use. Feel free to explore and contribute meanwhile.
Examples are located in a separate repository: Edyn Testbed
Build Instructions
Requirements
A compiler with C++17 support is required, along with CMake
version 3.12.4 or above.
Dependencies:
Steps
In the Edyn directory:
$ mkdir build
$ cd build
$ conan install ../conanfile.txt
$ cmake ..
$ make
The ECS approach
Typical physics engines will offer explicit means to create objects such as rigid bodies, whereas in Edyn object creation is implicit due to the entity-component design. A rigid body is created from the bottom up, by associating its parts to a single entity, such as:
entt::registry registry;
auto entity = registry.create();
registry.emplace<edyn::dynamic_tag>(entity);
registry.emplace<edyn::position>(entity, 0, 3, 0);
registry.emplace<edyn::orientation>(entity, edyn::quaternion_axis_angle({0, 1, 0}, edyn::to_radians(30)));
registry.emplace<edyn::linvel>(entity, edyn::vector3_zero);
registry.emplace<edyn::angvel>(entity, 0, 0.314, 0);
auto mass = edyn::scalar{50};
registry.emplace<edyn::mass>(entity, mass);
auto &shape = registry.emplace<edyn::shape>(entity, edyn::box_shape{0.5, 0.2, 0.4});
registry.emplace<edyn::inertia>(entity, shape.inertia(mass));
registry.emplace<edyn::material>(entity, 0.2, 0.9); // Restitution and friction.
registry.emplace<edyn::linacc>(entity, edyn::gravity_earth);
There's no explicit mention of a rigid body in the code, but during the physics update all entities that have a combination of the components assigned above will be treated as a rigid body and their state will be updated over time as expected. The update may be carried as follows:
// Apply gravity acceleration, increasing linear velocity.
auto view = registry.view<edyn::linvel, const edyn::linacc, const edyn::dynamic_tag>();
view.each([dt] (auto entity, edyn::linvel &vel, const edyn::linacc &acc, [[maybe_unused]] auto) {
vel += acc * dt;
});
// ...
// Move entity with its linear velocity.
auto view = registry.view<edyn::position, const edyn::linvel, const edyn::dynamic_tag>();
view.each([dt] (auto entity, edyn::position &pos, const edyn::linvel &vel, [[maybe_unused]] auto) {
pos += vel * dt;
});
// ...
// Rotate entity with its angular velocity.
auto view = registry.view<edyn::orientation, const edyn::angvel, const edyn::dynamic_tag>();
view.each([dt] (auto entity, edyn::orientation &orn, const edyn::angvel &vel, [[maybe_unused]] auto) {
orn = edyn::integrate(orn, vel, dt);
});
Assigning each component to every rigid body entity individually quickly becomes a daunting task which is prone to errors, thus utility functions are provided for common tasks such as creating rigid bodies:
// Equivalent to implicit example above.
auto def = edyn::rigidbody_def();
def.kind = edyn::rigidbody_kind::rb_dynamic;
def.position = {0, 3, 0};
def.orientation = edyn::quaternion_axis_angle({0, 1, 0}, edyn::to_radians(30));
def.linvel = edyn::vector3_zero;
def.angvel = {0, 0.314, 0};
def.mass = 50;
def.shape_opt = {edyn::box_shape{0.5, 0.2, 0.4}}; // Shape is optional.
def.update_inertia();
def.restitution = 0.2;
def.friction = 0.9;
def.gravity = edyn::gravity_earth;
auto entity = edyn::make_rigidbody(registry, def);
Basics
Edyn is built as a multi-threaded library from the ground up which requires initializing its worker threads on start-up invoking edyn::init()
, and then a edyn::world
must be created before setting up the scene:
#include <entt/entt.hpp>
#include <edyn/edyn.hpp>
edyn::init();
entt::registry registry;
auto &world = registry.set<edyn::world>(registry);
// Create rigid bodies and constraints...
// Call `edyn::world::update()` periodically in your main loop somewhere.
for (;;) {
world.update();
// Do something with the results, e.g. render scene.
// ...
}
When edyn::world::update()
is called, it processes any pending changes, creates/destroys workers if needed, dispatches messages to workers, reads and processes messages from workers which are merged into the entt::registry
, perparing the entities and components to be rendered right after.
Due to its multi-threaded nature, all changes to relevant components in the main entt::registry
need to be propagated to the worker threads. The edyn::world
doesn't automatically pick up these changes, thus it's necessary to notify it either by calling edyn::world::refresh()
or assigning a edyn::dirty
component to the entity and calling some of its functions such as edyn::dirty::updated()
(e.g. registry.emplace<edyn::dirty>(entity).updated<edyn::position, edyn::linvel>()
).
Documentation
See docs/Design.md to learn more about the engine's planned architecture.