Selective Level of Detail in Triangle Meshes
and the many difficulties involved in its implementation...
by Thomas Tresansky
OVERVIEW
PURPOSE
By utilizing the vertex split operation we can simplify a mesh by removing 2 of its faces and lessening its number of vertices by 1 and its number of edges by 3. Applying these collapses repeatedly while keeping track of which edge was collapsed allows us to create a heirarchy of edge collapses which will move us from the original mesh to a much simpler mesh. Since vertex split is an invertible operation, if we know the heirarchy we can move back from the simpler mesh to the original. If the collapses are chosen with care, it is possible to selectively refine certain portions of the mesh, while keeping the others simplified by inverting only those collapses of interest. This is known as selective level of detail and was my goal in this project.
PROCESS
I attacked this problem by building two separate programs. Since changes in one nessecitated changes in the other (especially changes to file format) they were developed in a very non-linear fashion. Many times I thought the first was done, only to notice something that needed changing in the second, which required changing the first...
THE COLLAPSER
FUNCTION
The collapser program's function is to select and perform a sequence of edge collapses to create the collapse heirarchy needed to create a progressively-refinable mesh. This is intended as a preprocess. I created forest class (1) which holds this information and is the major player in this preprocessing operation. This forest is a collection of root nodes, each of which correspond to vertices in the original mesh, and may possibly point to the child node created by an edge collapse performed on an edge which joins this vertex. This forest (1) is a distinct data structure from the forest (2) used by the Morpher. They began as one and the same, but then diverged...
ISSUE 1: EDGE COLLAPSES
It took me about half the total time availible for the project just to get edge collapses working. I spent almost ALL of the past week making up for this...
ISSUE 2: CHOOSING COLLAPSES
During the progressive mesh creation process, choosing edge collapses is of critical importance. We have already discussed in class how choosing edges "that won't be missed" results in a much better simplification which more closely mirrors the original mesh. Hoppe devotes a large amount of his paper presenting the progressive mesh format to discussing the heuristics used to choose a good edge collapse.
However, even more important to the performance of selective refinement is creating an mostly balanced collapse forest. If the forest should because extremely unbalanced, little improvement to the mesh will be seen when some areas are refined, and a great deal of improvement will be seen when other areas are refined. Also, if the forest is very complex, refinement will occur all at once, or not all as. Minimizing dependencies within a level of refinement is of prime importance to delivering a progressive mesh which exhibits good selective refinement capabilities. This means making sure each collapse's neighborhood is kept distinct within each level of collapses. The neighborhood of a collapse are the two vertices of the triangles destroyed by the edge collapse which remain in the mesh.
CURRENT STATUS
The collapser I have built is 100% complete. It generates a legal progressive mesh heirarchy which maintains separate neighborhoods for each collapse in each level of collapses and also chooses collapses based on a shortest-length-edge-first heuristic. it then writes to the heirarchy to a *.pm file which the morpher can read in. Unfortunately the collapser is slow. VERY SLOW. It runs in time proportional to the number of vertices in the mesh to the 4th power. The algorithm goes like this:
get all the current vertices in the mesh (current level of vertices array)
for all combinations of 4 vertices
if an edge and 2 neighbors can be formed out of these 4 vertices
if it is a legal collapse
add to list of possible collapses
sort list of possible collapses (shortest first)
perform collapse
remove all participating vertices from current level of vertices array
repeat until can't get a list of possible collapses from current level
if can't get a list from current level, get a new level
if can't get ANY collapses out of current level, YOU'RE DONE
It is not a very good algorithm, but it works: it produces a mesh in which the neighborhoods are non-overlapping within each level (as Xia and Varshney describe in the 2nd Hoppe paper). I've tried producing progressive meshes without ensuring non-overlaping
neighborhoods for each collapse, and the results are not nearly as nice as with this method. Only problem is: running time.
It takes less than a minute to run on the cube.obj and l.obj meshes. To run on the bunny_200.obj mesh takes a couple of minutes. I began running the bunny_1k.obj mesh on Thursday, December 8th at 11:53 AM on my roomates 3.0 GHz P4 (basically after I had given up all attempts at improving my algorithm, though attempt is still visible in the code within an #ifdef USE_OLD
block). I ran some calculations, and am currently giving 3/1 odds it won't finish by Monday.
USING
Command argument -input specifies the *.obj mesh to load, and -output specifies the output file. I've been using *.pm for the progressive meshes it outputs. Once loaded, the only command of significance is the "p" key, which will begin the progressive mesh generation process, which will write the output file when it finishes.
THE MORPHER
FUNCTION
The morpher program's function is to load a progressive mesh, refine it from a base minimum (tetrahedron) representation to what we'll call the original level by preforming the current level of vertex splits (which undo the final levels of edge collapses calculated by the collapser). Then the program will allow the user to further refine/coarsen the entir mesh, and provide a zone of refinement, which, when it intersects with the mesh, will cause the intersected area to refine itself while it remains intersected, and fall back to the original level when the zone moves away.
IMPORTANT ELEMENTS
The ProgMesh class is the major class which represents the mesh. It contains a ptrs to the zone of refinement. It knows how to draw itself, refine and coarsen itself via edgeCollapses and vertexSplit operations.
The heirarchy of collapses/splits is stored in a forest (2) class which is a member of the ProgMesh class. The heirarchy keeps track of the state of the mesh in terms of a cut through a forest of simplifications. The heirarchy contains checks for whether or not a suggested edgeCollapse or vertexSplit function is possible.
THE PAINT FUNCTION WITH ZONE ON
Before drawing the mesh, we get all the vertices which are currently in the mesh from the mesh's heirarchy. We then check which of these are both a) splitable and b) inside the zone of refinement. We then continue to split all splitable vertices, get all the current vertices, test them and repeat this until we can't split any more vertices. We then do exactly the opposite for vertices not in the current node of refinement: collapse them until we reach the original level of vertices.
ISSUES
There is an error which sometimes occurs. Sometimes the mesh gets "stuck" in a state where it can't further refine itself which should not occur (1 of the current nodes should be able to split at all times). This is probably due to a logic error somewhere, but I haven't been able to track it down.
USING
Command argument -input is the *.pm mesh file to load, -minfaces X is the minimum number of faces. Use i/k to move the zone of refinement away from and towards you. Use j/l to move it left and right. Use u/m to move it up and dowm. Use z to turn the zone of refinement on/off. Use r to attempt all possible refinements from the current level of nodes, and c to attempt all possible collapses (won't work with refine zone on). Use o and p to attempt to fully refine and coarsen the mesh to its peak level or its original level (also won't work with refine zone on) Use a to attempt to reset the mesh if it gets "stuck". Sometimes a causes a crash though, due to unknown errors.
THE RESULT
CLICK ME TO BEGIN VIEWING SEQUENCE!
FUTURE WORK
or: What I Wish I Had More Time For...
GENERAL
The Future Work Section...
COLLAPSER
Make it faster. Should be able to run in time proportional to the number of edges, not vertices^4.
MORPHER
Implement less stringent refinement. Add geomorph interpolation. Nicer user interface.
REFRENCES
The paper Progressive Meshes by Hugues Hopepe which we read in class was my starting point. This paper outlines the vertex split and edge collapse transformations, and also touches briefly on selective refinement. The section on progressive mesh construction is extemely concerned with mesh optimization via a lengthly algorithm for choosing the best possible next collapse. Unfortunately, I was not able to implement this algorithm or many other of his suggestions due to time constraints (the less stringent refinement criteria on the bottom page 4 seems like it could especially help).
The paper View-Dependent Refinement of Progressive Meshes also by Hoppe was a critical refrence. My implementation attempts to follow the process described in section 2.3 on Vertex Hierarchies used by Xia and Varshney to enable real-time selective refinement.