28. AGX Dynamics API Access
AGX Dynamics classes may only be used from an AGX Dynamics compatible Unreal Engine module. We call these modules “Barrier” modules because they act as a compilation barrier between the Unreal Engine classes and the AGX Dynamics classes. The barrier module is required because of incompatible compiler settings between Unreal Engine and AGX Dynamics.
These instructions describe how to add a Barrier module to a C++ Unreal Engine project. If you have a Blueprint-only project then it is recommended to first use the New C++ Class wizard from the Unreal Editor since that will convert the Blueprint-only project to a C++ project for you.
In the following all newly introduced names are prefixed with My
.
You may use any project-specific naming convention you may have for your project.
To add a Barrier module to an Unreal Engine project first declare it in the
Modules
array in the project’s .uproject
file as follows:
{
"Name": "MyBarrier",
"Type": "Runtime",
"LoadingPhase": "Default",
"AdditionalDependencies": [
"Engine"
]
}
The name MyBarrier
is just an example, any name will work.
It is also possible to have multiple Barrier modules with different names for different functionality.
The new module is registered in ExtraModuleNames
in the project’s two .Target.cs files
with
ExtraModuleNames.AddRange( new string[] { "MyBarrier" } );
The actual module is a directory named MyBarrier
in the project’s Source
directory.
It should contain MyBarrier.Build.cs
with the following contents:
using UnrealBuildTool;
public class MyBarrier : ModuleRules
{
public MyBarrier(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
bUseRTTI = true;
bEnableExceptions = true;
PublicDependencyModuleNames.AddRange(new string[] {
"Core", "CoreUObject", "Engine", "InputCore", "AGXUnreal", "AGXUnrealBarrier"
});
PrivateDependencyModuleNames.AddRange(new string[] {"AGXDynamicsLibrary"});
}
}
What makes the module a Barrier module is the fact that bUseRTTI
and bEnableExceptions
are set to true.
You may not include any “non-trivial” Unreal Engine headers in a Barrier module.
Examples of forbidden headers are all that causes UObject.h to be included.
It is possible to include use math types such as FVector
and FTransform
.
Adding a dependency on AGXDynamicsLibrary
makes it so that the Barrier module can include header files and link with library files in the bundled AGX Dynamics, available in AGXUnreal/Source/ThirdParty/agx
.
The module needs some C++ code to interact with Unreal Engine.
The following two files should be placed in Source/MyBarrier
, next to MyBarrier.Build.cs.
MyBarrier.h
:
#pragma once
#include "CoreMinimal.h"
MyBarrier.cpp
:
#include "MyBarrier.h"
#include "Modules/ModuleManager.h"
IMPLEMENT_GAME_MODULE(FDefaultGameModuleImpl, MyBarrier);
We declare the functions, classes, and other symbols we wish to provide in header files in the Barrier module’s Public
subdirectory.
These header files will be included in source files that belong to non-barrier modules so no AGX Dynamics headers may be included here.
All references to AGX Dynamics objects must be passed as Barrier
objects.
The AGX Dynamics for Unreal plugin provides a library of such Barrier
types for the most commonly used AGX Dynamics classes and additional Barrier
types may be declared in the project-specific Barrier module.
An example that can manipulate a native AGX Dynamics rigid body:
RigidBodyOperations.h
:
#pragma once
#include "CoreMinimal.h"
class FRigidBodyBarrier;
class MYBARRIER_API FRigidBodyOperations
{
public:
static void ManipulateBody(FRigidBodyBarrier& Barrier);
};
RigidBodyOperations.cpp
:
#include "RigidBodyOperations.h"
// AGX Dynamics includes.
#include "BeginAGXIncludes.h"
#include <agx/RigidBody.h>
#include "EndAGXIncludes.h"
// AGX Dynamics for Unreal includes.
#include "AGX_AgxDynamicsObjectsAccess.h"
#include "RigidBodyBarrier.h"
void FRigidBodyOperations::ManipulateBody(FRigidBodyBarrier& Barrier)
{
agx::RigidBody* Body = FAGX_AgxDynamicsObjectsAccess::GetFrom(Barrier);
if (Body == nullptr)
{
return;
}
// Use Body here.
}
The FAGX_AgxDynamicsObjectsAccess
class gives access to functions that unpack a Barrier object to give access to the underlying AGX Dynamics object.
An alternative is to directly access the AGX Dynamics pointer within the Barrier object.
That require that the inner type is known to the compiler.
Many of those types are defined in the header BarrierOnly/AGXRefs.h
.
Since the inner type makes use of AGX Dynamics class definitions it may never escape the Barrier module, i.e. all includes of BarrierOnly/AGXRefs.h
must be from .cpp
files within a Barrier module.
RigidBodyOperations.cpp
:
#include "RigidBodyOperations.h"
// AGX Dynamics for Unreal includes.
#include "BarrierOnly/AGXRefs.h"
#include "RigidBodyBarrier.h"
void FRigidBodyOperations::ManipulateBody(FRigidBodyBarrier& Barrier)
{
if (!Barrier->HasNative())
return;
agx::RigidBody* Body = Barrier.GetNative()->Native;
// Use Body here.
}
By including RigidBodyOperations.h
in any Unreal Engine module it is now possible to call FRigidBodyOperations::ManipulateBody
from that module.
A FRigidBodyBarrier
instance can be fetched from an UAGX_RigidBodyComponent
using UAGX_RigidBodyComponent::GetNative
if the component has been given a native AGX Dynamics body.
Check with UAGX_RigidBodyComponent::HasNative()
.
If the Component doesn’t have a native then nullptr
is returned from GetNative
.
In general, no native AGX Dynamics objects are create until the BeginPlay
callback, so they aren’t available while working the Unreal Editor.
During Begin Play the native AGX Dynamics objects are created in the order Unreal Engine calls Begin Play on the Components.
During Begin Play, where it is uncertain if a particular Component has had its native created yet, we can call GetOrCreateNative
instead of GetNative
to fast-track the native creation for that Component.
28.1. Container Memory And Different Heaps
Whenever a container, such as agx::Vector
or agx::String
, is returned by-value from an AGX Dynamics function then that container must be passed to agxUtil::freeContainerMemory
before it goes out of scope.
On some platforms different libraries of an applications may use different memory heaps depending on compiler flags.
It is safe to access memory that belongs to another library’s memory heap but it is not safe to free it, i.e. calling free
or delete
.
Therefore, it is not safe to have a Barrier module free memory allocated by AGX Dynamics.
A common source of such memory frees are containers, such as agx::Vector
or agx::String
, returned from AGX Dynamics functions.
When such a container is destroyed it tries to free its heap memory but it may try to free it from a memory heap different from the one the AGX Dynamics library allocated it in, which will lead to memory errors or crashes.
To prevent this AGX Dynamics provides the agxUtil::freeContainerMemory
function.
This function frees the memory from within the AGX Dynamics library and thus use the same memory heap as when the container was allocated.
It is not necessary to call agxUtil::freeContainerMemory
when the container is returned by-reference instead of by-value.
An example:
FString ToString(const agx::Uuid& Id)
{
agx::String IdAGX = Id.str();
FString IdUnreal = Convert(IdAgx);
agxUtil::freeContainerMemory(IdAGX);
return IdUnreal;
}