On-demand or View-Dependent Streaming

For large models, it is sometimes desirable to stream in only those parts which either the user wants to visualise or parts in the current view. This is especially the case when the model files are shared in a network environment and are accessed by various clients with bandwidth limitations.

Implementing this functionality requires the ability to access the .hsf file randomly and the file layout information. A .hsf file dictionary is a lookup table, providing the file offsets, bounding information, etc. to specified items and their variations. Before attempting to selectively stream a file, you must ensure that the file was written out with the dictionary. This is demonstrated by the following code:

// Parse the File Header (TKE_Comment) opcode first

// Parse the next opcode which is TKE_File_Info
TK_Status status = tk->ParseBuffer (stream_data, data_size, TK_Single);

if (status == TK_Complete)
{
        if((tk->GetWriteFlags() & TK_Generate_Dictionary) == TK_Generate_Dictionary)
        {
                // dictionary present, can do selective streaming
        }
}

The next task is to read the dictionary itself. To find out the location of the dictionary in the file, read the last 5 bytes of the file. The last byte of these 5 bytes is the #TKE_Termination opcode and the preceding 4 indicate the file offset of dictionary. Following source code provides an example:

// read the last 5 bytes

char data[5];
int amount_read = 0;

tk->PositionFile(-5);
tk->ReadBuffer(data, 5, &amount_read);

// minor sanity check -- last byte should be TKE_Termination
if (data[4] != TKE_Termination)
        return Error ("file does not end correctly");

// get the dictionary offset
int dictionary_offset;
memcpy (&dictionary_offset, data, sizeof (int));

Having the offset of the dictionary, reading it directly is quite easy. Position to the dictionary offset and read till the end of the file. Let us discuss how the information contained in the dictionary can be used to achieve selective streaming.

  • Reading the segment structure: Reading from the start of the file to the first #TK_Pause fetches the segment structure. The first few bytes of the #TKE_Dictionary opcode contains the information about #TK_Pause. For details, please refer to #TKE_Dictionary. The segment structure can be presented in a tree control for user to click and stream required entities.

  • Reading the simplified form: The lowest Level of Details (LODs) of all the entities are grouped together and stored between the first and second #TK_Pause opcodes in the file. Your application can read this portion of the file to initially display a simplified representation of the complete model. The exact location of the second #TK_Pause can be found out from the initial bytes of the #TKE_Dictionary opcode. The application can provide the user with an interface to select one or more of these objects and stream in the finer resolutions.

  • Streaming entities: To stream a specific entity, use the toolkit to find the offset and size of the entity. See the example below:

    // find out the offset of the entity to stream
    TK_Status status = tk->LocateEntity(shell_key, lod_level);
    assert(status == TK_Normal);
    
    // get an estimate of size of entity to stream
    int offset = 0;
    int entity_size = 0;
    
    status = tk->GetOffset(shell_key, lod_level, offset, entity_size);
    
    if (entity_size != 0)
    {
            // read the entity data and parse it
            int amount_read = 0;
            char * buffer = new char[entity_size];
            tk->ReadBuffer(buffer, entity_size, amount_read);
            tk->ParseBuffer(buffer, entity_size, TK_Single);
    }