using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using Random = UnityEngine.Random; namespace RayFire { [Serializable] public class RFCollapse { public enum RFCollapseType { ByArea = 1, BySize = 3, Random = 5 } public RFCollapseType type; public int start; public int end; public int steps; public float duration; public int var; public int seed; [NonSerialized] public bool inProgress; /// ///////////////////////////////////////////////////////// /// Constructor /// ///////////////////////////////////////////////////////// // Constructor public RFCollapse() { type = RFCollapseType.ByArea; start = 0; end = 75; steps = 10; duration = 15f; var = 0; seed = 0; } /// ///////////////////////////////////////////////////////// /// Rigid Collapse /// ///////////////////////////////////////////////////////// // Start collapse public static void StartCollapse(RayfireRigid scr) { // Not initialized if (scr.initialized == false) return; // Already running if (scr.clusterDemolition.collapse.inProgress == true) return; // Not enough shards if (scr.clusterDemolition.cluster.shards.Count <= 1) return; // Set random seed if (scr.clusterDemolition.collapse.seed == 0) scr.clusterDemolition.collapse.seed = Random.Range (1, 99); // Start collapse cor scr.StartCoroutine(scr.clusterDemolition.collapse.CollapseCor (scr)); } // Start collapse coroutine IEnumerator CollapseCor (RayfireRigid scr) { // Wait time WaitForSeconds wait = new WaitForSeconds (duration/steps); // Set state inProgress = true; // Iterate collapse float step = (end - start) / (float)steps; for (int i = 0; i < steps + 1; i++) { // Stop if (inProgress == false) break; float percentage = start + step * i; if (type == RFCollapseType.ByArea) AreaCollapse (scr, (int)percentage); else if (type == RFCollapseType.BySize) SizeCollapse (scr, (int)percentage); else if (type == RFCollapseType.Random) RandomCollapse (scr, (int)percentage); yield return wait; } // Set state inProgress = false; } /// ///////////////////////////////////////////////////////// /// Connectivity Collapse /// ///////////////////////////////////////////////////////// // Start collapse public static void StartCollapse(RayfireConnectivity scr) { // Already running if (scr.collapse.inProgress == true) return; // Not enough shards if (scr.cluster.shards.Count <= 1) return; // Set random seed if (scr.collapse.seed == 0) scr.collapse.seed = Random.Range (1, 99); // Start collapse cor scr.StartCoroutine(scr.collapse.CollapseCor (scr)); } // Stop collapse public static void StopCollapse (RayfireConnectivity scr) { scr.collapse.inProgress = false; } // Start collapse coroutine IEnumerator CollapseCor (RayfireConnectivity scr) { // Wait time WaitForSeconds wait = new WaitForSeconds (duration/steps); // Set running state inProgress = true; // Iterate collapse float step = (end - start) / (float)steps; for (int i = 0; i < steps + 1; i++) { // Stop if (inProgress == false) break; // Init collapse float percentage = start + step * i; if (type == RFCollapseType.ByArea) AreaCollapse (scr, (int)percentage); else if (type == RFCollapseType.BySize) SizeCollapse (scr, (int)percentage); else if (type == RFCollapseType.Random) RandomCollapse (scr, (int)percentage); yield return wait; } // Set state inProgress = false; } /// ///////////////////////////////////////////////////////// /// Connected Cluster Collapse /// ///////////////////////////////////////////////////////// // Collapse in percents public static void AreaCollapse (RayfireRigid scr, int areaPercentage) { areaPercentage = Mathf.Clamp (areaPercentage, 0, 100); AreaCollapse (scr, Mathf.Lerp (scr.clusterDemolition.cluster.minimumArea, scr.clusterDemolition.cluster.maximumArea, areaPercentage / 100f)); } // Collapse in percents public static void SizeCollapse (RayfireRigid scr, int sizePercentage) { sizePercentage = Mathf.Clamp (sizePercentage, 0, 100); SizeCollapse (scr, Mathf.Lerp (scr.clusterDemolition.cluster.minimumSize, scr.clusterDemolition.cluster.maximumSize, sizePercentage / 100f)); } // Break neib connection by shared are value and demolish cluster public static void AreaCollapse (RayfireRigid scr, float minAreaValue) { // Not initialized if (scr.initialized == false) return; // Value lower than last if (minAreaValue < scr.clusterDemolition.cluster.areaCollapse) return; // Set value scr.clusterDemolition.cluster.areaCollapse = minAreaValue; // Main cluster. int removed = RemNeibByArea (scr.clusterDemolition.cluster, minAreaValue, scr.clusterDemolition.collapse.var, scr.clusterDemolition.collapse.seed); if (removed > 0) CollapseCluster (scr); } // Break neib connection by size public static void SizeCollapse (RayfireRigid scr, float minSizeValue) { // Not initialized if (scr.initialized == false) return; // Value lower than last if (minSizeValue < scr.clusterDemolition.cluster.sizeCollapse) return; // Set value scr.clusterDemolition.cluster.sizeCollapse = minSizeValue; // Main cluster. int removed = RemNeibBySize (scr.clusterDemolition.cluster, minSizeValue, scr.clusterDemolition.collapse.var, scr.clusterDemolition.collapse.seed); if (removed > 0) CollapseCluster (scr); } // Break neib connection randomly public static void RandomCollapse (RayfireRigid scr, int randomValue) { // Not initialized if (scr.initialized == false) return; // Value lower than last if (randomValue < scr.clusterDemolition.cluster.randomCollapse) return; // Set value scr.clusterDemolition.cluster.randomCollapse = randomValue; // Main cluster. int removed = RemNeibRandom (scr.clusterDemolition.cluster, randomValue, scr.clusterDemolition.collapse.seed); if (removed > 0) CollapseCluster (scr); } // Init collapse after connection loss static void CollapseCluster (RayfireRigid scr) { // Collect solo shards, remove from cluster, no need to reinit List detachShards = new List(); RFCluster.DetachSoloShards (scr.clusterDemolition.cluster, detachShards); // Clear fragments in case of previous demolition if (scr.HasFragments == true) scr.fragments.Clear(); // Dynamic cluster connectivity check, all clusters are equal, pick biggest to keep as original if (scr.simulationType == SimType.Dynamic || scr.simulationType == SimType.Sleeping) { // Check left cluster shards for connectivity and collect not connected child clusters. Should be before ClusterizeDetachShards RFCluster.ConnectivityCheck (scr.clusterDemolition.cluster); // Cluster is not connected. Set biggest child cluster shards to original cluster. Cant be 1 child cluster here RFCluster.ReduceChildClusters (scr.clusterDemolition.cluster); } // Kinematic/ Inactive cluster, Connectivity check if cluster has uny shards. Main cluster keeps all not activated else if (scr.simulationType == SimType.Kinematic || scr.simulationType == SimType.Inactive) { RFCluster.ConnectivityUnyCheck (scr.clusterDemolition.cluster); } // Init final cluster ops RFDemolitionCluster.PostDemolitionCluster (scr, detachShards); } /// ///////////////////////////////////////////////////////// /// Connectivity Collapse /// ///////////////////////////////////////////////////////// // Collapse in percents public static void AreaCollapse (RayfireConnectivity connectivity, int areaPercentage) { areaPercentage = Mathf.Clamp (areaPercentage, 0, 100); AreaCollapse (connectivity, Mathf.Lerp (connectivity.cluster.minimumArea, connectivity.cluster.maximumArea, areaPercentage / 100f)); } // Collapse in percents public static void SizeCollapse (RayfireConnectivity connectivity, int sizePercentage) { sizePercentage = Mathf.Clamp (sizePercentage, 0, 100); SizeCollapse (connectivity, Mathf.Lerp (connectivity.cluster.minimumSize, connectivity.cluster.maximumSize, sizePercentage / 100f)); } // Crumbling public static void AreaCollapse (RayfireConnectivity connectivity, float areaValue) { // Value lower than last if (areaValue < connectivity.cluster.areaCollapse) return; // Set value connectivity.cluster.areaCollapse = areaValue; // Main cluster int removed = RemNeibByArea (connectivity.cluster, areaValue, connectivity.collapse.var, connectivity.collapse.seed); if (removed > 0) connectivity.CheckConnectivity(); } // Crumbling public static void SizeCollapse (RayfireConnectivity connectivity, float sizeValue) { // Value lower than last if (sizeValue < connectivity.cluster.sizeCollapse) return; // Set value connectivity.cluster.sizeCollapse = sizeValue; // Main cluster. int removed = RemNeibBySize (connectivity.cluster, sizeValue, connectivity.collapse.var, connectivity.collapse.seed); if (removed > 0) connectivity.CheckConnectivity(); } // Crumbling public static void RandomCollapse (RayfireConnectivity connectivity, int randomPercentage) { // Clamp randomPercentage = Mathf.Clamp (randomPercentage, 0, 100); // Value lower than last if (randomPercentage < connectivity.cluster.randomCollapse) return; // Set value connectivity.cluster.randomCollapse = randomPercentage; // Main cluster. int removed = RemNeibRandom(connectivity.cluster, randomPercentage, connectivity.collapse.seed); if (removed > 0) connectivity.CheckConnectivity(); } /// ///////////////////////////////////////////////////////// /// Neib removing /// ///////////////////////////////////////////////////////// // Remove neibs by area static int RemNeibByArea (RFCluster cluster, float minArea, float variation, int seed) { int removed = 0; float varMin = 1f - variation / 100f; float varMax = 1f + variation / 100f; int count = cluster.shards.Count; float finalArea; for (int s = 0; s < count; s++) { // Skip unyielding if (cluster.shards[s].uny == true) continue; // Skip if too much neibs // if (minNeib > 0 && cluster.shards[s].neibShards.Count > minNeib) continue; // Check neibs for (int n = cluster.shards[s].neibShards.Count - 1; n >= 0; n--) { // Define variation finalArea = cluster.shards[s].nArea[n]; if (variation > 0) { // Set random state same for shard Random.InitState (cluster.shards[s].id + cluster.shards[s].neibShards[n].id + seed); // Random mult finalArea *= Random.Range(varMin, varMax); } if (finalArea < minArea) { // Remove self in neib's neib list for (int i = cluster.shards[s].neibShards[n].neibShards.Count - 1; i >= 0; i--) { if (cluster.shards[s].neibShards[n].neibShards[i] == cluster.shards[s]) { cluster.shards[s].neibShards[n].RemoveNeibAt (i); break; } } // Remove in self cluster.shards[s].RemoveNeibAt (n); removed++; } } } return removed; } // Remove neibs by area static int RemNeibByAreaB (RFCluster cluster, float minArea, float variation, int seed) { int removed = 0; float varMin = 1f - variation / 100f; float varMax = 1f + variation / 100f; int count = cluster.shards.Count; for (int s = 0; s < count; s++) { // Skip unyielding if (cluster.shards[s].uny == true) continue; // Skip if too much neibs // if (minNeib > 0 && cluster.shards[s].neibShards.Count > minNeib) continue; // Check neibs for (int n = cluster.shards[s].neibShards.Count - 1; n >= 0; n--) { // Define variation float finalArea = cluster.shards[s].nArea[n]; if (variation > 0) { // Set random state same for shard Random.InitState (cluster.shards[s].id + cluster.shards[s].neibShards[n].id + seed); // Random mult finalArea *= Random.Range(varMin, varMax); } if (finalArea < minArea) { // Remove self in neib's neib list for (int i = cluster.shards[s].neibShards[n].neibShards.Count - 1; i >= 0; i--) { if (cluster.shards[s].neibShards[n].neibShards[i] == cluster.shards[s]) { cluster.shards[s].neibShards[n].RemoveNeibAt (i); break; } } // Remove in self cluster.shards[s].RemoveNeibAt (n); removed++; } } } return removed; } // Remove neibs by size static int RemNeibBySize (RFCluster cluster, float minSize, float variation, int seed) { int removed = 0; float varMin = 1f - variation / 100f; float varMax = 1f + variation / 100f; int count = cluster.shards.Count; for (int s = 0; s < count; s++) { // Skip unyielding if (cluster.shards[s].uny == true) continue; // Skip if too much neibs // if (minNeib > 0 && cluster.shards[s].neibShards.Count > minNeib) continue; // Define variation float finalSize = cluster.shards[s].sz; if (variation > 0) { // Set random state same for shard Random.InitState (cluster.shards[s].id + seed); // Random mult finalSize *= Random.Range (varMin, varMax); } // Check neibs if (finalSize < minSize) { for (int n = cluster.shards[s].neibShards.Count - 1; n >= 0; n--) { // Remove self in neib's neib list for (int i = cluster.shards[s].neibShards[n].neibShards.Count - 1; i >= 0; i--) { if (cluster.shards[s].neibShards[n].neibShards[i] == cluster.shards[s]) { cluster.shards[s].neibShards[n].RemoveNeibAt (i); break; } } } // Remove in self cluster.shards[s].ClearNeib(); removed++; } } return removed; } // Remove neibs by area static int RemNeibRandom (RFCluster cluster, int percent, int seed) { int removed = 0; int count = cluster.shards.Count; for (int s = 0; s < count; s++) { // Skip unyielding if (cluster.shards[s].uny == true) continue; // Skip if too much neibs // if (minNeib > 0 && cluster.shards[s].neibShards.Count > minNeib) continue; // Check neibs for (int n = cluster.shards[s].neibShards.Count - 1; n >= 0; n--) { // Set random state for same pairs Random.InitState (cluster.shards[s].id + cluster.shards[s].neibShards[n].id + seed); // Random percentage check if (Random.Range (0, 100) < percent) { // Remove self in neib's neib list for (int i = cluster.shards[s].neibShards[n].neibShards.Count - 1; i >= 0; i--) { if (cluster.shards[s].neibShards[n].neibShards[i] == cluster.shards[s]) { cluster.shards[s].neibShards[n].RemoveNeibAt (i); break; } } // Remove in self cluster.shards[s].RemoveNeibAt (n); removed++; } } } return removed; } // Remove connection in cluster in s shard and for its n neib static void RemoveConnection(RFCluster cluster, int s, int n) { // Remove self in neib's neib list for (int i = cluster.shards[s].neibShards[n].neibShards.Count - 1; i >= 0; i--) { if (cluster.shards[s].neibShards[n].neibShards[i] == cluster.shards[s]) { cluster.shards[s].neibShards[n].RemoveNeibAt (i); break; } } // Remove in self cluster.shards[s].RemoveNeibAt (n); } /// ///////////////////////////////////////////////////////// /// Range Data /// ///////////////////////////////////////////////////////// // Set range for area and size public static void SetRangeData (RFCluster cluster, int perc = 0) { if (cluster.shards.Count == 0) return; // Start values cluster.maximumSize = cluster.shards[0].sz; cluster.minimumSize = cluster.shards[0].sz; cluster.maximumArea = 0f; cluster.minimumArea = 10000f; cluster.randomCollapse = perc; // Loop shards for (int i = 0; i < cluster.shards.Count; i++) { if (cluster.shards[i].sz > cluster.maximumSize) cluster.maximumSize = cluster.shards[i].sz; if (cluster.shards[i].sz < cluster.minimumSize) cluster.minimumSize = cluster.shards[i].sz; for (int j = 0; j < cluster.shards[i].nArea.Count; j++) { if (cluster.shards[i].nArea[j] > cluster.maximumArea) cluster.maximumArea = cluster.shards[i].nArea[j]; if (cluster.shards[i].nArea[j] < cluster.minimumArea) cluster.minimumArea = cluster.shards[i].nArea[j]; } } // Fix if (cluster.minimumArea < 0.001f) cluster.minimumArea = 0f; cluster.areaCollapse = cluster.minimumArea; cluster.sizeCollapse = cluster.minimumSize; } // Copy Range data for runtime clusters public static void CopyRangeData(RFCluster cluster, RFCluster source) { cluster.maximumSize = source.maximumSize; cluster.minimumSize = source.minimumSize; cluster.maximumArea = source.maximumArea; cluster.minimumArea = source.minimumArea; cluster.randomCollapse = source.randomCollapse; cluster.areaCollapse = cluster.minimumArea; cluster.sizeCollapse = cluster.minimumSize; } } }