Let's start from the problem: it is common to have a mesh built with different types of primitives, but gl*Draw* functions let you draw only one type of primitives per call. So you are either forced to convert all the geometry to one type of primitive (in most cases, GL_TRIANGLE_STRIP is the most optimal solution) or call gl*Draw* multiple times sorting the primitives by their type. Well, primitive restarting indices helped to separate primitives "on the fly" without bothering to call gl*Draw* for each individual primitive or inserting multiple index clones, but still the optimization on the mesh have to be done to combine individual triangles into primitives of the same type. Howether, merging triangles of abstractly-shaped geometry all into strips only, with blind machine algorithms (even advanced ones), results in a considerable quantity of short strips. Some of that "garbage" may even better fit into GL_TRIANGLE_FAN rather than GL_TRIANGLE_STRIP, or even rendered as individual triangles. But as we forced to use strips for all primitives, we still have to use separation indices for each of those small chunks.
So here is my solution for that mess: let's extend the functionality of the primitive restart index(es) to let it(them) change the primitive mode. In order to do that we need more than one value of index to have a special meaning rather then referencing the corresponding set of vertex attribute. I think, the most elegant way is to state that once the extended functionality of Primitive Restart Index (shortly, PRI) is enabled, then any index, which value is equal or higher than the value specified by glPrimitiveRestartIndex will have the special meaning. If the index is equal to the specified PRI, then the primitive type stays the same (just restarted); if the index is higher than PRI, then the type of the primitive changes, so all subsequent indices will be used to construct the primitive of the new type.
So what are the rules of converting the special index value into the desired primitive type? Let's have a look on the defined values of all currently known primitives (let's keep legacy types in that table just for consistency purposes):
GL_POINTS 0x00
GL_LINES 0x01
GL_LINE_LOOP 0x02
GL_LINE_STRIP 0x03
GL_TRIANGLES 0x04
GL_TRIANGLE_STRIP 0x05
GL_TRIANGLE_FAN 0x06
GL_QUADS 0x07
GL_QUAD_STRIP 0x08
GL_POLYGON 0x09
GL_LINES_ADJACENCY 0x0A
GL_LINE_STRIP_ADJACENCY 0x0B
GL_TRIANGLES_ADJACENCY 0x0C
GL_TRIANGLE_STRIP_ADJACENCY 0x0D
GL_PATCHES 0x0E
So far there are 15 values only, defined as consecutive numbers. The straightforward approach is to think of the index just above the PRI value as a base offset for those numbers. So once the index encountered which is equal to PRI, the primitive will be just restarted, but it's type left unchanged; if the index value is equal to (PRI+1), then the primitive type will switch to GL_POINTS; if index is (PRI+2), then the new primitive type will became a GL_LINES and so on.
The way I described this may be raw and messy, I know, but I hope the idea will find the support.
The extended primitive restart functionality will let the meshes to be constructed with primitives of different types and greatly simplify their drawing and storing in files. The quntity of gl*DrawElements* calls required to draw a single mesh will be reduced to 1 single call as all required information may be stored right in the GL_ELEMENT_ARRAY_BUFFER making the "mode" parameter of a set of gl*DrawElements* functions obsolete.