User Tools

Site Tools


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 <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_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<const SpeedTree::Vec3*>(&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<const SpeedTree::Vec3*>(&sLeafRef.m_vPos.x);
                            }
                            printf(".");
                        }
                    }
                }
 
                // using C, so no destructors
                SteDeleteGeometry(&sGeometry);
 
                bSuccess = true;
            }
        }
 
        // using C, so no destructors
        SteDelete(pEngine);
    }
 
    return bSuccess;
}