IHair Interface 

IHair is the main Ornatrix interface representing a hair object. Both guides and hairs implement the IHair interface, so it is safe to assume that you will be able to access it from any Ornatrix scene object. IHair provides access to structure and shape of the hair and lets the user modify it.

 

Accessing IHair 

IHair interface is defined in IHair.h file provided in the Ornatrix SDK. It is accessible via Object::GetInterface( ulong id ) method found in 3dsmax SDK. The following examples demonstrate how to get a pointer to IHair in various scenarios.

  • If you have a 3dsmax INode pointer (scene node):
    IHair* hair = static_cast<IHair*>( myNode->EvalWorldState( time ).obj->GetInterface( IHairInterfaceID ) );

    Where time is the current scene time.
  • If you are inside a modifier's ModifyObject( ... ) method:
    IHair* hair = static_cast<IHair*>( os->obj->GetInterface( IHairInterfaceID ) );
 

Basic Concepts 

  1. Strand topology
    There are multiple ways to represent strands in a hair structure, some of which are more beneficial at certain times than others. Ornatrix hair supports numerous such ways. In all of them it keeps all of the strand vertices in one large array (called "vertices") similar to how a Mesh object does it.
    One way to represent strands is to assume that each one has the same vertex count. This way it is very trivial to have random access to any vertex on any strand and it takes less storage space. This is the default way of handling strands in Ornatrix and IHair::UsesGlobalPerStrandVertexCount() will return true if this is the case.
    It might also be beneficial to have different vertex count for each strand (some strands can be a lot shorter than others). In that case Ornatrix stores StrandTopology structure per each strand. Each <strong>StrandTopology* class contains the starting index of the strands' vertices and the number of points inside the strand. You can check if StrandTopology is currently used through IHair::UsesStrandTopology(), and you can access strand topologies through IHair::GetRoot( index ).
  2. Surface dependency
    Unless you have floating hairs, hair is typically generated from some type of surface (polygon mesh, patch, spline, etc.). It can be very beneficial to be able to get access to this surface and query it for additional information down the pipeline. For this purpose Ornatrix retains the information about the surface used to generate hair as well as a special per-strand structure called SurfaceDependency.
    SurfaceDependency contains the face (for meshes) index, the 'group' index of the strand, and a barycentric coordinate on the face where the strand is placed. For splines the barycentric coordinate represents strand's position along the spline. This information can be used to query things like vertex colors, UV coordinates, normals, or any other additional information you might need from the surface that generated each strand.<br /><br />To access the mesh which generated a given IHair, use IHair::DistributionMesh(). To get SurfaceDependency for a particular strand use IHair::GetSurfDep( rootIndex ).
    NOTE: Surface dependency is not stored in all cases, so please use IHair::KeepsSurfaceDependency() to check for its presense.
  3. Guide dependency
    In most cases guide hairs are used to generate the final dense hair model. Guides are manageable for the user because there are fewer of them and hair is typically interpolated between such guides (similarly to how mesh smooth works on base meshes).
    Similar to surface dependency, Ornatrix retains information about guides which generate a particular hair so that users can query them for additional information. For this there exists a per-strand GuideDependency structure. This structure contains the indices of three closest guides used to generate the strand as well as the distances to each of these three guides. In cases where fewer than three guides were used the indices will be set to -1. Distances can be used similarly to barycentric coordinates in surface dependency.
    To access the guides used to generate the hair use IHair::Guides(). This will return nullptr if no guides were used (hair was generated directly). To access guide dependency use IHair::GetGuideDep( rootIndex ). Use IHair::KeepsGuideDependency() to determine if guide dependency is present.
  4. Per-root transformations
    By default all strand vertices in Ornatrix are kept in local, strand coordinates and not in object coordinates. So, for example, if all points on a strand are aligned along the Z axis the result will be the strand sticking directly out of a surface. Therefore, you need to multiply these coordinates with a special root transformation matrix to put them into object coordinates (and vice-versa). You don't have to use this workflow if you prefer to work in object coordinates.
    To check whether per-strand transforms are used use IHair::HasRootTM(), or use IHair::HasRootTM( false ) to disable it. You can get each per-strand transformation suing IHair::GetRootTM( rootIndex ). To save you some time we implemented IHair::GetPointInObjectCoordinates(...) which will always return points in a strand in object coordinates.
 

Editing Hair Structure 

These are the most widely used functions you can use to access and modify the hair structure:

  • NumRoots, SetNumRoots - To determine the total number of strands in hair
  • NumPts( rootIndex ) - To get number of points in a particular strand
  • GetVerts( rootIndex ), GetPoint( rootIndex, pointIndex ), GetPointInObjectCoordinates(...) - to access and modify individual vertices

For example:

for( unsigned rootIndex = 0; rootIndex < hair.NumRoots(); rootIndex++ )
{
    for( unsigned pointIndex = 0; pointIndex < hair.NumPts( rootIndex ); pointIndex++ )
    {
        hair.GetPoint( rootIndex, pointIndex ) = hair.GetPoint( rootIndex, pointIndex ) + Point3( 1, 0, 0 );
    }
}

This code will move all vertices by 1 unit.