meta data for this page
  •  

Loading .stsdk files

.stsdk files are binary files that store a single model as defined in and exported from the SpeedTree Modeler. They are designed as a game resource and can be loaded as individual files by the SDK or stored as part of larger memory blocks and loaded from there. These files are exported from the SpeedTree Modeler via File > Export to Game. They contain geometry, materials, extents, collision objects, and billboard data but they do not contain embedded texture data, only texture references.

.stsdk files can be loaded by the CCore class function CCore::LoadTree() in the SDK either by providing a filename or a pointer to an already memory-resident copy of the file. This structure, passed to CCore::LoadTree(), determines how it's loaded:

struct SLoadConfig
{
    // load either from a filename or an already-loaded buffer;
    // if filename is empty, the sdk will use m_pFileBlock
    CFixedString    m_strFilename;
 
    const st_byte*  m_pFileBlock;
    size_t          m_siFileBlockSize;
    CFixedString    m_strAssetSearchPath; // if m_strFilename is not used, put search path here
 
    // parameters
    st_bool         m_bGrassModel;
};

The code below is a simple example of loading a tree model exported as an .stsdk file. Once the file is loaded, all of its data, available from the CCore accessor functions, is immediately available.

#include "Core/Core.h"
 
// simple loading of a binary SpeedTree .stsdk file
 
void SimpleStsdkLoad(const char* pFilename)
{
    // populate loading config struct so that the sdk
    // handles the file and memory operations automatically
    SpeedTree::CCore::SLoadConfig sLoadConfig;
    sLoadConfig.m_strFilename = pFilename;
    sLoadConfig.m_bGrassModel = false;
 
    // create a core/tree object to read stsdk into
    SpeedTree::CCore cModel;
 
    // call .stsdk-loading function: returns true on success
    //
    // the sdk will use its default file/memory callback definitions
    // to access the file and create a buffer to store its contents
    if (cModel.LoadTree(sLoadConfig))
        printf(".stsdk load was successful\n");
    else
        fprintf(stderr, ".stsdk load was unsuccessful\n");
}

The next example is more complex, showing how to set a number of key callbacks for the SDK in addition to how to load an .stsdk file via a memory block instead of passing a filename to the SDK. See more on the SDK's callback system.

#include <cstdio>
#include "Core/Core.h"
#include "Core/Callbacks.h"
using SpeedTree::st_bool;
using SpeedTree::st_byte;
 
// simple error callback example
static void MyErrorCallback(const char* pMsg) { fprintf(stderr, "SpeedTree SDK Error: [%s]\n", pMsg); }
 
// simple heap allocation callback example
static void* MyHeapAllocCallback(size_t siSizeInBytes, size_t siAlignment) { return malloc(siSizeInBytes); }
 
// simple heap free callback example
static void MyHeapFreeCallback(void* pBlock) { if (pBlock) free(pBlock); }
 
// simple file size callback example
static size_t MyFileSizeCallback(const char* pFilename)
{
    size_t siSize = 0;
 
    FILE* pFile = NULL;
    if (fopen_s(&pFile, pFilename, "rb") == 0)
    {
        // go to the end of the file
        fseek(pFile, 0L, SEEK_END);
 
        // determine how large the file is
        siSize = static_cast<size_t>(ftell(pFile));
 
        fclose(pFile);
    }
 
    return siSize;
}
 
// simple load binary file callback example (returns true on success)
static bool MyLoadFileCallback(const char* pFilename, void* pBuffer)
{
    st_bool bSuccess = false;
 
    if (pFilename && pBuffer)
    {
        size_t siFileSize = MyFileSizeCallback(pFilename);
        if (siFileSize > 0)
        {
            FILE* pFile = NULL;
            if (fopen_s(&pFile, pFilename, "rb") == 0)
            {
                size_t siBytesRead = fread(pBuffer, 1, siFileSize, pFile);
                bSuccess = (siBytesRead == siFileSize);
 
                (void) fclose(pFile);
            }
        }
    }
 
    return bSuccess;
}
 
 
// more complex example of loading a SpeedTree STSDK file with 
// callback overrides
 
void MoreComplexStsdkLoad(const char* pFilename)
{
    // assign example callbacks
    SpeedTree::Callbacks::Error( ) = MyErrorCallback;
    SpeedTree::Callbacks::HeapAlloc( ) = MyHeapAllocCallback;
    SpeedTree::Callbacks::HeapFree( ) = MyHeapFreeCallback;
    SpeedTree::Callbacks::GetFileSize( ) = MyFileSizeCallback;
    SpeedTree::Callbacks::LoadBinaryFile( ) = MyLoadFileCallback;
 
    // populate loading config struct so that the loading function will use 
    // a client-side memory block instead of reading the file in the sdk
    SpeedTree::CCore::SLoadConfig sLoadConfig;
    sLoadConfig.m_strFilename = "";
    sLoadConfig.m_bGrassModel = false;
 
    // example of loading the stsdk file into memory on the client side then
    // passing to the sdk (minimal error handling for sake of brevity)
    sLoadConfig.m_siFileBlockSize = MyFileSizeCallback(pFilename);
    sLoadConfig.m_pFileBlock = MyHeapAllocCallback(sLoadConfig.m_siFileBlockSize, 16);
    MyLoadFileCallback(pFilename, sLoadConfig.m_pFileBlock);
 
    // create a model to hold and read the stsdk file
    SpeedTree::CCore cModel;
    if (cModel.LoadTree(sLoadConfig))
    {
        printf(".stsdk load was successful\n");
 
        // do something with the model
        // ...
 
        // delete buffer
        MyHeapFreeCallback(sLoadConfig.m_pFileBlock);
        sLoadConfig.m_pFileBlock = NULL;
    }
    else
        fprintf(stderr, ".stsdk load was unsuccessful\n");
}

Note: The bulk of an .stsdk file size is geometry (vertices and indices). Once the geometry has been uploaded to the GPU, that data can be deleted using CCore::DeleteGeometry(), leaving the rest of the CCore/STSDK object intact. This works only when the SDK has ownership of the .stsdk memory block (when a filename is passed to CCore::LoadTree() instead of a memory block).