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. Call SteGetGeometry to force a compute and return an SSteGeometry object.
  6. Pull needed data from SSteGeometry object.
  7. 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;
 
            // this causes the api to compute the geometry from the model file definition already loaded
            SSteGeometry* pGeometry = SteGetGeometry(pEngine, &sGeometryOptions);
            if (pGeometry)
            {
                printf("\n[%s] has %s polys, %s vertices, and %s total indices.\n\n",
                       SpeedTree::FilenameNoPath(pModelFilename).c_str( ),
                       SpeedTree::FormatWithCommas(pGeometry->m_nNumPolygons).c_str( ),
                       SpeedTree::FormatWithCommas(pGeometry->m_nNumVertices).c_str( ),
                       SpeedTree::FormatWithCommas(pGeometry->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 < pGeometry->m_pGeometryRoot->m_nNumChildren; ++i)
                {
                    const SSteGeometryBlock* pChild = pGeometry->m_pGeometryRoot->m_pChildren[i];
 
                    // should always be one mesh when using STE_GROUP_BY_MATERIAL
                    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, pGeometry->m_pGeometryRoot->m_nNumChildren);
                    printf("              # meshes: %d\n", pChild->m_nNumMeshes);
                    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 j = 0; j < 3 && j < pChild->m_nNumVertices; ++j)
                    {
                        int nIndex = pMesh->m_pIndices[j];
                        const SSteVec3& vPos = pChild->m_pVertices[nIndex].m_vPos;
                        printf("                 pos %d: (%g, %g, %g)\n", j, vPos.x, vPos.y, vPos.z);
                    }
 
                    // material data
                    const SSteMaterial* pMeshMaterial = pGeometry->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(pGeometry);
 
                bSuccess = true;
            }
        }
 
        // using C, so no destructors
        SteDelete(pEngine);
    }
 
    return bSuccess;
}