internal static void ApplySmoothingGroups(ProBuilderMesh mesh, IEnumerable <Face> faces, float angleThreshold, Vector3[] normals) { if (mesh == null || faces == null) { throw new System.ArgumentNullException("mesh"); } // Reset the selected faces to no smoothing group bool anySmoothed = false; foreach (Face face in faces) { if (face.smoothingGroup != smoothingGroupNone) { anySmoothed = true; } face.smoothingGroup = Smoothing.smoothingGroupNone; } // if a set of normals was not supplied, get a new set of normals // with no prior smoothing groups applied. if (normals == null) { if (anySmoothed) { mesh.mesh.normals = null; } normals = mesh.GetNormals(); } float threshold = Mathf.Abs(Mathf.Cos(Mathf.Clamp(angleThreshold, 0f, 89.999f) * Mathf.Deg2Rad)); HashSet <int> used = new HashSet <int>(mesh.facesInternal.Select(x => x.smoothingGroup)); int group = GetNextUnusedSmoothingGroup(1, used); HashSet <Face> processed = new HashSet <Face>(); List <WingedEdge> wings = WingedEdge.GetWingedEdges(mesh, faces, true); foreach (WingedEdge wing in wings) { // Already part of a group if (!processed.Add(wing.face)) { continue; } wing.face.smoothingGroup = group; if (FindSoftEdgesRecursive(normals, wing, threshold, processed)) { used.Add(group); group = GetNextUnusedSmoothingGroup(group, used); } else { wing.face.smoothingGroup = Smoothing.smoothingGroupNone; } } }
/// <summary> /// Builds a list of predecessors from a given face index to all other faces /// Uses the Djikstra pathfinding algorithm /// </summary> /// <param name="mesh">The mesh of the object</param> /// <param name="start">The index of the starting face</param> /// <returns>A list of predecessors from a face index to all other faces</returns> private static int[] Dijkstra(ProBuilderMesh mesh, int start) { HashSet <int> visited = new HashSet <int>(); HashSet <int> toVisit = new HashSet <int>(); if (s_cachedMesh != mesh || s_cachedFacesCount != mesh.faceCount) { s_cachedWings = WingedEdge.GetWingedEdges(mesh, true); s_cachedFacesIndex.Clear(); s_cachedFacesCount = mesh.faceCount; for (int i = 0; i < mesh.facesInternal.Length; i++) { s_cachedFacesIndex.Add(mesh.facesInternal[i], i); } } int wingCount = s_cachedWings.Count; float[] weights = new float[wingCount]; int[] predecessors = new int[wingCount]; for (int i = 0; i < wingCount; i++) { weights[i] = float.MaxValue; predecessors[i] = -1; } int current = start; weights[current] = 0; visited.Add(current); // Construct the paths between the start face and every other faces while (visited.Count < wingCount) { var currentWing = s_cachedWings[current]; var otherWing = currentWing; // Update the weight array for each face next to the current one do { var opposite = otherWing.opposite; if (opposite == null) { otherWing = otherWing.next; continue; } var idx = s_cachedFacesIndex[opposite.face]; var weight = GetWeight(current, idx, mesh); // Change the predecessor and weight if the new path found if shorter if (weights[current] + weight < weights[idx]) { weights[idx] = weights[current] + weight; predecessors[idx] = current; } // Add the face to the ones we can visit next, if not yet visited if (!toVisit.Contains(idx) && !visited.Contains(idx)) { toVisit.Add(idx); } otherWing = otherWing.next; } while (otherWing != currentWing); // This means there is an isolated face if (toVisit.Count == 0) { return(predecessors); } // Look for the next face to visit, choosing the one with less weight float min = float.MaxValue; foreach (var i in toVisit) { if (weights[i] < min) { min = weights[i]; current = i; } } visited.Add(current); toVisit.Remove(current); } return(predecessors); }