// Quadric Edge Collapse Decimation // threshold: is the maximum length edge that will be contracted // target_face_count: is interpreted based on the assumption that every // collapsed edge removes two faces. // safer_mode: if true then at most one edge associated with each vertex will be // collapsed, this seems to reduce artifacts for equivalent performance, though // less can be achieved per invokation. func QECD(m *mesh.Mesh, threshold float64, target_face_count int, safer_mode bool) { vertices := make([]*vertex, 0) faces := make([]*face, 0) edges := &edgeHeap{} // build up vertices m.Vertices.Each(func(x, y, z float64) { vertices = append(vertices, &vertex{ Coords: [3]float64{x, y, z}, Faces: make([]*face, 0), Q: &Quadric{}, Edges: make([]*edge, 0), }) }) // Build up faces and update verts // iterate through faces and collect non-border edges // by counting the occurances of every edge, and keeping those with a count of 2 edge_occurances := make(map[[2]int][]*face) m.Faces.Each(func(a, b, c int) { new_face := &face{ Vertices: [3]*vertex{vertices[a], vertices[b], vertices[c]}, } faces = append(faces, new_face) new_face.calculateKp() vertices[a].Faces = append(vertices[a].Faces, new_face) vertices[a].Q.Add(new_face.Kp) vertices[b].Faces = append(vertices[b].Faces, new_face) vertices[b].Q.Add(new_face.Kp) vertices[c].Faces = append(vertices[c].Faces, new_face) vertices[c].Q.Add(new_face.Kp) var edge_description [2]int if a < b { edge_description = [2]int{a, b} } else { edge_description = [2]int{b, a} } edge_occurances[edge_description] = append( edge_occurances[edge_description], new_face, ) if a < c { edge_description = [2]int{a, c} } else { edge_description = [2]int{c, a} } edge_occurances[edge_description] = append( edge_occurances[edge_description], new_face, ) if c < b { edge_description = [2]int{c, b} } else { edge_description = [2]int{b, c} } edge_occurances[edge_description] = append( edge_occurances[edge_description], new_face, ) }) // First iterate through edge_occurances to identify border vertices boundary_vertices := make(map[int]bool) for e, occurances := range edge_occurances { if len(occurances) == 1 { boundary_vertices[e[0]] = true boundary_vertices[e[1]] = true } } // Iterate through edge_occurances again and build up edges for e, occurances := range edge_occurances { // Skip if edge doesn't occur in two faces (boundary or non-manifold) if len(occurances) == 2 { // Skip edge if one of the vertices is on a mesh boundary _, is_boundary1 := boundary_vertices[e[0]] _, is_boundary2 := boundary_vertices[e[1]] if !is_boundary1 && !is_boundary2 { new_edge := &edge{ V1: vertices[e[0]], V2: vertices[e[1]], Faces: occurances, Removed: false, } for _, occurance := range occurances { occurance.Edges = append(occurance.Edges, new_edge) } new_edge.calculateError() vertices[e[0]].Edges = append(vertices[e[0]].Edges, new_edge) vertices[e[1]].Edges = append(vertices[e[1]].Edges, new_edge) edges.Push(new_edge) } } } // Sort edges by error heap.Init(edges) // Iteratively Collapse the lowest error edges, resorting after each collapse // just arbitrarily half the original number of edges for starters edges_collapse_target := (len(faces) - target_face_count) / 2 for len(*edges) > 0 && edges_collapse_target > 0 { lowest_cost_edge := heap.Pop(edges).(*edge) did_collapse := lowest_cost_edge.collapse(threshold) if did_collapse { edges_collapse_target-- } edges.UpdateEdges(lowest_cost_edge.V1.Edges) } // // Update the mesh with the changes made to vertices and faces // m.Vertices = tb.NewVertexBuffer() m.Norms = tb.NewVectorBuffer() m.Faces = tb.NewTriangleBuffer() i := 0 for _, v := range vertices { if !v.Collapsed { v.FinalIndex = i m.Vertices.Buffer = append(m.Vertices.Buffer, v.Coords[:]...) i++ } } for _, f := range faces { if !(f.Collapsed || f.Vertices[0].Collapsed || f.Vertices[1].Collapsed || f.Vertices[2].Collapsed) { a := f.Vertices[0].FinalIndex b := f.Vertices[1].FinalIndex c := f.Vertices[2].FinalIndex m.Faces.Buffer = append(m.Faces.Buffer, a, b, c) } } }