SpeedTree
    Show / Hide Table of Contents

    Accessing 3D geometry

    The run-time model format, .stsdk, is parsed by the Runtime 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 Runtime 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]);
                    }
                }
            }
        }
    }
    
    Copyright © 2023 Unity Technologies
    • Legal
    • Privacy Policy
    • Cookies
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)
    "Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
    Generated by DocFX.