User Tools

Site Tools


Example: Growth

Animating tree growth is about as straightforward as a typical geometry query. The only difference is that SteGetGeometry must be called per frame of animation with a new value for SSteGeometryOptions::m_fGrowthFrame at each frame and SSteGeometryOptions::m_bGrowthActive must be set to true.

The number of frames of growth is defined in the timeline window in SpeedTree Cinema.

This growth example is nearly identical to the wind example.

///////////////////////////////////////////////////////////////////////
//  SpeedTree Engine API error callback
 
static void MyEngineApiErrorCallback(const char* pError)
{
	fprintf(stderr, "    **[Error Report from SpeedTree Engine API]**: %s\n", pError);
}
 
 
///////////////////////////////////////////////////////////////////////
//  SpeedTree Engine API message callback
 
static void MyEngineApiMessageCallback(const char* pMessage)
{
	fprintf(stderr, "    [Message from SpeedTree Engine API]: %s\n", pMessage);
}
 
 
///////////////////////////////////////////////////////////////////////
//  Example_Growth
 
bool Example_Growth(const char* pModelFilename)
{
    bool bSuccess = false;
 
    // register callback message functions; when api calls fail, messages will be sent here
    SteSetErrorFunction(MyEngineApiErrorCallback);
    SteSetMessageFunction(MyEngineApiMessageCallback);
 
    // uses a C interface -- objects are created and initialized through function calls
    SSteEngine* pEngine = SteNew( );
    if (pEngine)
    {
        // this causes the model file to be loaded and parsed, but nothing is computed yet
        if (SteLoadSpeedTreeFile(pEngine, pModelFilename) == STE_SUCCESS)
        {
            bSuccess = true;
 
            // geometry export options
            SSteGeometryOptions sGeometryOptions;
            SteInitGeometryOptions(pEngine, &sGeometryOptions);
            sGeometryOptions.m_eGroupType = STE_GROUP_BY_MATERIAL;
            sGeometryOptions.m_fFramesPerSec = 30.0f;
            sGeometryOptions.m_bGrowthActive = true; // this is a key flag
 
            // set up time loop
            const int c_nNumFrames = SteGetNumGrowthFrames(pEngine);
 
            SSteGeometry* pGeometry = NULL;
            for (int nFrame = 0; nFrame < c_nNumFrames; ++nFrame)
            {
                sGeometryOptions.m_fGrowthFrame = static_cast<float>(nFrame);
 
                // SteGetGeometry() is called for each frame. Note that the tree computes internally only
                // once. After the first query/compute, the time taken by this function is almost entirely
                // from the wind computation.
                pGeometry = SteGetGeometry(pEngine, &sGeometryOptions);
                if (pGeometry)
                {
                    // each of these children will have new, wind-affected data
                    for (int i = 0; i < pGeometry->m_pGeometryRoot->m_nNumChildren; ++i)
                    {
                        const SSteGeometryBlock* pChild = pGeometry->m_pGeometryRoot->m_pChildren[i];
 
                        // process the wind-updated data
                        (void) pChild; // quiet compiler warning
                    }
                }
                else
                    bSuccess = false;
            }
 
            // using C, so no destructors
            if (pGeometry)
                SteDeleteGeometry(pGeometry);
        }
 
        // using C, so no destructors
        SteDelete(pEngine);
    }
 
    return bSuccess;
}