User Tools

Site Tools


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 "Core/Core.h"
#include <cassert>
 
// speedtree types
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)
{
    SpeedTree::Callbacks::Error( ) = MyErrorCallback;; // set up error callback
 
    // parameters needed to load
    SpeedTree::CCore::SLoadConfig sLoadConfig;
    sLoadConfig.m_strFilename = pFilename;
 
    // create a model to hold and read the stsdk file
    SpeedTree::CCore cModel;
    if (cModel.LoadTree(sLoadConfig))
    {
        printf("# LODs: %d\n", cModel.LodData( ).Count( ));
        printf("Billboard present: %s\n", cModel.HasBillboard( ) ? "yes" : "no");
 
        // 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 the Modeler
                    struct SMyVertexFormat 
                    {
                        st_float16 m_vPos[3], m_fTexCoordU;
                        st_float16 m_vLodPos[3], m_fTexCoordV;
                        // the next two attrs are heavily packed
                        st_uint8   m_uiNormal, m_uiBinormal, m_uiTangent, m_uiWindBranchDir;
                        st_uint8   m_uiWindWeight, m_uiWindRipple, m_uiWindBranchOffset, m_uiMisc;
                    };
 
                    // get raw VB data, reinterpret as our struct
                    const SMyVertexFormat* pVB = 
                        reinterpret_cast<const SMyVertexFormat*>(cLodData.VertexStreams( )[0].Data( )[0]);
 
                    // position of first vertex
                    printf("  vert[0].pos = (%g, %g, %g)\n", 
                        static_cast<float>(pVB[0].m_vPos[0]),
                        static_cast<float>(pVB[0].m_vPos[1]),
                        static_cast<float>(pVB[0].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]);
                }
            }
        }
    }
}