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.

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

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 Cache data

The cache data can be accessed and modified the following way:

getBonePositions(frameData)
getBoneOrientations(frameData)
getSnsValues(frameData)
getGeoBehaviorAnimFrameInfo(frameValue)
getPPFloatAttributeData(frameData)
getPPVectorAttributeData(frameData)

Those accessors return float array pointers which can be cast in a Python array the following way:

bonePos = []
bonePos.append(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._entityCount
for iEntity in range(0, entityCount):
    # get the bone count for this entity
    boneCount = 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')
 
# 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

Get a Character File Bone Names and Sorted List

The bone names or a Character File can be accessed the following way:

getBoneNames(characterFile)

The sorted bone ids of a Character File can be accessed the following way:

getSortedBones(characterFile)

The parent bone ids or a Character File can be accessed the following way (non sorted):

getParentBones(characterFile)

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