David Williams wrote:
In your work with a TVA extractor, have you found that this representation is useful? Did you implement your algorithm as a class (like the existing SurfaceExtractor) or just as a free function which takes a volume as a parameter? I don't really remember why it was done that way and there may not have been a good reason. However, replacing the algorithm classes with free functions is not something I want to do at the moment, just because it is a large and breaking change. It will be strongly considered in the future though.
The tva surface_extractor is implemented as a class, *and* the execute is templatized. The class takes a "traits" template which defines generally generic types and class-static functions which define some types (density_type for example), and policies (for example, I managed to abstract material selection, interpolation, and normalization to the traits). Then execute() is static, and takes some more template paramaters such as "VertexOutputIterator" vertices, "IndexOutputIterator" indices, "deck_range_t" deck_range. So, instead of dumping vertices into a SurfaceMesh, I decided to be more generic and use the OutputIterator concept from stl. The reason I did this was because boost provides
make_function_output_iterator() which can take a function object and turn it into an output-iterator concept. This allows the user to specify *exactly* what they want to do with the vertices; they can just count it, they can insert it into a surface mesh, or use the stl-provided std::back_inserter to push_back() into any object, or they can make their own function-object that does any combination of these or anything else, or even write their own structure that matches the OutputIterator concept directly.
The "deck_range_t" is the variable that my extractor uses to obtain voxel/cell data, and is a special concept I define and provide an implementation for polyvox volumes for (it is not a function object and another subject).
Internally, I have useful functions such as extract_cell_triangles() which takes a "cell" and a function-object called "for_each_triangle". For every triangle it finds in the cell, it will call the callback for_each_triangle. This is not exposed to the user for now though.
In general the reason there is a "traits" and template functions is that I put types and functionality into traits that shouldn't be changing from one extraction to the next, while execute() takes very specific one-time-use types like output iterators.
The genericity allows me to make a PolyVox specialization that exactly matches the PolyVox API where applicable (for the SurfaceExtractor for example). This means that I can probably make a competing version of the Marching Cubes SurfaceExtractor (leaving aside the transition_extractor entirely), and in fact, in an old refactor I had this. Currently, in the demo, the transition extraction and scene management (octree etc.) is complex enough that I think it best just to force/leave/expose the output-iterator interface.