.. include:: definitions.rstinc .. _rst_simulation: ============================ Creating a Simulation ============================ The namespace agxSDK contains classes that bridges agx (dynamics) and agxCollide (collision). All insertion/deletion of objects such as rigid bodies, geometries, and materials should go through the main class agxSDK::Simulation to make sure that that everything is registered/executed in the correct manner. .. _fig-building-simulations-agxsdk-1: .. figure:: images/building_simulations__agxsdk_1.png Structural description of a Simulation :numref:`Fig. %s ` show the structure of a simulation with the n-ary relationships between the various classes. An agxSDK::Simulation contain one agxCollide::Space and one agx::DynamicsSystem, it also contains any number of agx::Material and any number of agxSDK::EventListeners etc. ----------- Simulation ----------- The class **agxSDK::Simulation** is the class encapsulating most aspects regarding building and executing a simulation. This class holds a reference to an **agx::DynamicsSystem** that is responsible for integrating physical bodies and solving constraint equations, and an **agxCollide::Space** which generate contacts between geometries. All entities that should be part of a running simulation, such as Geometry, Constraint, and EventListener have to be added to the simulation. There can be several instances of the class Simulation, and they will each have their own Space and DynamicsSystem. .. ATTENTION:: Always add/remove objects through the agxSDK::Simulation interface, not via DynamicsSystem or Space. However Space/DynamicsSystem can be used to access objects, such as getting a vector of all Geometries etc. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Timeline for Simulation::stepForward() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. _fig-building-simulations-agxsdk-2: .. figure:: images/building_simulations__agxsdk_2.png Timeline illustrated for a Simulation::stepForward() call. (* indicates parallelizable steps). :numref:`Fig. %s ` show a general picture of flow of events during a call to Simulation::stepForward(). An asterisk (*) indicate that the step is possible to parallelize. *User events* mean that it is possible to inject user code using EventListeners. In reality, there are many, many more tasks executed, many of them parallelizable. .. note:: During the call to stepForward, the *simulation time* will be kept unmodified, except for the call to the :cpp:`last()` callback. This means that the simulation time :math:`t` will be incremented to :math:`d + \delta t` when it is time to call the :cpp:`StepEventListener::last()` callbacks. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Initialization and shutdown of AGX ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To properly initialize AGX, an initialization method needs to be called before creating an AGX object (rigid bodies, geometries, constraints etc.): .. code-block:: c++ agx::init(); Various resources in AGX are handled by Singletons, classes that ensure that only one instance exists. AGX depends on various plugins (components etc.) which are all initialized at the call to agx::init. To properly call the destructors and shut down any threads the last call to AGX should be a call to agx::shutdown(): .. code-block:: c++ agx::shutdown(); The shutdown method is used to release any resources currently allocated by AGX. This includes loaded plugins, running threads, search paths (Environment) etc. **New in 2.3.0.0**: agx:init() can be called again after a call to agx::shutdown() to re-initialize AGX for use. .. ATTENTION:: To properly shutdown any running threads, a call to **agx::shutdown()** should be done *before* the end of scope of main(). You should not register an atExit() callback and execute the call there, as this callback will be executed after the scope of main has ended. So always end your application with a call to the shutdown() method. **agx::AutoInit** is a class that will upon construction call agx::init(), and upon its destruction call agx::shutdown(). This class can be used for handling init/shutdown within a scope. Hence, if an exception is thrown and not caught, the method agx::shutdown will still be called. Just remember that any paths specified for agxIO::Environment will be lost after a call to the shutdown method, this goes for other resources too. You will need to re-initialize these to whatever value you want them to have, just as you did when you first started the application (initialized AGX). ^^^^^^^^^^^^^^^^^^^^^ Shutting down threads ^^^^^^^^^^^^^^^^^^^^^ Having running threads when leaving the scope of main can on the Windows platform cause hanging processes. Therefore, always make sure you either call agx::shutdown() or call the static method **agx::Thread::shutdown()** *before end of the scope of main*. -------------------- agx::DynamicsSystem -------------------- The DynamicsSystem is responsible for all rigid bodies and constraints in a simulation. It sets up a toolchain with solver, integration, etc. The DynamicsSystem stores an instance of an **agx::TimeGovernor** which is responsible for supplying a time step. DynamicsSystem also has an instance **agx::MergeSplit** (:numref:`agx--mergedbody`) which handles the merging/splitting of rigid bodies. ------------------ agxCollide::Space ------------------ Space handles geometric overlap tests such as broad phase tests for testing bounding volume overlaps, and near phase tests which result in detailed contact information: contact point, normal and penetration depth. -------- Events -------- Events are used for adding user code into the simulation. Types of Events implemented in AGX: *add/remove events, collision events, step events* and *gui* events. Event listeners are classes that can be implemented by the user through specialization of base classes supplied by the agxSDK namespace. All listeners are registered to the Simulation class and are triggered by the system whenever appropriate. All instances of the class EventListener have the methods: - addNotification - removeNotification These methods are called whenever an event listener is added/removed from a simulation and can be used for initialization etc. Those methods cannot be filtered away, they will always be executed. The method **EventListener::getSimulation()** is used to get a reference to the Simulation that the listener is currently registered to. .. _fig-building-simulations-agxsdk-3: .. figure:: images/building_simulations__agxsdk_3.png Available event listeners and methods. ^^^^^^^^^^^^^ Filter events ^^^^^^^^^^^^^ Event listeners can be masked/filtered, so that they only are invoked for selected events. Each sub-class of agxSDK::EventListener has a method setMask() and a number of enums specifying for which events the class will be activated For example, specifying that a ContactEventListener should only react to IMPACT and SEPARATION events can be done as follows: .. code-block:: c++ agx::ref_ptr l = new MyContactEventListener; unsigned int mask = agxSDK::ContactEventListener::IMPACT| agxSDK::ContactEventListener::SEPARATION; // Only react to IMPACT and SEPARATION events. l->setMask( mask ); Some event listeners have more detailed filters. For ContactEventListener, see the subchapter "Filters" below. ^^^^^^^^^^^^^^^^^^^ Order of execution ^^^^^^^^^^^^^^^^^^^ The order in which event listeners are executed can be controlled using a priority. This is available for ContactEventListeners and StepEventListener, but not currently for GuiEventListeners. For EventListeners with the same priority, the order should be considered undefined. .. code-block:: c++ simulation->add( listenerA ); simulation->add( listenerB ); In the above example, listenerA *can* be executed before listenerB, but the opposite is equally true. .. ATTENTION:: The order in which Contact/Step event listeners are triggered should be considered undefined when they all have the same priority. This can affect for example a ContactEventListener that removes a contact. There is no guarantee that other ContactListeners will not see that contact. .. code-block:: c++ simulation->add( listenerA, 10 ) ; simulation->add( listenerB, 20 ) ; In the example above, listener B will be executed before listener A. Priority is only valid within the range: .. code-block:: c++ [EventManager::LOWEST_PRIORITY, EventManager::HIGHEST_PRIORITY] .. _collision-events: ^^^^^^^^^^^^^^^^^ Collision Events ^^^^^^^^^^^^^^^^^ During the discrete simulation of a dynamic system, geometric interference calculations are done. They are done in two different steps, one for simpler bounding volumes (currently Axis-Aligned-Bounding-Boxes, AABB) testing for overlap, a process called *broad phase* test. For each pair of AABBs that overlap, a more detailed *narrow phase* test is performed, which can have two outcomes: intersect or disjoint. Disjoint means that even though the bounding volumes overlap, there is still no intersection between the underlying geometries. If the result is intersect, contact data such as contact points, normals and penetration depth are calculated in a class named **agxCollide::GeometryContact**. Each intersection is stored in a list for later use (events and simulation). The events generated by the *narrow phase* test are: *IMPACT, CONTACT* and *SEPARATION*. IMPACT occurs when two geometries intersect for the first time. CONTACT occurs when two geometries that in the previous time step caused an IMPACT event to occur. SEPARATION occurs when two geometries that have intersected (and generated either an IMPACT or CONTACT event) are now disjoint. | Examples of event orders are: | | IMPACT->SEPARATION->IMPACT->SEPARATION | IMPACT->CONTACT->CONTACT->SEPARATION | Collision events are caught by implementing a specialization of a class named **agxSDK::ContactEventListener** and overriding virtual methods with names corresponding to the events. A ContactEventListener can also be told to listen only to a few of the above events by using the **setState()** method. .. _table-contacteventlistener-events: .. csv-table:: Available virtual methods for ContactEventListener :header: "EVENT", "INTERFACE" ,"DESCRIPTION" :widths: 10, 30, 30 "**IMPACT**", "KeepContactPolicy impact( const agx::TimeStamp& agxCollide::GeometryContact \*gc)", "Called upon first impact between two geometries. No prior contact." "**CONTACT**", "KeepContactPolicy contact( const agx::TimeStamp&, agxCollide::GeometryContact \*)", "Called when two geometries have narrow phase contact. Called after impact." "**SEPARATION**", "void separation(const agx::TimeStamp& t, agxCollide::GeometryPair& cd);", "Called when two previously overlapping geometries separates from narrow phase test." .. code-block:: c++ class MyContactListener : public agxSDK::ContactEventListener { public: KeepContactPolicy impact( const agx::TimeStamp&, agxCollide::GeometryContact *gc); }; .. ATTENTION:: EventListeners only operate together with an agxSDK::Simulation. Explicitly calling Space::update() will NOT generate any callbacks to event listeners and might result in missing events. Filters @@@@@@@@ A *ContactEventListener* is activated during contact between two geometries. The question is how to specify for which two geometries the listener should be activated? *agxSDK::ExecuteFilter* is a base class which is specialized by a number of classes: - **agxSDK::GeometryFilter** - can be used to specify which Geometry or pair of Geometries should trigger a listener. - **agxSDK::PropertyFilter** (String, Int, Float, …) - can be used to specify a property or pair of properties that should trigger a listener. - **agxSDK::RigidBodyFilter** - can be used to specify which RigidBody or pair of RigidBodies should trigger a listener. - **agxSDK::RigidBodyGeometryFilter** - can be used to specify which combination of RigidBody and Geometry should trigger a listener. - **agxSDK::CollisionGroupFilter** - can be used to specify which combination of collision groups should trigger a listener. - **Your own** – by inheriting from **agxSDK::ExecuteFilter** and overriding some of its functions, you can specify your own criteria. Examples of *ContactEventListeners* can be found in the examples/agxSDK directory. A filter is bound to the ContactEventListener: .. code-block:: c++ agx::ref_ptr listener = new MyContactListener(); listener->setFilter( new agxSDK::GeometryFilter(geom1) ); simulation->addEventListener( listener ): ^^^^^^^^^^^^^^^^^^ Modifying contacts ^^^^^^^^^^^^^^^^^^ The methods impact and contact have a return type of **KeepContactPolicy**. This is an enum with the following values: .. _table-enum-keepcontactpolicy: .. csv-table:: Values for the enum KeepContactPolicy :widths: auto "**KEEP_CONTACT**", "This contact will be kept and used in the contact solver. (if all other ContactEventListener also return KEEP_CONTACT for this contact)" "**REMOVE_CONTACT**", "This contact will be removed AFTER all other ContactEventListener have operated on it." "**REMOVE_CONTACT_IMMEDIATELY**", "This contact will be removed directly after the return of this method, and no other ContactEventListener executed after this listener will see the contact." So by using the values in :numref:`Table %s ` one can specify whether a contact should be kept or not after the callbacks has been executed. Any data in an **agxCollide::GeometryContact** structure can be modified in a contact callback. If the methods *impact* and *contact* is to return REMOVE or REMOVE_IMMEDIATELY, the contact will be removed from the list of contacts. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Calculating relative velocity ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Given that two geometries overlap in space, the result can be one or more intersection points. The class **agxCollide::GeometryContact** contain all the information necessary for the solver to calculate the required impulses/forces to restore the overlap. This information can also be of use for the user of the toolkit. At a contact event, such as IMPACT, or CONTACT, the contact information is readily available as an argument to the event methods: .. code-block:: c++ KeepContactPolicy impact( const agx::TimeStamp&, agxCollide::GeometryContact* gc); KeepContactPolicy contact( const agx::TimeStamp&, agxCollide::GeometryContact* gc); The argument *gc* is a pointer to a buffer containing all overlaps in the system after the last update to the collision detection system. The actual overlap data is available as: .. code-block:: c++ for( size_t i =0; i < gc->points().size(); i++) { const agxCollide::ContactPoint& p = gc->points()[i]; p.normal(); // contact normal p.point(); // point of contact p.depth(); // penetration depth agx::Vec3 contactVel = gc->calculateRelativeVelocity( i ); } The last call to the method **calculateRelativeVelocity(i)** calculates the relative velocity between two colliding rigid bodies. The argument i specifies the index in the points vector. This method will return a velocity in world coordinates that can be used for calculating sound etc. After the solver is done, the ContactPointBuffer will also be updated with state and force information for each contact: .. code-block:: c++ const agxCollide::ContactPoint& p = gc->points()[i]; agx::Vec3 normalForce = p.getNormalForce()(); agx::Vec3 frictionForce = p.getTangentialForce(); agx::Bool isImpactingPoint = p.hasState( agxCollide::ContactPoint::IMPACTING ); However, this information is not available in a contact event listener, as this event occurs before the solver is done. So to access this information, you need to get a reference to the vector of contacts directly from agxCollide::Space, or by storing a pointer to the ContactPoint which you are interested in. .. ATTENTION:: It's not valid to keep references to contact data (:code:`agxCollide::GeometryContact` and :code:`agxCollide::ContactPoint`) between calls to :code:`stepForward`. The objects are only views into data buffers and becomes invalid when the buffers are resized during collision detection. Below is an example of how to access the actual vector containing points from within an event listener Remember that all event listeners has a method for accessing the simulation in which they exist: .. code-block:: c++ const agxCollide::GeometryContactPtrVector& contacts = simulation->getSpace()->getGeometryContacts(); .. _step-events: ^^^^^^^^^^^^ Step Events ^^^^^^^^^^^^ A step event is generated before and after a discrete step is taken in the simulation. The two events generated are *PRE* and *POST*. **PRE** is generated after the collision detection phase but just before a step is taken in the dynamic simulation. POST is generated directly after a step is taken in the dynamic simulation. **agxSDK::StepEventListener** is a base class which can be specialized by the user by inheritance. .. _table-stepeventlistener-events: .. csv-table:: Available StepEventListener events :header: "EVENT", "INTERFACE", "DESCRIPTION" :widths: 15, 20, 30 "**PRE_COLLIDE**", "void preCollide(const agx::TimeStamp&)", "Called before collision detection is performed. Last chance to add Geometries/Shapes to Space." "**PRE_STEP**", "void pre(const agx::TimeStamp&)", "Called before updating the dynamics information in the system (solver, integrating velocity/position)." "**POST_STEP**", "void post(const agx::TimeStamp&)", "Called after the update of dynamics information. Bodies/Geometries have updated transformations." "**LAST_STEP**", "void last(const agx::TimeStamp&)", "Called last in Simulation::stepForward(). Simulation time is now incremented to :math:`time + \delta t`. Operations executed in this stage will not be included into the timing for agxSDK::Simulation::stepForward()." ^^^^^^^^^^^ GUI Events ^^^^^^^^^^^ GUI events are keyboard presses and mouse events. The Simulation class does not know of how to read of the mouse/keyboard for any specific system Instead an abstract class agxSDK::GuiEventAdapter can be specialized and implemented for a specific system which will inject appropriate events into the Simulation object An example of an implementation can be found in agxOSG::GuiEventAdapter which can be used together with OpenSceneGraph to generate GUI events. To be able to receive GUI events a class named **agxSDK::GuiEventListener** is specialized and implemented. Examples of this can be found in *tutorials/ tutorial_basic1.cpp* ----------- agx::Frame ----------- The class agx::Frame is used to handle multiple coordinate systems. This class contains a transformation (AffineMatrix4x4), velocities, parent/child relations and methods for converting points and vectors between local and world coordinate systems. Parent/child relationship can be constructed with the **Frame::setParent(Frame \*)** method. This makes it possible to build up a scene of transformations. Many classes in AGX such as RigidBody, Geometry, Assembly, etc. use an agx::Frame internally to store transformations and velocities. An example of how to build a structure with parent/child relationship using Frames: .. code-block:: c++ using namespace agx; FrameRef parent = new Frame; FrameRef child = new Frame; child->setParent(parent); parent->setTranslate( Vec3( 1, 0, 0) ); child->getTranslate(); // Now returns (1,0,0) due to its parent parent->setRotate( EulerAngles(0, M_PI/2, 0) ); // Rotate 90 deg around Y. Vec3 localVelocity( 1, 0, 0 ); // Local velocity, 1 in X. Vec3 worldVelocity = child->transformVectorToWorld( localVelocity ); // World velocity is now (0,0,-1) as it is rotated around Y 90 deg. child->getTranslate(); // Now returns (0,0,-1) due to its parents transformation. --------- Assembly --------- An agxSDK::Assembly is a class for logically (and coordinate wise) grouping objects together. It can store a set of: - agx::RigidBody - agxCollide::Geometry - agx::Constraint - agxSDK::StepEventListener - agxSDK::Assembly - agx::Material - agx::ContactMaterial This can be useful when building larger constructions where a logical grouping is needed. An Assembly also contains a Frame which holds a transformation. Each RigidBody, Geometry and Constraint will derive the transformations specified in the Assembly’s Frame. So if an Assembly is moved (Assembly::setPosition()) all contained objects will also be moved accordingly. Schematic example of a function creating a car, returning a pointer to an assembly containing the whole car: .. code-block:: c++ agxSDK::Assembly* buildCar() { agxSDK::Assembly* parent = new agxSDK::Assembly(); agx::RigidBodyRef chassis = new agx::RigidBody(); // ... parent->add(chassis); parent->add(new agx::Material("rubber")); // ... return parent; } ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Adding/Removing an Assembly ^^^^^^^^^^^^^^^^^^^^^^^^^^^ An assembly can contain various objects such as RigidBody, Geometry, StepEventListener, GuiEventListener, ContactEventListener and Constraints. When an Assembly is added to the simulation, all its contained items will also be added to the simulation. The same rule is applied when removing an Assembly from a simulation. This can however be controlled with the second argument to the Simulation::remove method: .. code-block:: c++ bool Simulation::remove(agxSDK::Assembly *assembly, bool removeAllEntries = true ); ^^^^^^^^^^^^^^^^^^^^^^ Derived transformation ^^^^^^^^^^^^^^^^^^^^^^ When a RigidBody is added to an Assembly, the RigidBody derives the Assembly’s transformation/velocity through a Frame/Parent interface. This means that a transformation made on the Assembly, will affect all rigid bodies contained in the Assembly. For example: .. code-block:: c++ body->getFrame()->setLocalTranslate( agx::Vec3(1,0,0) ); // Set the transformation of the rigidbody agxSDK::AssemblyRef assembly = new agxSDK::Assembly(); assembly->add( body ); // Move the assembly up in z 1 unit. assembly->setPosition( agx::Vec3(0,0,1) ); // The line below will be true as body will inherit the assembly's transformation assert(body->getPosition() == agx::Vec3(1,0,1)); ---------- Collection ---------- The class **agxSDK::Collection** is derived from Assembly. It has the same functionality except for the transformation. A Collection does not contain any transformation and it will not take ownership of added objects Frame. Changing the transformation of a Collection has no effect on its children. The Collection class is useful for collecting objects when reading from files, building entities where it is important to keep previous frame/transformation hierarchies.