====== Example: Growth ====== Animating tree growth is about as straightforward as a [[example_typical|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 [[http://docs8.speedtree.com/modeler/doku.php?id=kcgrowth#using_the_timeline|timeline window]] in SpeedTree Cinema. This growth example is nearly identical to the [[example_wind|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(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; }