User Tools

Site Tools


Example: Wind

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

Note that there are several parameters in SSteGeometryOptions that apply to wind. Most are set in the example below.

This wind example is nearly identical to the animated growth example.

#include <cstdio>
#include "speedtree_engine.h"
 
///////////////////////////////////////////////////////////////////////
//  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_Wind
 
bool Example_Wind(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_bWindActive = true;
            sGeometryOptions.m_fWindPrerollInSecs = 10.0f;
            sGeometryOptions.m_fFramesPerSec = 30.0f;
            sGeometryOptions.m_fWindStrength = 0.5f; // 0.0 to 1.0 range
            sGeometryOptions.m_bUseUniquePositions = false;
            sGeometryOptions.m_bComputeVelocities = false;
 
            // set up time loop
            const float c_fExampleTime = 10.0f; // seconds
            const float c_fFrameTime = 1.0f / sGeometryOptions.m_fFramesPerSec;
 
            SSteGeometry* pGeometry = NULL;
            for (float fTime = 0.0f; fTime < c_fExampleTime; fTime +=c_fFrameTime)
            {
                sGeometryOptions.m_fWindTimeInSecs = fTime;
 
                // 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;
}