Merging meshes in the absence of smoothing groups blurs the meshes' normals that, when recalculated for the new shapes that you thus create, tend to acquire orientations which weren't present in the distinct originals.
You may continue to ignore this fact or again prefer to minimize its impact on the model by adding extra millions of polies to the original model by subdivision as you do now. But you can't eliminate all the artifacts completely; some normal blur will still reside somewhere in the model no matter what you do for as long as you disregard smoothing groups.
OTOH preserving the numerous original meshes may over-stress the renderer that has to set up and reset continuously the pipeline for many more mesh-specific materials (textures, lighting and shaders) than is actually necessary. The mesh-specific materials may in fact appear to be one and the same base material (the one you're using to merge the meshes with). In this way, hundreds if not thousands of extra (and basically unnecessary) state changes (i.e. glXXXX function calls) are made in every frame rendered.
This can be corrected by what is called "material batching" as described below.
1. On loading, the meshes are sorted out in such a way that those of them which have the same base material are grouped to come one after another in the thus sorted mesh list.
2. Render procs are re-written in such a way that everything that comes after the glDrawElements() call (that's effectively the OpenGL state machine reset sequence of glXXXX function calls) appears at the very beginning of respective render procs -- before the state machine set sequence (textures/lighting/shaders).
3. If a material change is detected at the top of the mesh rendering loop iteration, then the state machine is first reset and then set to match the new material as it does now.
4. If no change in the current material is detected, then the state machine reset/set function calls are bypassed as necessary, and the glDrawElements() call occurs almost immediately thus sparing us a few dozens extra function calls in each iteration.
Do you think you will be able to implement material batching in ObjReader without my assistance following the above strategy, Patrice? This optimization could have a very good impact on our renderers.
(And yes, I would like to have the unmerged version too, please.)