Golaem Geometry Generation C API

Golaem Crowd is shipped with glm_crowd_io.h, a C/C++ header file exposing functions from the glmCrowdIO(.so or .dll) library. The API provided by this file allows to generate geometry and meshes/shaders associations from a simulation cache.

glm_crowd_io.h depends on glm_crowd.h to read Golaem Crowd simulation caches.

Including glm_crowd_io.h and glmCrowdIO.so in a project

The Golaem Geometry Generation API can be added to a project, by adding the glm_crowd_io.h file in your project include path, and adding the glmCrowdIO(.so or .dll) library to your project dependencies & library path.

For custom error handling and memory allocation please refer to glm_crowd.h.

Using the API to create meshes/shaders associations and entities geometry

Error handling

The functions glmBeginGeometryGenerationglmCreateEntityGeometry and glmEndGeometryGeneration return GIO_SUCCESS on success.

If the call is not successful the status will be one of these:

GIO_GSC_FILE_OPEN_FAILED simulation cache files open failed
GIO_CHARACTER_NO_RENDERING_TYPE a character has not been assigned a Rendering Type from the Rendering Attributes panel
GIO_CHARACTER_INVALID_RENDERING_TYPE a character has an invalid Rendering Type. Using the first Rendering Type found
GIO_CHARACTER_NO_MESH_ASSET a character has no mesh asset
GIO_FRAME_NO_DATA insufficient data in simulation cache for the frame context->_frame
GIO_FBX_FILE_OPEN_FAILED failed to load the Fbx file of a Character
GIO_FBX_FILE_MESH_NOT_FOUND failed to find a mesh in the Fbx file of a Character
GIO_MESH_NO_SHADER_GROUP failed to find a shader group for a mesh
GIO_INVALID_CONTEXT context passed as a parameter is invalid, either it has not yet been created or destroyed
GIO_GCG_FILE_OPEN_FAILED failed to load the Gcg file of a Character
GIO_GCG_FILE_BAD_FORMAT the gcg file has a bad version or is malformed (potentially not a gcg file)
GIO_GCG_BAD_CACHE the file cache read has a different point count than base mesh
GIO_GCG_BONE_SCALE_NOT_ONE the skeleton has a bindpose scale not at one
GIO_GCG_FILE_NO_MESH_FOUND could not match any of the asset meshes in the gcg file
GIO_GCG_FILE_MESH_NOT_FOUND could not find soome of the assets meshes
GIO_GCG_ROOT_NOT_FOUND the gcha root bone cannot be found in the gcg file
GIO_GCG_BONE_NOT_FOUND a bone could not be found in the gcg file

Status codes can be converted to printable error messages using this function:

const char* glmConvertGeometryGenerationStatus(GlmGeometryGenerationStatus status);

Log callbacks can also be registered to get more information on the process, callbacks must have this signature:

typedef void(*glmLogCallback)(const char* msg, void* userData);

And can be registered by calling:

void glmRegisterLogCallback(glmLogCallback callback, void* userData);

Initializing the glmCrowdIO library

Before calling any other function from the API it is needed to initialize the library, this should be done once.

glmInitCrowdIO();

Begin geometry generation for a frame

Per frame it is necessary to allocate and set the input parameters of a GlmGeometryGenerationContext struct as in the example below:

GlmGeometryGenerationContext context;
memset(&context, 0, sizeof(GlmGeometryGenerationContext));
context._frame = 10;
context._crowdFieldCount = 2;
context._crowdFieldNames = (char(*)[GIO_NAME_LENGTH])GLMC_MALLOC(context._crowdFieldCount * GIO_NAME_LENGTH * sizeof(char));
strcpy(context._crowdFieldNames[0], "crowdField1");
strcpy(context._crowdFieldNames[1], "crowdField2");
strcpy(context._cacheName, "stadium");
context._cacheDirectoryCount = 1;
context._cacheDirectories = (char(*)[GIO_NAME_LENGTH])GLMC_MALLOC(context._cacheDirectoryCount * GIO_NAME_LENGTH * sizeof(char));
strcpy(context._cacheDirectories[0], "/home/username/maya/projects/default/export/stadium/cache");
context._characterFilesDirectoryCount = 1;
context._characterFilesDirectories = (char(*)[GIO_NAME_LENGTH])GLMC_MALLOC(context._characterFilesDirectoryCount * GIO_NAME_LENGTH * sizeof(char));
strcpy(context._characterFilesDirectories[0], "/home/username/Golaem/GolaemCrowdCharacterPack-4.2.0.1/crowd/characters/crowdMan_light.gcha");
context._excludedEntityCount = 0;
...
Please note the call to memset(&context, 0, sizeof(GlmGeometryGenerationContext)); which is mandatory if you do not plan to initialize all the struct parameters.
 
Once done the output parameters of context will be allocated and set by a call to:
glmBeginGeometryGeneration(&context);

Create entities meshes/shaders associations and geometry

The same function can be called to either create an entity meshes names and shaders, or the meshes geometry, or both.

GlmEntityGeometry geometry;
glmCreateEntityGeometry(&geometry, &context, &context._entityBBoxes[iEntity], 
GLM_CREATE_ALL);

In order to call glmCreateEntityGeometry with the GLM_CREATE_GEOMETRY parameter the function must have been previously called with the GLM_CREATE_NAMES_AND_SHADERS parameter for the same entity.

Destroy entities meshes/shaders associations and geometry

To deallocate an entity geometry this function must be called:

glmDestroyEntityGeometry(&geometries[iEntity], &context, &context._entityBBoxes[iEntity]);

Where as this first function should be called once the entity has been rendered, another function allows to free up the mesh part of the entity earlier (once the mesh has been forwarded to the renderer). It can be safely called after creating the renderer-specific mesh data, and will keep all shader / attributes still allocated and available for the renderer. This function does take care of keeping "master" instanced meshes available. To free mesh data, call :

glmDestroyEntityGeometryMesh(&geometries[iEntity]._meshes[iMesh], context._geometryContext, context._entityBBoxes[iEntity]._crowdFieldIndex);

End geometry generation for a frame

Once all the desired entities geometry have been generated for a frame, the context output parameters can be deallocated calling:
glmEndGeometryGeneration(&context);
The context input parameters allocated by the user must also be deallocated by the user.

Deinitializing the glmCrowdIO library

Once the geometry generation has been done for all the desired frames the glmCrowdIO library can be deinitialized by calling:

glmFinishCrowdIO();

Basic usage sample code

glmInitCrowdIO();
GlmGeometryGenerationContext context;
memset(&context, 0, sizeof(GlmGeometryGenerationContext));
context._frame = 10;
context._crowdFieldCount = 2;
context._crowdFieldNames = (char(*)[GIO_NAME_LENGTH])GLMC_MALLOC(context._crowdFieldCount * GIO_NAME_LENGTH * sizeof(char));
strcpy(context._crowdFieldNames[0], "crowdField1");
strcpy(context._crowdFieldNames[1], "crowdField2");
strcpy(context._cacheName, "stadium");
context._cacheDirectoryCount = 1;
context._cacheDirectories = (char(*)[GIO_NAME_LENGTH])GLMC_MALLOC(context._cacheDirectoryCount * GIO_NAME_LENGTH * sizeof(char));
strcpy(context._cacheDirectories[0], "/home/username/maya/projects/default/export/stadium/cache");
context._characterFilesDirectoryCount = 1;
context._characterFilesDirectories = (char(*)[GIO_NAME_LENGTH])GLMC_MALLOC(context._characterFilesDirectoryCount * GIO_NAME_LENGTH * sizeof(char));
strcpy(context._characterFilesDirectories[0], "/home/username/Golaem/GolaemCrowdCharacterPack-4.2.0.1/crowd/characters/crowdMan_light.gcha");
context._excludedEntityCount = 0;
...
glmBeginGeometryGeneration(&context);
GlmEntityGeometry* geometries = (GlmEntityGeometry*)GLMC_MALLOC(context._entityCount * sizeof(GlmEntityGeometry));
for (unsigned int iEntity = 0; iEntity < context._entityCount; ++iEntity)
{
glmCreateEntityGeometry(&geometries[iEntity], &context, &context._entityBBoxes[iEntity]);
}
...
for (unsigned int iEntity = 0; iEntity < context._entityCount; ++iEntity)
{
GlmEntityBoundingBox* bbox = &context._entityBBoxes[iEntity];
printf("Entity ID: %u \n", bbox->_entityId);
GlmEntityGeometry* geometry = &geometries[iEntity];
printf("Number of meshes: %u \n", geometry->_meshCount);
for (unsigned int iMesh = 0; iMesh < geometry->_meshCount; ++iMesh)
{
GlmMesh* mesh = geometry->_meshes[iMesh];
printf("Mesh name: %s \n", mesh->_name);
printf("Mesh vertice count: %u \n", mesh->_verticeCount);
for (unsigned int iFrame = 0; iFrame < context._frameToProcessCounts[bbox._crowdFieldIndex]; ++iFrame)
{
printf("For frame: %u \n", _framesToProcess[bbox._crowdFieldIndex][iFrame]);
printf("First vertice: [%f, %f, %f] \n", mesh->_vertice[iFrame][0][0], mesh->_vertice[iFrame][0][1], mesh->_vertice[iFrame][0][2]);
}
}
}
...
for (unsigned int iEntity = 0; iEntity < context._entityCount; ++iEntity)
{
glmDestroyEntityGeometry(&geometries[iEntity], &context, &context._entityBBoxes[iEntity]);
}
GLMC_FREE(geometries);
glmEndGeometryGeneration(&context);
GLMC_FREE(context._crowdFieldNames);
GLMC_FREE(context._cacheDirectories);
GLMC_FREE(context._characterFilesDirectories);
GLMC_FREE(context._excludedEntities);
glmFinishCrowdIO();