meta data for this page
  •  

Accessing 3D geometry

The run-time model format, .stsdk, is parsed by the SDK and the model content is made available via several functions in the CCore class, defined in Core/Core.h. The listing below details how an .stsdk file is loaded and its data extracted.

This example uses SpeedTree::CCore::SLoadConfig to set up a file load but could also use a memory block preloaded by the application as illustrated by the complex example on the Loading .stsdk Files page.

Billboard geometry will be stored in the last level of detail (LOD), if present. Use CCore::HasBillboard() to query if billboard geometry is present.

#include "SpeedTree/Core/Core.h"
using SpeedTree::st_uint32;
using SpeedTree::st_float16;
using SpeedTree::st_uint8;
using SpeedTree::st_uint16;
 
void MyErrorCallback(const char* pMsg) { fprintf(stderr, "SpeedTree SDK Error: [%s]\n", pMsg); }
 
void ShowGeometryAccess(const char* pFilename)
{
    // set up error callback
    SpeedTree::Callbacks::Error() = MyErrorCallback;; // set up error callback
 
    // parameters needed to load
    SpeedTree::CCore::SLoadConfig sLoadConfig;
    sLoadConfig.m_strFilename = pFilename;
    sLoadConfig.m_bGrassModel = false;
 
    // create a model to hold and read the stsdk file
    SpeedTree::CCore cModel;
    if (cModel.LoadTree(sLoadConfig))
    {
        printf("# LODs: %d (%d are 3D)\n", cModel.LodData().Count(), cModel.Num3dLods());
        printf("Billboard present: %s", cModel.HasBillboard() ? "yes" : "no");
        if (cModel.HasBillboard())
        {
            printf(" (%d side billboards", cModel.BillboardInfo().SideViewCount());
            if (cModel.BillboardInfo().IncludesTopDown())
                printf(" + one top-down)");
            else
                printf(")");
        }
        printf("\n");
 
        // run through LODs, printing info about each; highest is first
        for (st_uint32 i = 0; i < cModel.LodData().Count(); ++i)
        {
            // each LOD has a set of vertices shared by one or more draw calls; CLodData and CLodInfo are 
            // separated so that we can free the bulk of the geometry data, housed in CLodData, after passing
            // it to the GPU and keep CLodInfo for the render loop if needed
            const SpeedTree::CLodData cLodData = cModel.LodData()[i];
            const SpeedTree::CLodInfo cLodInfo = cModel.LodInfo()[i];
            printf("\nLOD %d -----\n", i);
 
            // print vertex info -- each LOD has one or more vertex streams, but we're just
            // looking at the first one
            if (cLodData.VertexStreams().Count() > 0)
            {
                printf("  # vertices: %d\n", cLodData.VertexStreams()[0].Data().Count());
 
                // print first vertex for example
                if (cLodData.VertexStreams()[0].Data().Count() > 0)
                {
                    // must match the Lua script used to export this model; this structure
                    // matches the standard tree vertex packer that ships with Modeler 9.1.1
                    struct SMyVertexFormat
                    {
                        st_float16 m_vPos[3], m_fTexCoordU;
                        st_float16 m_vLodPos[3], m_fTexCoordV;
                        // the next trhee attrs are heavily packed, see shader examples for unpacking code
                        st_uint8   m_uiNormal, m_uiBinormal, m_uiTangent, m_uiAO;
                        st_uint8   m_uiWindBranch1Weight, m_uiWindBranch1Dir, m_uiWindBranch1Offset, m_uiWindRipple;
                        st_uint8   m_uiWindBranch2Weight, m_uiWindBranch2Dir, m_uiWindBranch2Offset, m_uiBlendTwoSided;
                    };
 
                    // get raw VB data, reinterpret as our struct
                    const SMyVertexFormat* pVB =
                        reinterpret_cast<const SMyVertexFormat*>(cLodData.VertexStreams( )[0].Data( )[0]);
 
                    // position of first 3 vertices
                    for (int j = 0; j < 3; ++j)
                        printf("  vert[%d].pos = (%g, %g, %g)\n", j,
                            static_cast<float>(pVB[j].m_vPos[0]), static_cast<float>(pVB[j].m_vPos[1]), static_cast<float>(pVB[j].m_vPos[2]));
                }
            }
 
            // print info about the draw calls
            printf("  # draw calls: %d\n", cLodInfo.DrawCalls().Count());
            for (st_uint32 j = 0; j < cLodInfo.DrawCalls().Count(); ++j)
            {
                const SpeedTree::SDrawCall sDrawCall = cLodInfo.DrawCalls()[j];
                const st_uint32 uiIndexSize = cLodData.Indices().ElementSize();
 
                printf("    # triangles: %d\n", sDrawCall.m_uiIndexCount / 3);
                printf("     index size: %d bytes\n", uiIndexSize);
 
                // print the first triangle's indices (if 2 byte indices)
                if (uiIndexSize == 2)
                {
                    const st_uint16* pIndices = reinterpret_cast<const st_uint16*>(cLodData.Indices()[0]);
                    printf("    triangle[0].indices = { %d, %d, %d }\n", pIndices[0], pIndices[1], pIndices[2]);
                }
            }
        }
    }
}