Using HAIR files
A HAIR file is a binary file that keeps hair model data (see HAIR file description for more information). This solution presents a way of using cyHairFile code release to easily load HAIR files and display hair models using OpenGL.
Loading HAIR files
The actual loading operation is handled using cyHairFile
class
by calling LoadFromFile
method.
The following function loads the given HAIR file into the given
cyHairFile
object,
and calls FillDirectionArray
method of
cyHairFile
to precompute hair directions and store them in the given directions array.
These directions will be used for shading when drawing the hair model.
If an error occurs, it prints proper error messages.
void LoadHairModel( const char *filename, cyHairFile &hairfile, float *&dirs ) { // Load the hair model int result = hairfile.LoadFromFile( filename ); // Check for errors switch( result ) { case CY_HAIR_FILE_ERROR_CANT_OPEN_FILE: printf("Error: Cannot open hair file!\n"); return; case CY_HAIR_FILE_ERROR_CANT_READ_HEADER: printf("Error: Cannot read hair file header!\n"); return; case CY_HAIR_FILE_ERROR_WRONG_SIGNATURE: printf("Error: File has wrong signature!\n"); return; case CY_HAIR_FILE_ERROR_READING_SEGMENTS: printf("Error: Cannot read hair segments!\n"); return; case CY_HAIR_FILE_ERROR_READING_POINTS: printf("Error: Cannot read hair points!\n"); return; case CY_HAIR_FILE_ERROR_READING_COLORS: printf("Error: Cannot read hair colors!\n"); return; case CY_HAIR_FILE_ERROR_READING_THICKNESS: printf("Error: Cannot read hair thickness!\n"); return; case CY_HAIR_FILE_ERROR_READING_TRANSPARENCY: printf("Error: Cannot read hair transparency!\n"); return; default: printf("Hair file \"%s\" loaded.\n", filename); } int hairCount = hairfile.GetHeader().hair_count; int pointCount = hairfile.GetHeader().point_count; printf("Number of hair strands = %d\n", hairCount ); printf("Number of hair points = %d\n", pointCount ); // Compute directions if( hairfile.FillDirectionArray( dirs ) == 0 ) { printf("Error: Cannot compute hair directions!\n"); } }
Drawing hair models
Now that we have loaded the hair model, we are ready to draw it as line segments.
The following function first initializes OpenGL arrays using the given
cyHairFile
object
and precomputed directions,
then iterates over all hair strands and draws them using
glDrawArrays
function.
void DrawHairModel( const cyHairFile &hairfile, float *dirs ) { // Set point array glVertexPointer( 3, GL_FLOAT, 0, hairfile.GetPointsArray() ); glEnableClientState( GL_VERTEX_ARRAY ); // Set normal array glNormalPointer( GL_FLOAT, 0, dirs ); glEnableClientState( GL_NORMAL_ARRAY ); // Set color array (if exists) float *colors = hairfile.GetColorsArray(); if ( colors ) { glColorPointer( 3, GL_FLOAT, 0, colors ); glEnableClientState( GL_COLOR_ARRAY ); } // Draw arrays int pointIndex = 0; int hairCount = hairfile.GetHeader().hair_count; const unsigned short *segments = hairfile.GetSegmentsArray(); if ( segments ) { // If segments array exists for ( int hairIndex=0; hairIndex < hairCount; hairIndex++ ) { glDrawArrays( GL_LINE_STRIP, pointIndex, segments[ hairIndex ]+1 ); pointIndex += segments[ hairIndex ]+1; } } else { // If segments array does not exist, use default segment count int dsegs = hairfile.GetHeader().d_segments; for ( int hairIndex=0; hairIndex < hairCount; hairIndex++ ) { glDrawArrays( GL_LINE_STRIP, pointIndex, dsegs+1 ); pointIndex += dsegs+1; } } // Disable arrays glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_NORMAL_ARRAY ); glDisableClientState( GL_COLOR_ARRAY ); }