private boolean collapse(Vertex src) {
        Vertex dest = src.collapseTo;
        if (src.edges.isEmpty()) {
            return false;
        }
//        assert (assertValidVertex(dest));
//        assert (assertValidVertex(src));
        
//        assert (src.collapseCost != NEVER_COLLAPSE_COST);
//        assert (src.collapseCost != UNINITIALIZED_COLLAPSE_COST);
//        assert (!src.edges.isEmpty());
//        assert (!src.triangles.isEmpty());
//        assert (src.edges.contains(new Edge(dest)));

        // It may have vertexIDs and triangles from different submeshes(different vertex buffers),
        // so we need to connect them correctly based on deleted triangle's edge.
        // mCollapsedEdgeIDs will be used, when looking up the connections for replacement.
        List<CollapsedEdge> tmpCollapsedEdges = new ArrayList<>();
        for (Iterator<Triangle> it = src.triangles.iterator(); it.hasNext();) {
            Triangle triangle = it.next();
            if (triangle.hasVertex(dest)) {
                // Remove a triangle
                // Tasks:
                // 1. Add it to the collapsed edges list.
                // 2. Reduce index count for the LODs, which will not have this triangle.
                // 3. Mark as removed, so it will not be added in upcoming LOD levels.
                // 4. Remove references/pointers to this triangle.

                // 1. task
                int srcID = triangle.getVertexIndex(src);
                if (!hasSrcID(srcID, tmpCollapsedEdges)) {
                    CollapsedEdge cEdge = new CollapsedEdge();
                    cEdge.srcID = srcID;
                    cEdge.dstID = triangle.getVertexIndex(dest);
                    tmpCollapsedEdges.add(cEdge);
                }

                // 2. task
                indexCount -= 3;

                // 3. task
                triangle.isRemoved = true;
                nbCollapsedTri++;

                // 4. task
                removeTriangleFromEdges(triangle, src);
                it.remove();
                
            }
        }
//        assert (!tmpCollapsedEdges.isEmpty());
//        assert (!dest.edges.contains(new Edge(src)));
        
        
        for (Iterator<Triangle> it = src.triangles.iterator(); it.hasNext();) {
            Triangle triangle = it.next();
            if (!triangle.hasVertex(dest)) {
                // Replace a triangle
                // Tasks:
                // 1. Determine the edge which we will move along. (we need to modify single vertex only)
                // 2. Move along the selected edge.

                // 1. task
                int srcID = triangle.getVertexIndex(src);
                int id = findDstID(srcID, tmpCollapsedEdges);
                if (id == Integer.MAX_VALUE) {
                    // Not found any edge to move along.
                    // Destroy the triangle.
                    //     if (!triangle.isRemoved) {
                    triangle.isRemoved = true;
                    indexCount -= 3;
                    removeTriangleFromEdges(triangle, src);
                    it.remove();
                    nbCollapsedTri++;
                    continue;
                }
                int dstID = tmpCollapsedEdges.get(id).dstID;

                // 2. task
                replaceVertexID(triangle, srcID, dstID, dest);
                
                
                if (bestQuality) {
                    triangle.computeNormal();
                }
                
            }
        }
        
        if (bestQuality) {
            for (Edge edge : src.edges) {
                updateVertexCollapseCost(edge.destination);
            }
            updateVertexCollapseCost(dest);
            for (Edge edge : dest.edges) {
                updateVertexCollapseCost(edge.destination);
            }
            
        } else {
            // TODO: Find out why is this needed. assertOutdatedCollapseCost() fails on some
            // rare situations without this. For example goblin.mesh fails.
            //Treeset to have an ordered list with unique values
            SortedSet<Vertex> updatable = new TreeSet<>(collapseComparator);
            
            for (Edge edge : src.edges) {
                updatable.add(edge.destination);
                for (Edge edge1 : edge.destination.edges) {
                    updatable.add(edge1.destination);
                }
            }
            
            
            for (Vertex vertex : updatable) {
                updateVertexCollapseCost(vertex);
            }
            
        }
        return true;
    }
Computes the LODs and bakes them into the mesh. <br>

This method must be fed with the reduction method
{@link TriangleReductionMethod} and a list of reduction values.<br> for
each value a LOD will be generated. <p> <strong>Important note: </strong>
some meshes cannot be decimated, so the result of this method can vary
depending on the given mesh. Also, the reduction values are approximate, and
the algorithm won't always achieve the specified reduction.

@param reductionMethod the reduction method to use
@param reductionValues the reduction value to use for each LOD level.
jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java