User Tools

Site Tools


Vertex Declarations

The following example vertex declaration (HLSL/Cg syntax) is used as a reference to define a series of terms the SDK uses to define supporting data types used in the vertex data accessing functions and classes:

// example vertex declaration, HLSL/Cg syntax
struct SVertexDecl
{
    float3  vSlot0 : POSITION;    // xyz = position.xyz
    float4  vSlot1 : TEXCOORD0;   // xy = diffuse texcoords, z = amb occ, w = normal.z
};

Terms:

  • Vertex Declaration: The structure SVertexDecl taken as a whole.
  • Vertex Attribute: Each row, taken as a whole, defining a part of the vertex defines an attribute. vSlot0 and vSlot1 are each vertex attributes.
  • Vertex Property: Properties are defined by how the vertex shader interprets the vertex attributes. vSlot0 contains the float3 position property and vSlot1 contains the float2 diffuse texture coordinates, the float ambient occlusion value, and the z component of the vertex surface normal. Not to be confused with a vertex attribute, as there may be multiple properties in a single attribute, or a single property may be split across multiple attributes.
  • Vertex Semantic: As the term is defined in the HLSL and Cg languages, a semantic which of the typically-sixteen vertex attributes is being used. vSlot0 uses the semantic POSITION and vSlot1 uses TEXCOORD0. Note that the source generated by the compiler uses macros ATTR0 through ATTR15, which are defined per-platform as necessary, instead of semantics like POSITION. This helps with portability.
  • Vertex Component: Any single x/y/z/w float value within vSlot0 or vSlot1 is a vertex component.
  • Vertex Format: Format refers to how the data was stored in the vertex buffer (e.g. byte, half float, full float). Each attribute may have its own format. The format is not visible in the SVertexDecl declaration above. SpeedTree supports three basic vertex data types:
    • Full-precision floats (32-bit): Directly compatible with C/C++'s float type. The SDK uses st_float32 to identify this type, but on most platforms it is simply a C++ float. Note that the SpeedTree toolchain avoids 32-bit floats unless explicitly requested in the SRT exporter.
    • Half-precision floats (16-bit): Directly supported by most moden hardware, including the current generation of consoles, the half float yields meaningful space savings will still maintaining reasonable resolution for most values. All of the trees in the SDK's example reference applications forests are compiled with nothing higher than half float resolution for every property. There is no direct vanilla C/C++ analog, but the SDK provides st_float16, which contains a complete definition of the type. It readily typecasts back and forth with the regular 32-bit float type.
    • Bytes (8-bit): When byte values are stored in the SDK, they're mostly used to store normalized vectors like normals, tangents, and wind directions. They can also be used to store ambient occlusion values & geometry hints.

Structure SVertexDecl

The structure SVertexDecl is declared in Core.h and defined in the Core library. Each render state for a given tree defines a single SVertexDecl vertex declaration, which contains a complete vertex declaration organized in a way that makes it easy to access by attribute or by property. To explore the definition by attribute, access the SVertexDecl::m_asAttributes array as in the following example:

///////////////////////////////////////////////////////////////////////  
//  VertexDecl_ByAttribute
 
void VertexDecl_ByAttribute(void)
{
    const char* pFilename = "example.srt";
 
    CCore* pTree = Internal_LoadSrtFile(pFilename);
    if (pTree)
    {
        const SGeometry* pGeometry = pTree->GetGeometry( );
        if (pGeometry->m_nNum3dRenderStates > 0)
        {
            // grab the first render state to use for example
            const SVertexDecl& sDecl = pGeometry->m_p3dRenderStates[SHADER_PASS_LIT][0].m_sVertexDecl;
 
            for (int i = 0; i < VERTEX_ATTRIB_COUNT; ++i)
            {
                const SVertexDecl::SAttribute& sAttrib = sDecl.m_asAttributes[i];
                if (sAttrib.IsUsed( ))
                {
                    printf("%s in use:\n", SVertexDecl::AttributeName(i));
                    printf("  Num components: %d\n", sAttrib.NumUsedComponents( ));
                    printf("  Format: %s\n", SVertexDecl::FormatName(sAttrib.m_eFormat));
 
                    // print per-componenet info
                    const char* c_apCompNames[ ] = { "x", "y", "z", "w" };
                    for (int j = 0; j < sAttrib.NumUsedComponents( ); ++j)
                    {
                        printf("  [%s.%s], ", SVertexDecl::AttributeName(i), c_apCompNames[j]);
                        printf("byte offset %d, ", sAttrib.m_auiVertexOffsets[j]);
                        printf("holds [%s.%s]\n",
                            SVertexDecl::PropertyName(sAttrib.m_aeProperties[j]), 
                            c_apCompNames[sAttrib.m_aePropertyComponents[j]]);
                    }
                    printf("\n");
                }
            }
        }
 
        st_delete(pTree);
    }
    else
        Error("Failed to load [%s]: %s\n", pFilename, CCore::GetError( ));
}

Note that in the output of this example, you may see some “pad.x” entries, which is simply padding the unused parts of some attributes.

To explore the definition by property, access the SVertexDecl::m_asProperties array as in this example:

///////////////////////////////////////////////////////////////////////  
//  VertexDecl_ByProperty
 
void VertexDecl_ByProperty(void)
{
    const char* pFilename = "example.srt";
 
    CCore* pTree = Internal_LoadSrtFile(pFilename);
    if (pTree)
    {
        const SGeometry* pGeometry = pTree->GetGeometry( );
        if (pGeometry && pGeometry->m_nNum3dRenderStates > 0)
        {
            // grab the first render state to use for example
            const SVertexDecl& sDecl = pGeometry->m_p3dRenderStates[SHADER_PASS_LIT][0].m_sVertexDecl;
 
            for (int i = 0; i < VERTEX_PROPERTY_COUNT; ++i)
            {
                const SVertexDecl::SProperty& sProp = sDecl.m_asProperties[i];
                if (sProp.IsPresent( ))
                {
                    printf("[%s]\n", SVertexDecl::PropertyName(i));
                    printf("  uses %d %s%s\n", 
                        sProp.NumComponents( ),
                        SVertexDecl::FormatName(sProp.m_eFormat),
                        sProp.NumComponents( ) > 1 ? "s" : "");
 
                    const char* c_apCompNames[ ] = { "x", "y", "z", "w" };
                    for (int j = 0; j < sProp.NumComponents( ); ++j)
                    {
                        printf("  [%s.%s] stored in [%s.%s]\n",
                            SVertexDecl::PropertyName(i),
                            c_apCompNames[j],
                            SVertexDecl::AttributeName(sProp.m_aeAttribs[j]),
                            c_apCompNames[sProp.m_aeAttribComponents[j]]);
                    }
                    printf("\n");
                }
            }
        }
 
        st_delete(pTree);
    }
    else
        Error("Failed to load [%s]: %s\n", pFilename, CCore::GetError( ));
}