====== 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#More Complex Example|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(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(pVB[j].m_vPos[0]), static_cast(pVB[j].m_vPos[1]), static_cast(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(cLodData.Indices()[0]); printf(" triangle[0].indices = { %d, %d, %d }\n", pIndices[0], pIndices[1], pIndices[2]); } } } } }