User Tools

Site Tools


Example: Typical Geometry Query

This example focuses on how geometry will be queried by most users. When STE_GROUP_BY_GEOMETRY_TYPE and STE_GROUP_BY_MATERIAL are used for SSteGeometryOptions::m_eGroupType, all of the geometry is contains in a variable number of nodes directly underneath SSteGeometry::m_pGoemetryRoot. This keeps things relatively simply. However, when STE_GROUP_BY_HIERARCHY is used, SSteGeometry::m_pGoemetryRoot is the root of a tree of variable width and depth. An example of traversing this data is here.

Most queries are set up similarly to this example. The general set up is:

  1. Make sure the callbacks are set (done once per application lifetime).
  2. Allocate a new SSteEngine object.
  3. Load the SpeedTree Model file (STE or SPM).
  4. Create, init, and set the query options using SSteGeometryOptions.
  5. Create and init the SSteGeometry object to hold the data.
  6. Call SteGetGeometry to force a compute and population of the SSteGeometry object.
  7. Pull needed data from SSteGeometry object.
  8. Clean up by deleting the SSteGeometry and SSteEngine objects.
#include <cstdio>
#include <cassert>
#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_GeneralGeometryAccess
 
bool Example_GeneralGeometryAccess(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); // functions used to init most structs
            sGeometryOptions.m_eResolution = STE_RESOLUTION_MEDIUM;
            sGeometryOptions.m_eGroupType = STE_GROUP_BY_MATERIAL;
 
            // init structure to hold generated geometry
            SSteGeometry sGeometry;
            SteInitGeometry(&sGeometry);
 
            // this causes the api to compute the geometry from the model file definition already loaded
            if (SteGetGeometry(pEngine, &sGeometryOptions, &sGeometry) == STE_SUCCESS)
            {
                printf("\n[%s] has %s polys, %s vertices, and %s total indices.\n\n",
                       SpeedTree::FilenameNoPath(pModelFilename).c_str( ),
                       SpeedTree::FormatWithCommas(sGeometry.m_nNumPolygons).c_str( ),
                       SpeedTree::FormatWithCommas(sGeometry.m_nNumVertices).c_str( ),
                       SpeedTree::FormatWithCommas(sGeometry.m_nNumIndices).c_str( ));
 
                // 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 i = 0; i < sGeometry.m_pGeometryRoot->m_nNumChildren; ++i)
                {
                    const SSteGeometryBlock* pChild = sGeometry.m_pGeometryRoot->m_pChildren[i];
 
                    // should always be one mesh when not using STE_GROUP_BY_HIERARCHY
                    assert(pChild->m_nNumMeshes == 1);
                    assert(pChild->m_pMeshes);
 
                    // mesh data
                    const SSteMesh* pMesh = pChild->m_pMeshes;
                    printf("  child %d of %d:\n", i + 1, sGeometry.m_pGeometryRoot->m_nNumChildren);
                    printf("               # faces: %d\n", pMesh->m_nNumFaces);
                    printf("             # indices: %d\n", pMesh->m_nNumIndices);
 
                    // mesh indices index pChild->m_pVertices
                    printf("            # vertices: %d (first 3 indexed follow)\n", pChild->m_nNumVertices);
                    for (int i = 0; i < 3 && i < pChild->m_nNumVertices; ++i)
                    {
                        int nIndex = pMesh->m_pIndices[i];
                        const SSteVec3& vPos = pChild->m_pVertices[nIndex].m_vPos;
                        printf("                 pos %d: (%g, %g, %g)\n", i, vPos.x, vPos.y, vPos.z);
                    }
 
                    // material data
                    const SSteMaterial* pMeshMaterial = sGeometry.m_pMaterials + pMesh->m_nMaterialIndex;
                    printf("         material name: '%s'\n", pMeshMaterial->m_szName);
                    printf("    color map filename: '%s'\n", pMeshMaterial->m_aMaps[STE_MAP_COLOR].m_szFilename);
 
                    printf("\n");
                }
 
                // using C, so no destructors
                SteDeleteGeometry(&sGeometry);
 
                bSuccess = true;
            }
        }
 
        // using C, so no destructors
        SteDelete(pEngine);
    }
 
    return bSuccess
}