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;
}