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