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