PhysX

Overview

PhysX is a useful library to represent entities (known as actors in PhysX) in 3D worlds, attaching to them various shapes, types of materials, handle collisions, etc.
Simulate allows the library to be used in a distributed way. It handles PhysX serialization when entities move between Simulate cells on the same and different machines. Therefore, only exposing the software developer to standard PhysX programming practice.
Prerequisites
Simulate supports version 4.1.0 of PhysX (refer to PhysX documentation). You must include the following in simulate.hh
#include <PxPhysicsAPI.h>
#include <aether/physx/physx.hh>

Walkthrough

Simulate's PhysX integration deals with managing the per-PhysX instance state, the per-entity/actor state and the complex serialization. The current API is that you include a c_physx component on your entity, call physx->add_actor(*actor) to connect an actor and call physx->add_to_simulation() to add the actor to the simulation. You create actors as you normally would in PhysX. The one gotcha to be careful of is that we do not yet support sharing of shapes, materials or other things between actors (you may get a segfault if you do share). Every actor must have at least a unique shape and material created for it.
Cell initialization can be defined as follows (please note that the code uses ECS physx_update_system , but coding in non-ECS way is also allowed):
void user_cell_state_impl::initialise_cell(const aether_state_type &aether_state) {
store.user_data = std::make_unique<aether::physx::physx_state>(aether::physx::physx_state::basic_scene(new event_callback(store)));
store.add_system<physx_update_system>();
// ...
}
In order to access the physx_state through the user_data field of the ECS, you need to configure the ECS, like so:
using user_cell_state = aether::ecs::ecs<octree_traits, component_types, std::unique_ptr<aether::physx::physx_state>>;
event_callback is used to handle events from PhysX, e.g. onTrigger, onContact, onConstraintBreak, onSleep, onWake. event_callback should inherit from physx::PxSimulationEventCallback, and override the method you care about, such as onContact in the tutorial. This allows you to get a callback when two PhysX objects come into contact.
Cell deinitialization would be defined similarly. In this case, the state is properly destructed by the destructor of unique_ptr and aether::physx::physx_state.
initialise_world would contain the initial world definition:
void user_cell_state_impl::initialise_world(const aether_state_type &aether_state) {
aether::physx::physx_state *physx_state = store.user_data.get();
// ...
}
physx_update_system ECS system needs to be defined by the user in the following way (see Game Logic and the ECS ):
struct physx_update_system {
using accessed_components = std::tuple<c_physx>;
using ecs_type = aether::constrained_ecs<user_cell_state, accessed_components>;
void operator()(const aether_cell_state<octree_traits> &aether_state, ecs_type &store, float delta_time) {
aether::physx::physx_state *physx_state = store.user_data.get();
physx_state->scene->simulate(delta_time);
physx_state->scene->fetchResults(true);
// ...
}
};

Creating PhysX entities

New PhysX based entities can be created as follows using standard PhysX SDK functionalities mixed with Simulate ECS:
for (size_t i = 0; i < num_boxes; i++) {
physx::PxMaterial* material = physx_state->physics->createMaterial(0.5f, 0.5f, 1.0f);
physx::PxShape* shape = physx_state->physics->createShape(physx::PxSphereGeometry(halfExtent), *material);
vec3f position = vec3f{generate_random_f32(), generate_random_f32(), generate_random_f32()} * 64.0f;
vec3f velocity = vec3f::uniform_unit() * 20.0f;
PxTransform transform(PxVec3{position.x, position.y, position.z});
physx::PxRigidDynamic* actor = physx_state->physics->createRigidDynamic(transform);
actor->attachShape(*shape);
physx::PxRigidBodyExt::updateMassAndInertia(*actor, 10.0f);
actor->setLinearVelocity(PxVec3{velocity.x, velocity.y, velocity.z});
auto entity = update.new_entity_local();
auto physx = entity.create_component<c_physx>();
physx->add_actor(*actor);
physx->add_to_simulation();
auto trivial = entity.create_component<c_trivial>();
trivial->id = generate_random_u64();
material->release();
shape->release();
}