Golaem Simulation Cache C++ API

Golaem Crowd is shipped with a devkit for C++. It is strongly advised to use the API provided by this API to handle Golaem Simulation Cache data. 

The devkit is built with the same compiler than the Maya package it comes with (Visual C++ 14.0 x64 for Maya2018), and contains the header and libraries (glmCore_vcXXXx64.lib and glmCrowdIO.lib) that are needed to build your own project. The execution library (glmCrowdIO.dll) is included in the bin directory of the installation package. Some devkit usage is demonstrated in the glmCrowdSimulationCacheTool and glmCrowdSimulationCacheReaderPlugin samples, which can be found in devkit/samples directory.

Including Golaem Simulation Cache API file in a project

The Golaem Simulation Cache API contains many header files, but only a few should be needed by most users to handle cache simulation, frames and layout:

  • the simulation data is inluded from the glmSimulationData.h header file;
  • the frame data is included from the glmFrameData.h header file;
  • the layout modifications is included from the glmHistory.h header file, and the previous version (GolaemV6 and prior) is included from the glmHistoryV0.h header file;
  • to simplify access to simulation/frame/layout data, a simulation cache factory is provided from the glmSimulationCacheFactory.h header file. It also provides a cache to store simulation data and frame data when it's needed for further computation.

The SimulationCacheFactory is the recommended way of accessing the source simulation and frame data, as well as simulation and frame data that needs to be modified by a layout file.

When a custom memory allocator is needed, the glmMemory.h header file provides the necessary function to set a user defined allocator: or the default allcoator can be used;

#include "glmMemory.h"
glm::setDefaultAllocator(myAllocator);
or
glm::useCoreDefaultAllocator();

Before accessing the cache, the library must be properly initialized, and  the necessary information to check the Golaem licence must be provided with the following lines:

glm::initCore();
glm::crowdio::setupGolaemProduct("GolaemSDK", GlmString(), ProductDetails(), false /*deferLicenseCheck*/, false /*allowCreatePLE*/);
glm::crowdio::init();

Using the Simulation Cache Factory to read simulation and frame data

The Simulation Cache Factory is the recommended way to access simulation or frame data. It'll also handle the cache modifications from a layout file. With both methods, it is assumed that we will search structures and methods in glm::crowdio:: namespace. You can either prefix calls and structs, or use "using namespace" to handle this.

Load an existing cache or frame

Once a Simulation Cache Factory object created, it's quite simple to access the simulation or frame data:

SimulationCacheFactory cacheFactory;
// Define the simulation to access
CachedSimulation& simulation=cacheFactory.getCachedSimulation(cacheDir, cacheName, crowdFieldName);
// Access the sim data
const GlmSimulationData* simData= simulation.getSrcSimulationData();
if (simData==NULL)
   { GlmSimulationCacheStatus status = simulation.getSrcSimulationStatus(); }
// Access the frame data
bool interpolateBetweenFrames = true;
glm::Time frameDataTime = 10;
const GlmFrameData* frameData= simulation.getSrcFrameData(frameDataTime, interpolateBetweenFrames );
if frameData==NULL
   GlmSimulationCacheStatus status = simulation.getSrcFrameStatus(frameDataTime);

If the simulation or frame data is not found, the method returns NULL, and another method is provided to get the error status.

Note that the simulation and frame data belongs to the factory and should not be deleted by the user.

Load a cache or frame with layout modifications

To load a cache with layout modifications, one must first load the layout file, as well as set the source and destination terrains when ground adaptation is expected.

SimulationCacheFactory cacheFactory;

// Layout file
cacheFactory.loadLayoutHistoryFile(GSCLFilePath);

// Layout terrain
crowdTerrain::TerrainMesh* terrainMeshSource = crowdTerrain::loadTerrainAsset(terrainSrcFilePath);
crowdTerrain::TerrainMesh* terrainMeshDestination(NULL);
if(hasTerrainMeshDestination)
     terrainMeshDestination = crowdTerrain::loadTerrainAsset(terrainDstFilePath);
else
     terrainMeshDestination = terrainMeshSource ;
cacheFactory.setTerrainMeshes(terrainMeshSource, terrainMeshDestination);

Once the cache factory is setup, one can get as many simulation or frame data as whished:

CachedSimulation& simulation=cacheFactory.getCachedSimulation(cacheDir, cacheName, crowdFieldName);
// Sim data with layout modifications
const GlmSimulationData* simData= simulation.getModifiedSimulationData();
if simData==NULL
   GlmSimulationCacheStatus status = simulation.getModifiedSimulationData();
// Frame data with layout modifications
bool interpolateBetweenFrames = true;
glm::Time frameDataTime = 10;
const GlmFrameData* frameData= simulation.getModifiedFrameData(frameDataTime, interpolateBetweenFrames );
if frameData==NULL
   GlmSimulationCacheStatus status = simulation.getModifiedFrameStatus(frameDataTime);

Clean-up allocated memory

Simulation data and Frame data belongs to the cache factory and should not be deleted directly by the user. But note that the terrain meshes do not belongs to the factory, so it's the user's responsibility to delete them once not in use anymore.

cacheFactory.setTerrainMeshes(NULL, NULL);
crowdTerrain::closeTerrainAsset(terrainMeshSource);
if(hasTerrainMeshDestination)
     crowdTerrain::closeTerrainAsset(terrainMeshDestination);
SimulationCacheFactory.clear(FactoryClearMode::ALL);

Handling the cache memory

Note that it is not safe to hold pointers on old frame data after calling the getSrcFrameData or getModifiedFrameData because the Simulation Cache Factory might delete old data to ensure it respects the memory budget it was allowed. To stop the factory from deleting frame data for a given period of code, one should encapsulate the code between a lockFrameDeletion() and unlockFrameDeletion() calls:

cacheFactory.lockFrameDeletion();

//here goes my code where I access old pointers to the frame/cache data
//...
//for instance:

simulation.getModifiedFrameData(frameDataTime, interpolateBetweenFrames ); //no old frame data will be deleted now

cacheFactory.unlockFrameDeletion();

simulation.getModifiedFrameData(frameDataTime, interpolateBetweenFrames ); //during this call, old frame data might be deleted if the cache factory needs room for new data

The Simulation Cache Factory cache all previously acceded simulation and frame data for later use, and delete them only when the desired amount of RAM memory is used. One can set and get the memory allowed budget thanks to 2 methods:

cacheFactory.setMemoryBudget(4 * 1024*1024*1024); //4Gb is the default memory budget
cacheFactory.getMemoryBudget();

And one can get the currently used memory from the computeTotalMemory method.

It's also possible to manually cleaning parts of the cache factory by using the clear methods with any combination of the clearMode flag:

void clear(FactoryClearMode::Value clearMode, const glm::Time& frame = -1, uint32_t nodeID = UINT32_MAX);

NONE Nothing to delete
CHARACTERS Will delete characters when they're hold by the Factory
ALL_HISTORY Will delete history file and runtime acceleration structures
SRC_SIM Will delete the source simulation data
SRC_FRAMES Will delete all the source frame data
SRC_SINGLE_FRAME Will delete only a single source frame data (when used in combination with the frame parameter)
ALL_SRC SRC_SIM | SRC_FRAMES
MODIFIED_SIM Will delete the modified simulation data
MODIFIED_FRAMES Will delete all the modified frame data
MODIFIED_SINGLE_FRAME Will delete only a single modified frame data (when used in combination with the frame parameter)
MODIFIED_SINGLE_NODE Will delete only the frame data for a given layout node (when used in combination with the nodeID parameter)
ALL_MODIFIED MODIFIED_SIM | MODIFIED_FRAMES
ALL CHARACTERS | ALL_HISTORY | ALL_SRC | ALL_MODIFIED

Return status

The last read command status can be acceded thanks to 4 methods:

  • getSrcSimulationStatus
  • getModifiedSimulationStatus
  • getSrcFrameStatus
  • getModifiedFrameStatus

The result is is a GlmSimulationCacheStatus Enum, which convey the following meaning:

GSC_SUCCESS No Error, everything went fine.
GSC_UNKNOWN_ERROR An error occurred, but no details can be given on the error
GSC_FILE_OPEN_FAILED File does not exist, or have access rights issues, or is already opened by another process
GSC_FILE_MAGIC_NUMBER_ERROR File header does not begin by 0x65C5 or 0x65CF, this is neither a Golaem Simulation Cache or a Golaem Simulation Frame
GSC_FILE_VERSION_ERROR Incorrect version, could be a newer version of the Golaem Simulation Cache
GSC_FILE_FORMAT_ERROR Incorrect format, could be a newer version of the Golaem Simulation Cache
GSC_SIMULATION_FILE_DOES_NOT_MATCH The GlmSimulationData, used to read that frame file, does not match the one used at the export (the size of some items differ : entity count, etc.)
GSC_SIMULATION_NO_FRAMES_FOUND Only happen when trying to write a frame data. When this happens, quantization must be disabled
GSC_FILE_POSITION_OUT_OF_MAX_BONECHAIN_LENGTH  

 

Using the API to load cache files, read and write data

The Simulation Cache Factory is the recommended way to access simulation or frame data. But if needed, it's also possible to access simulation/frame data without using it.

Load an existing cache

Loading is done in 2 steps.

The first step is to load a GlmSimulationData from a gscs file. It will read shared data valid for all the cache frame, such as sizes and types. There is one gscs (simulation) file per CrowdField to load. These files are loaded via the command :

GlmSimulationData* simulationData;
GlmSimulationCacheStatus status = createAndReadSimulationData(&simulationData, "cacheFilePath/cacheFileName.gscs");

The command returns a status GSC_SUCCESS on success, or an error from the table below.

Once a GlmSimulationData has been loaded, each frame data can be loaded via :

GlmFrameData* frameData;
createFrameData(&frameData, simulationData);
GlmSimulationCacheStatus status = readFrameData(frameData, simulationData, "cacheFilePath/cacheFileName.gscf");

The read command returns a status GSC_SUCCESS on success, or an error amongst:

GSC_SUCCESS No Error, everything went fine.
GSC_FILE_OPEN_FAILED File does not exist, or have access rights issues, or is already opened by another process
GSC_FILE_MAGIC_NUMBER_ERROR File header does not begin by 0x65CF or 0x65CF, this is not a Golaem Simulation Cache
GSC_FILE_VERSION_ERROR Incorrect version, could be a newer version of the Golaem Simulation Cache
GSC_FILE_FORMAT_ERROR Incorrect format, could be a newer version of the Golaem Simulation Cache
GSC_SIMULATION_FILE_DOES_NOT_MATCH The GlmSimulationData, used to read that frame file, does not match the one used at the export (the size of some items differ : entity count, etc.)

Accessing/Modifying Cache data

To see how to access data once the cache is loaded, you can refer to glmCrowdSimulationCacheReaderPlugin source provided in the devkit directory of your Golaem Crowd installation directory. 

Writing a cache 

Once cache data has been modified, it can be written back if required :

GlmSimulationCacheStatus status = writeFrameData("cacheFilePath/cacheFileName.gscf", frameData, simulationData);

Clean-up allocated memory

Note that GlmFrameData can be kept allocated to edit several frames. Once done with reading./editing, each Simulation and frame data must be destroyed by calling the commands :

destroyFrameData(&frameData, simulationData);

then

destroySimulationData(&simulationData);

Basic usage sample code

using namespace glm;
using namespace glm::crowdio;
 
GlmSimulationCacheStatus status;
GlmSimulationData* simulationData;
status = createAndReadSimulationData(&simulationData, gscsFileName);
if (status != GSC_SUCCESS) ...
GlmFrameData* frameData;
createFrameData(&frameData, simulationData);
status = readFrameData(frameData, simulationData, gscfFileName);
if (status != GSC_SUCCESS) ...
...
status = readFrameData(frameData, simulationData, gscfFileName);
if (status != GSC_SUCCESS) ...
...
destroyFrameData(&frameData);
destroySimulationData(&simulationData);