Golaem Simulation Cache Python API
Golaem Crowd is shipped with a SWIG generated glm.devkit Python module. It is strongly advised to use the API provided by this file to handle Golaem Simulation Cache data. As the Python module has been generated from the Golaem Simulation Cache C++ API, all structures listed in this page can be inspected in the C++ API, especially the glmSimulationData.h and glmFrameData.h.
Including Golaem Simulation Cache API file in a project
The Golaem Simulation Cache API can be added to a project, by importing the module:
from glm.devkit import *
Initialize/Uninitialize the Golaem API
Initialize the Golaem API
When using Licensing functions, it is required to specify the name of the product to check license for (available product names are GolaemForMaya, GolaemForKatana, GolaemForUnreal, GolaemForHoudini, Standalone) :
initGolaemProduct(productName, pleLicense) # set the second string to ''
Then the Golaem API can be initialized:
initGolaem(bool useThreadedFramedataDecompression) #by default, Golaem will use several threads to decompress frame data, but this can be set to monothreaded if needed
Uninitialize the Golaem API
Then the Golaem API can be uninitialized the following way
finishGolaem()
And the Golaem Product name:
finishGolaemProduct()
Using the API to load cache files, read and write data
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 :
simulationData = createAndReadSimulationData('cacheFilePath/cacheFileName.gscs')
if (simulationData is not None):
print('Success while opening Simulation Data file')
The command returns None on error.
Once a GlmSimulationData has been loaded, each frame data can be loaded via :
frameData = createFrameData(simulationData)
frameStatus = 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 Simulation and Cache data
The simulation data provides access to some global variables such as _entityCount, _entityIds, _characterIdx... The complete list can be accessed from glmSimulationData.h.
They can be accessed through accessors which return array pointers which can be cast in Python array the following way. Notice that each array type has its own cast function.
# as _entityIds is declared as int64_t pointer in glmSimulationData.h, use the relative cast function
entityIds = int64Array_frompointer(simulationData._entityIds)
The cache data can be accessed and modified the following way. The complete list can be accessed from the devkit.py module and the glmFrameData.h header:
getBonePositions(frameData)
getBoneOrientations(frameData)
getSnsValues(frameData)
getGeoBehaviorAnimFrameInfo(frameValue)
getPPFloatAttributeName(simulationData, attrIdx)
getPPFloatAttributeData(frameData, attrIdx)
getPPVectorAttributeData(frameData, attrIdx)
... more accessors in the devkit.py module
Those accessors return float array pointers which can be cast in a Python array the following way:
bonePos = (floatArray_frompointer(getBonePositions(frameData)))
Some accessors are also provided to help dealing with frame data
getEntityBoneCount(entityIndex, simulationData)
getEntityBoneOffsetIndex(entityIndex, simulationData)
Writing a cache
Once cache data has been modified, it can be written back if required :
writeFrameData('cacheFilePath/cacheFileName.gscf', frameData, simulationData)
The write command returns a status GSC_SUCCESS on success, or an error from the above table
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
simulationData = createAndReadSimulationData('cacheFilePath/cacheFileName.gscs')
if (simulationData is not None):
print('Success while opening Simulation Data file')
# read entityIds and characterIds
entityIds = int64Array_frompointer(simulationData._entityIds)
characterIds = int32Array_frompointer(simulationData._characterIdx)
frameData = createFrameData(simulationData)
if (readFrameData(frameData, simulationData, 'cacheFilePath/cacheFileName.gscf') == GSC_SUCCESS):
print('Success while opening Frame Data file')
else:
print('Error while opening Frame Data file')
destroyFrameData(frameData, simulationData)
destroySimulationData(simulationData)
return
# get bone position array
bonePos = floatArray_frompointer(getBonePositions(frameData))
# set root bone height (Y) to 0
entityCount = simulationData._entityCountfor iEntity in range(0, entityCount):# get the bone count for this entityboneCount = getEntityBoneCount(iEntity, simulationData)
# get the bone offset for this entity
boneOffset = getEntityBoneOffsetIndex(iEntity, simulationData)# iterate for each bone of this entity
for iBone in range(0, boneCount):
# each position is made of 3 float (*3), and we access the Y (+1)
bonePos[(boneOffset + iBone) * 3 + 1] = 0
# write frame data
if (writeFrameData('cacheFilePath/cacheFileName.gscf', frameData, simulationData) == GSC_SUCCESS):
print('Success while writing Frame Data file')
# read float attribute names and data available in the cache
for iFlAttr in range(simulationData._ppFloatAttributeCount):
print('Name of attribute {} is {}'.format(iFlAttr, getPPFloatAttributeName(simulationData, iFlAttr)))attrValue = floatArray_frompointer(getPPFloatAttributeData(frameData, iFlAttr))for iEntity in range(simData._entityCount):print(attrValue[iEntity])# clean memory
destroyFrameData(frameData, simulationData)
destroySimulationData(simulationData)
Taking Layout into account
The above example only works with raw simulation and frame data files. A higher level API is available when using Golaem Layout files. This API matches the Golaem Simulation Cache C++ API. It uses internally a Simulation Cache Factory object which needs to be initialized with the following code:
initSimulationCacheFactory()
Once it is initialized, the following steps are needed to setup the Simulation Cache Factory :
Load the Golaem Character files
loadSimulationCacheFactoryCharacters(characterFiles)
The characterFiles parameter is a single string. Multiple files can be specified using the ";" delimiter.
Load the terrain files (if required):
loadSimulationCacheFactoryTerrain(sourceTerrainFile, destinationTerrainFile)
Load the Golaem Layout files:
loadSimulationCacheFactoryLayoutFiles(layoutFiles)
The layoutFiles parameter is a single string. Multiple files can be specified using the ";" delimiter.
After this step, you can obtain a reference to a Cached Simulation through an index:
cachedSimulationIndex = getCachedSimulationIndex(cacheDir, cacheName, simulationName)
To obtain the GlmSimulationData and GlmFrameData that were modified by the Layout files, use the following functions:
getFinalSimulationData(cachedSimulationIndex)
getFinalFrameData(cachedSimulationIndex, frameValue, interpolateBetweenFrames)
# frameValue: float, interpolateBetweenFrames: bool
The unmodified simulation and frame data can also be obtained with:
getSrcSimulationData(cachedSimulationIndex)
getSrcFrameData(cachedSimulationIndex, frameValue, interpolateBetweenFrames)
# frameValue: float, interpolateBetweenFrames: bool
The GlmSimulationData and GlmFrameData objects that are obtained this way cannot be modified, as they belong to the Simulation Cache Factory, which is also responsible for cleaning the memory of the simulation and frame data objects - the destroyFrameData and destroySimulationData functions are not needed.
To obtain a modifiable frame data, use:
createAndCopyFrameData(srcFrameData, simulationData)
# this will need cleanup with destroyFrameData
The cache data can be accessed with the same functions as described in the previous example.
At the end of the script, the Simulation Cache Factory must be uninitialized the following way:
finishSimulationCacheFactory()
Sample code with Golaem Layout
This sample prints out the position of the root bone of each entity in the cache
import glm.devkit as devkit
def displayCacheInfo():
characterFiles = "./characters/CasualMan_Light.gcha"
layoutFiles = "./cache/layout.gscl"
cacheDir = "./cache"
cacheName = "pythonAPISample"
simulationName = "crowdField1"
currentFrame = 400
devkit.initGolaemProduct("Standalone", None)
devkit.initGolaem()
devkit.initSimulationCacheFactory()
devkit.loadSimulationCacheFactoryCharacters(characterFiles)
devkit.loadSimulationCacheFactoryLayoutFiles(layoutFiles)
cacheIndex = devkit.getCachedSimulationIndex(cacheDir, cacheName, simulationName)
simulationData = devkit.getFinalSimulationData(cacheIndex)
if simulationData is None:
print("Could not get simulation data!")
else:
frameData = devkit.getFinalFrameData(cacheIndex, currentFrame)
if frameData is None:
print("Could not get frame data for frame " + str(currentFrame) + "!")
else:
entityCount = simulationData._entityCount
print("Found " + str(entityCount) + " entities:")
entityIds = devkit.int64Array_frompointer(simulationData._entityIds)
# get bone position array
bonePos = devkit.floatArray_frompointer(devkit.getBonePositions(frameData))
for iEntity in range(0, entityCount):
entityId = entityIds[iEntity]
# get the bone offset for this entity
boneOffset = devkit.getEntityBoneOffsetIndex(iEntity, simulationData)
bonePosStr = str(bonePos[boneOffset * 3]) + " " + str(bonePos[boneOffset * 3 + 1]) + " " + str(bonePos[boneOffset * 3 + 2])
print(str(entityId) + " pos [" + bonePosStr + "]")
devkit.finishSimulationCacheFactory()
devkit.finishGolaem()
devkit.finishGolaemProduct()
displayCacheInfo()
Using the API to fetch Character File data
Function | Description |
getBoneNames(characterFile) | Returns the bone names or a Character File |
getSortedBones(characterFile) | Returns the sorted bone ids of a Character File |
getParentBones(characterFile) | Returns the parent bone ids of a Character File (non sorted) |
getShadingGroupNames(characterFile) | Returns the Shading Group names of a Character File |
getShaderAssetNames(characterFile) | Returns the Shader Asset names or a Character File |
getRenderingTypeNames(characterFile) | Returns the Rendering Type names of a Character File |
Basic Usage Sample Code
characterFile = 'N:/CrowdMan.gcha'
sortedBoneIds = intArray_frompointer(getSortedBones(characterFile))
boneNames = getBoneNames(characterFile).split(';')
parentIds = intArray_frompointer(getParentBones(characterFile))
# get sorted bone names
for iBone in range(0, len(boneNames)-1):
print('Bone Name is {}'.format(boneNames[sortedBoneIds[iBone]]))
print('Parent Bone Name is {}'.format(boneNames[parentIds[sortedBoneIds[iBone]]]))
Using the API to query and edit Golaem Fur Cache files (gfc)
Load a Golaem Fur Cache File
The bone names or a Character File can be accessed the following way:
furData = createAndReadFurData(furFile)
Accessing/Modifying Fur Data
As the Python module has been generated from the Golaem C++ API, the fur file structure can be inspected in the C++ API, especially the glmFurData.h. There are also some predefined functions:
setFurWidths(furData, baseWidth, tipWidth)
Write a Golaem Fur Cache File
Once fur data has been modified, it can be written back if required :
writeFurData(furFile, furData)
Clean-up allocated memory
Note that the fur data can be kept allocated to edit attributes. Once done with reading./editing, each the fur data must be destroyed by calling the commands :
destroyFurData(furData)
Basic Usage Sample Code
from glm.devkit import *
# variables
mainFurIn = 'N:/inFurFile.gfc'
mainFurOut = 'N:/outFurFile.gfc'
# read gfc data
furData = createAndReadFurData(mainFurIn)
# check data
print('Number of curve sets: ' + str(furData._curveGroupsCount))
if furData._curveGroupsCount:
furGData = furData.getCurveGroup(0)
print('Number of curve in set 0: ' + str(furGData._curveCount))
numVerts = uint32Array_frompointer(furGData._numVertices)
print('Number of control points in curve 0: ' + str(numVerts[0]))
# query first curve
cPos = floatArray_frompointer(asFloatPointer(furGData._positions))
cWidths = floatArray_frompointer(furGData._widths)
dWidths = []
for iV in range(numVerts[0]):
dWidths.append(cWidths[iV])
print(dWidths)
# set widths
setFurWidths(furData, 1, 0.5)
# write out
writeFurData(mainFurOut, furData)
destroyFurData(furData)
Using the API to fetch License Info
Fetch License Information
The type of license can be checked the following way:
usingGolaemPLELicense()
usingGolaemFullLicense()
usingGolaemLayoutLicense()
The license information can be fetched the following way:
getGolaemLicenseString()
getGolaemVersionString()