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]); } } } } }