====== Example: Leaf References ====== Leaf references provide the following information for each leaf placement of a given model: * 3D leaf position * Leaf scalar * Leaf orientation The code below details how to access this data as well as how to interpret and apply the leaf orientation data. #include #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_LeafRefs bool Example_LeafRefs(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) { // geometry export options SSteGeometryOptions sGeometryOptions; SteInitGeometryOptions(pEngine, &sGeometryOptions); sGeometryOptions.m_eGroupType = STE_GROUP_BY_MATERIAL; sGeometryOptions.m_eResolution = STE_RESOLUTION_HIGH; sGeometryOptions.m_bExport3dGeometry = false; sGeometryOptions.m_bExportLeafRefs = true; // pull over the geometry SSteGeometry sGeometry; SteInitGeometry(&sGeometry); if (SteGetGeometry(pEngine, &sGeometryOptions, &sGeometry) == STE_SUCCESS) { // if sGeometryOptions.m_eGroupType was set to STE_GROUP_BY_HIERARCHY, we'd have to walk a tree // but using any other option, a simple loop will do // // each child under sGeometry.m_pGeometryRoot represents geometry that shares the same material for (int nChild = 0; nChild < sGeometry.m_pGeometryRoot->m_nNumChildren; ++nChild) { const SSteGeometryBlock* pChild = sGeometry.m_pGeometryRoot->m_pChildren[nChild]; // each child may have an arbitrary number of blocks of leaves, depending on the tree org // and value of m_eGroupType for (int nBlock = 0; nBlock < pChild->m_nNumLeafRefBlocks; ++nBlock) { const SSteLeafRefBlock* pBlock = pChild->m_pLeafRefBlocks + nBlock; // each block holds a variable number of leaf references for (int nRef = 0; nRef < pBlock->m_nNumLeafRefs; ++nRef) { const SSteLeafRef& sLeafRef = pBlock->m_pLeafRefs[nRef]; // each leaf reference contains a position, scalar and orientation; the orientation // takes some explanation // using utility math classes we provide in sample code, here's how a 3x3 orientation // matrix is constructed from the orientation data // extract axis of rotation from sLeafRef.m_vOrientation.xyz const SpeedTree::Vec3* pAxis = reinterpret_cast(&sLeafRef.m_vOrientation.x); // extract axis rotation angle (in degrees, but convert to radians) // from sLeafRef.m_vOrientation.w float fAngleInRadians = SpeedTree::DegToRad(sLeafRef.m_vOrientation.w); // create 3x3 ID matrix SpeedTree::Mat3x3 mOrientLeaf; // use rotate-around-arbitrary-axis function (takes radians) mOrientLeaf.RotateArbitrary(*pAxis, fAngleInRadians); // use simple flat leaf mesh -- positions defined here const float c_fLeafSize = 1.0f; const SpeedTree::Vec3 c_avUnitLeaf[3] = { SpeedTree::Vec3(0.0f, 0.0f, 0.0f), SpeedTree::Vec3(0.5f * c_fLeafSize, c_fLeafSize, 0.0f), SpeedTree::Vec3(-0.5f * c_fLeafSize, c_fLeafSize, 0.0f) }; // orient the 3 points in c_avUnitLeaf SpeedTree::Vec3 avOrientedPoints[3]; for (int i = 0; i < 3; ++i) { // orient leaf mesh point avOrientedPoints[i] = c_avUnitLeaf[i] * mOrientLeaf; // put leaf mesh into position avOrientedPoints[i] += *reinterpret_cast(&sLeafRef.m_vPos.x); } printf("."); } } } // using C, so no destructors SteDeleteGeometry(&sGeometry); bSuccess = true; } } // using C, so no destructors SteDelete(pEngine); } return bSuccess; }