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 glmBeginGeometryGeneration, glmCreateEntityGeometry 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;...
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
glmEndGeometryGeneration(&context);
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();