OldBlueWater/BlueWater/Assets/RayFire/Scripts/Components/RayfireConnectivity.cs

1127 lines
43 KiB
C#
Raw Normal View History

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RayFire
{
[DisallowMultipleComponent]
[AddComponentMenu ("RayFire/Rayfire Connectivity")]
[HelpURL ("https://rayfirestudios.com/unity-online-help/components/unity-connectivity-component/")]
public class RayfireConnectivity : MonoBehaviour
{
public enum RFConnInitType
{
AtStart = 1,
ByMethod = 3,
ByIntegrity = 5
}
public ConnectivityType type;
public float expand;
public float minimumArea;
public float minimumSize;
public int percentage;
public int seed;
public bool clusterize = true;
public bool demolishable;
public RFConnInitType startCollapse = RFConnInitType.ByMethod;
public int collapseByIntegrity = 50;
public RFCollapse collapse;
public RFConnInitType startStress = RFConnInitType.ByMethod;
public int stressByIntegrity = 70;
public RFStress stress;
public bool showConnections = true;
public bool showNodes;
public bool showGizmo;
public bool showStress;
public bool checkConnectivity;
public List<RayfireRigid> rigidList;
public RFCluster cluster;
public int initShardAmount;
public int clsCount;
public RayfireRigidRoot rigidRootHost;
public RayfireRigid meshRootHost;
public Collider triggerCollider;
public int triggerDebris = 15;
public RFJointProperties joints;
// Non Serialized
[NonSerialized] public bool connectivityCheckNeed;
[NonSerialized] public RFBackupCluster backup;
[NonSerialized] public bool childrenChanged;
[NonSerialized] bool childrenCorState;
[NonSerialized] bool connectivityCorState;
[NonSerialized] bool corState;
// Event
public RFConnectivityEvent connectivityEvent = new RFConnectivityEvent();
/// /////////////////////////////////////////////////////////
/// Common
/// /////////////////////////////////////////////////////////
// Set hosts at init if applied in runtime
void SetRuntimeMeshRootHost()
{
if (meshRootHost == null && rigidRootHost == null)
{
meshRootHost = GetComponent<RayfireRigid>();
if (meshRootHost != null)
{
if (meshRootHost.objectType == ObjectType.MeshRoot)
meshRootHost.activation.cnt = this;
else
meshRootHost = null;
}
}
}
/// /////////////////////////////////////////////////////////
/// Enable/Disable
/// /////////////////////////////////////////////////////////
// Disable
void OnDisable()
{
corState = false;
childrenCorState = false;
connectivityCorState = false;
}
// Enable
void OnEnable()
{
// Start cors
if (gameObject.activeSelf == true && cluster != null &&
cluster.shards != null && cluster.shards.Count > 0 &&
corState == false)
{
// Init connectivity coroutines
StartCoroutine(ChildrenCor());
StartCoroutine(ConnectivityCor());
// Continue collapse
if (collapse.inProgress == true)
{
collapse.inProgress = false;
RFCollapse.StartCollapse (this);
}
// Continue stress
if (stress.enable == true)
{
if (stress.inProgress == true)
{
stress.inProgress = false;
RFStress.StartStress (this);
}
}
// All coroutines are running
corState = true;
}
}
/// /////////////////////////////////////////////////////////
/// Initialize
/// /////////////////////////////////////////////////////////
// Initialize, Only for child Rigids
public void Initialize()
{
// Set hosts at init if applied in runtime
SetRuntimeMeshRootHost();
// Check
if (WarningCheck() == false)
return;
// TODO check for uny shard/fragments. show warning
// Set rigidList if has meshRoot host
if (meshRootHost != null)
{
// Set connections by meshRoot fragments list
SetClusterForMeshRoot();
// RigidList check
if (rigidList.Count == 0)
{
// Disabled by connectivity
if (meshRootHost.activation.con == false)
Debug.Log ("RayFire Connectivity: " + name + " object has Rigid host with Mesh Root type but activation By Connectivity is disabled. Enable By Connectivity activation.", gameObject);
Debug.Log ("RayFire Connectivity: " + name + " has no objects to check for connectivity. Connectivity disabled.", gameObject);
ResetSetup();
return;
}
}
// Set shardList if has rigidRoot host
if (rigidRootHost != null)
SetClusterForRigidRoot();
// Create phys joints for connections TODO temp
if (joints.enable == true)
{
RFJointProperties.CreateJoints (cluster, joints);
RFJointProperties.SetDeformation (this);
return;
}
// Start all coroutines in playmode. Do not if editor setup
if (Application.isPlaying == true)
StartAllCoroutines();
}
// Check
bool WarningCheck()
{
// TODO temp
if (joints.enable == true)
{
return true;
}
// Do not show in editor setup. IMPORTANT prevent connections preview errors
if (Application.isPlaying == false)
return true;
// Check for not meshRoot rigid
if (meshRootHost != null)
{
// Not mesh root rigid
if (meshRootHost.objectType != ObjectType.MeshRoot)
{
Debug.Log ("RayFire Connectivity: " + name + " object has Rigid host but it's object type is not Mesh Root. Connectivity disabled.", gameObject);
return false;
}
// Disabled by connectivity
if (meshRootHost.activation.con == false)
{
Debug.Log ("RayFire Connectivity: " + name + " object has Rigid host with Mesh Root type but activation By Connectivity is disabled. ", gameObject);
return false;
}
if (meshRootHost.simulationType != SimType.Inactive && meshRootHost.simulationType != SimType.Kinematic)
{
Debug.Log ("RayFire Connectivity: " + name + " object has Rigid but simulation type is not Inactive or Kinematic. Connectivity disabled. ", gameObject);
return false;
}
}
// Check for not rigidRoot
if (rigidRootHost != null)
{
// Disabled by connectivity
if (rigidRootHost.activation.con == false)
{
Debug.Log ("RayFire Connectivity: " + name + " object has RigidRoot host but activation By Connectivity is disabled.", gameObject);
return false;
}
if (rigidRootHost.simulationType != SimType.Inactive && rigidRootHost.simulationType != SimType.Kinematic)
{
Debug.Log ("RayFire Connectivity: " + name + " object has RigidRoot but simulation type is not Inactive or Kinematic. Connectivity disabled. ", gameObject);
return false;
}
}
return true;
}
// Start all coroutines
public void StartAllCoroutines()
{
// Start cors
StartCoroutine(ChildrenCor());
StartCoroutine(ConnectivityCor());
// Init collapse
if (startCollapse == RFConnInitType.AtStart)
RFCollapse.StartCollapse(this);
// Init stress
if (stress != null && stress.enable == true)
if (startStress == RFConnInitType.AtStart)
RFStress.StartStress(this);
// All coroutines are running
corState = true;
}
/// /////////////////////////////////////////////////////////
/// Cluster Common
/// /////////////////////////////////////////////////////////
// Prepare cluster
void PrepareCluster()
{
// In case of runtime add
if (cluster == null)
cluster = new RFCluster();
// Missing shards check in case of cached shards
if (RFCluster.IntegrityCheck (cluster) == false)
{
Debug.Log ("RayFire Connectivity: " + name + " object has missing shards. Reset and Editor Setup Connectivity host again.", gameObject);
cluster = new RFCluster();
stress.initialized = false;
// TODO stress / support list shards reset
}
// Cluster props
cluster.demolishable = demolishable;
// Cluster amount
clsCount = 1;
}
// Create default cluster
RFCluster CreateCluster()
{
RFCluster cls = new RFCluster();
cls.id = RFCluster.GetUniqClusterId (cls);
cls.tm = transform;
cls.depth = 0;
cls.pos = transform.position;
cls.rot = transform.rotation;
cls.demolishable = demolishable;
cls.initialized = true;
return cls;
}
/// /////////////////////////////////////////////////////////
/// Set Cluster by Rigids
/// /////////////////////////////////////////////////////////
// Set cluster
void SetClusterForMeshRoot ()
{
// Prepare cluster. Check for integrity of cached shards.
PrepareCluster();
// Set rigids list and connect with Connectivity component
SetRigidListByFragments (meshRootHost.fragments);
// Play mode ops. Not for Editor. Shards were cached, reinit non serialized vars, clear list otherwise
if (Application.isPlaying == true)
if (InitCachedShardsByRigidList (rigidList, cluster) == true)
cluster.shards.Clear();
// Create main cluster
if (cluster.shards.Count == 0)
{
// Create default cluster
cluster = CreateCluster();
// Set shards for main cluster
RFShard.SetShardsByRigidList (cluster, rigidList, type);
// Set expand
SetExpand();
// Set shard neibs
RFShard.SetShardNeibs (cluster.shards, type, minimumArea, minimumSize, percentage, seed);
// Set range for area and size
RFCollapse.SetRangeData (cluster, percentage);
}
// Set stress
if (stress != null && stress.enable == true)
RFStress.Initialize(this);
// Set backup cluster
RFBackupCluster.BackupConnectivity (this);
// Set initial shards amount
initShardAmount = cluster.shards.Count;
}
// Set cluster
void SetClusterForRigidRoot ()
{
// Prepare cluster
PrepareCluster();
// Cached RigidRoot runtime init. Reinit non serialized vars, clear list otherwise
if (Application.isPlaying == true && cluster.cachedHost == true)
InitCachedShardsByRigidRoot (rigidRootHost, cluster);
// Create main cluster and set neibs. If Editor Setup or clean runtime init
if (cluster.cachedHost == false)
{
// Create default cluster
cluster = CreateCluster();
// Mirror shards from RigidRoot for main cluster
for (int i = 0; i < rigidRootHost.cluster.shards.Count; i++)
{
rigidRootHost.cluster.shards[i].cluster = cluster;
cluster.shards.Add(rigidRootHost.cluster.shards[i]);
}
// Set expand
SetExpand();
// Set faces data for connectivity
RFShard.SetMeshData (cluster.shards, type);
// Set shard neibs
RFShard.SetShardNeibs (cluster.shards, type, minimumArea, minimumSize, percentage, seed);
// Set range for area and size
RFCollapse.SetRangeData (cluster, percentage);
// Shard connections are cached and stored in host as well
cluster.cachedHost = true;
}
// Set stress
if (stress.enable == true)
RFStress.Initialize(this);
// Set backup cluster
RFBackupCluster.BackupConnectivity (this);
// Set initial shards amount
initShardAmount = cluster.shards.Count;
}
// Set bound expand for shards for better BBOX connectivity
void SetExpand()
{
if (expand > 0)
if (type == ConnectivityType.ByBoundingBox ||
type == ConnectivityType.ByBoundingBoxAndTriangles ||
type == ConnectivityType.ByBoundingBoxAndPolygons )
for (int i = 0; i < cluster.shards.Count; i++)
cluster.shards[i].bnd.Expand (expand);
}
/// /////////////////////////////////////////////////////////
/// Setup rigidList without host
/// /////////////////////////////////////////////////////////
// Set rigidList
void SetRigidListByFragments(List<RayfireRigid> rgList)
{
// No targets
if (rgList.Count == 0)
return;
// Create list
rigidList = new List<RayfireRigid>();
// Use input list for joints TODO temp
if (joints.enable == true)
{
for (int i = 0; i < rgList.Count; i++)
rigidList.Add (rgList[i]);
return;
}
// Get rigid with byConnectivity
for (int i = 0; i < rgList.Count; i++)
if (rgList[i].simulationType == SimType.Inactive || rgList[i].simulationType == SimType.Kinematic)
if (rgList[i].activation.con == true)
rigidList.Add (rgList[i]);
// No targets
if (rigidList.Count == 0)
return;
// Set this connectivity as main connectivity node
for (int i = 0; i < rigidList.Count; i++)
rigidList[i].activation.cnt = this;
}
/// /////////////////////////////////////////////////////////
/// Reinit shards in case of cached prefab
/// /////////////////////////////////////////////////////////
// Reinit shard's non serialized fields in case of prefab use
public static bool InitCachedShardsByRigidList (List<RayfireRigid> rigids, RFCluster cluster)
{
// Not initialized
if (cluster.initialized == true)
return false;
// No shards
if (cluster.shards.Count == 0)
return false;
// Rigid list doesn't match shards. TODO compare per shard
if (cluster.shards.Count != rigids.Count)
return true;
// Reinit
for (int i = 0; i < cluster.shards.Count; i++)
{
if (rigids[i] != null)
{
cluster.shards[i].uny = rigids[i].activation.uny;
cluster.shards[i].act = rigids[i].activation.atb;
cluster.shards[i].sm = rigids[i].simulationType;
cluster.shards[i].col = rigids[i].physics.meshCollider;
cluster.shards[i].rb = rigids[i].physics.rigidBody;
cluster.shards[i].rigid = rigids[i];
}
cluster.shards[i].cluster = cluster;
if (cluster.shards[i].neibShards == null)
cluster.shards[i].neibShards = new List<RFShard>(cluster.shards[i].nIds.Count);
else
cluster.shards[i].neibShards.Clear();
for (int n = 0; n < cluster.shards[i].nIds.Count; n++)
cluster.shards[i].neibShards.Add (cluster.shards[cluster.shards[i].nIds[n]]);
}
cluster.initialized = true;
return false;
}
// Reinit shard's non serialized fields in case of prefab use
public static void InitCachedShardsByRigidRoot (RayfireRigidRoot root, RFCluster cluster)
{
// Set shards for main cluster
cluster.shards.Clear();
for (int i = 0; i < root.cluster.shards.Count; i++)
{
root.cluster.shards[i].cluster = cluster;
cluster.shards.Add(root.cluster.shards[i]);
}
// Reinit neibShards
for (int i = 0; i < cluster.shards.Count; i++)
{
if (cluster.shards[i].neibShards == null)
cluster.shards[i].neibShards = new List<RFShard>(cluster.shards[i].nIds.Count);
else
cluster.shards[i].neibShards.Clear();
for (int n = 0; n < cluster.shards[i].nIds.Count; n++)
cluster.shards[i].neibShards.Add (cluster.shards[cluster.shards[i].nIds[n]]);
}
cluster.initialized = true;
}
/// /////////////////////////////////////////////////////////
/// Connectivity Cor
/// /////////////////////////////////////////////////////////
// Connectivity check cor
IEnumerator ConnectivityCor()
{
// Stop if running
if (connectivityCorState == true)
yield break;
// Set running state
connectivityCorState = true;
checkConnectivity = true;
while (checkConnectivity == true)
{
// Child deleted
if (childrenChanged == true)
ChildrenCheck();
// Get not connected groups
if (connectivityCheckNeed == true)
CheckConnectivity();
yield return null;
}
// Set state
connectivityCorState = false;
}
// Check for connectivity
public void CheckConnectivity()
{
// Rigid Root connectivity check.
if (rigidRootHost != null)
{
CheckConnectivityRigidRoot();
return;
}
// Check for connectivity
CheckConnectivityRigidList();
// Start collapse by integrity
if (startCollapse == RFConnInitType.ByIntegrity)
if (AmountIntegrity < collapseByIntegrity)
RFCollapse.StartCollapse(this);
// Start stress by integrity
if (startStress == RFConnInitType.ByIntegrity)
if (AmountIntegrity < stressByIntegrity)
RFStress.StartStress(this);
}
/// /////////////////////////////////////////////////////////
/// Connectivity check
/// /////////////////////////////////////////////////////////
// Check for connectivity
void CheckConnectivityRigidList()
{
// Do once
connectivityCheckNeed = false;
// Clear all activated/demolished shards
CleanUpActivatedShardsRigidList (cluster);
// No shards to check
if (cluster.shards.Count == 0)
return;
// Reinit neibs after cleaning
RFShard.ReinitNeibs (cluster.shards);
// TODO do not collect solo uny shards
// Check for solo shards and collect to activate
List<RFShard> soloShards = new List<RFShard>(); // TODO make local private field
RFCluster.DetachSoloShards (cluster, soloShards);
// Separate all not connected groups to child clusters
RFCluster.ConnectivityCheck (cluster);
// TODO all shards connected and has no uny shards, should be activated
// do not because no child clusters
// Get not connected and not unyielding child cluster
CheckUnyieldingRigidList (cluster);
// TODO ONE NEIB DETACH FOR CHILD CLUSTERS
// Activate solo shards or/and clusterize not connected groups
ActivateShards (soloShards);
// Stop checking. Everything activated
if (cluster.shards.Count == 0)
checkConnectivity = false;
}
// Check for connectivity
void CheckConnectivityRigidRoot()
{
// Do once
connectivityCheckNeed = false;
// Clear all activated/demolished shards
CleanUpActivatedShardsRigidRoot (cluster);
// No shards to check
if (cluster.shards.Count == 0)
return;
// Reinit neibs after cleaning
RFShard.ReinitNeibs (cluster.shards);
// TODO do not collect solo uny shards
// Check for solo shards and collect
List<RFShard> soloShards = new List<RFShard>();
RFCluster.DetachSoloShards (cluster, soloShards);
// Connectivity check TODO new cluster id fail
RFCluster.ConnectivityCheck (cluster);
// Get not connected and not unyielding child cluster
CheckUnyieldingRigidRoot (cluster);
// TODO ONE NEIB DETACH FOR CHILD CLUSTERS
// Activate shards or clusterize not connected groups
ActivateShards (soloShards);
// Stop checking. Everything activated
if (cluster.shards.Count == 0)
checkConnectivity = false;
}
/// /////////////////////////////////////////////////////////
/// Activation
/// /////////////////////////////////////////////////////////
// Activate solo shards or/and clusterize not connected groups
void ActivateShards(List<RFShard> soloShards)
{
// Event
RFConnectivityEvent.ConnectivityEvent (this, soloShards, cluster);
// Activate not connected shards.
if (soloShards.Count > 0)
for (int i = 0; i < soloShards.Count; i++)
RFActivation.ActivateShard (soloShards[i], rigidRootHost);
// Clusterize childClusters or activate their shards
if (cluster.HasChildClusters == true)
{
if (clusterize == true)
Clusterize();
else
for (int c = 0; c < cluster.childClusters.Count; c++)
for (int s = 0; s < cluster.childClusters[c].shards.Count; s++)
RFActivation.ActivateShard (cluster.childClusters[c].shards[s], rigidRootHost);
}
}
// Clusterize not connected groups
void Clusterize()
{
for (int i = 0; i < cluster.childClusters.Count; i++)
{
// Activatable unyielding activated with 0 shards left
if (cluster.childClusters[i].shards.Count == 0)
return;
// Set bound
cluster.childClusters[i].bound = RFCluster.GetShardsBound (cluster.childClusters[i].shards);
// Create root for left children
if (cluster.childClusters[i].tm == null)
RFCluster.CreateClusterRoot (cluster.childClusters[i], cluster.childClusters[i].shards[0].tm.position,
Quaternion.identity, gameObject.layer, gameObject.tag, gameObject.name + RFDemolitionCluster.nameApp + clsCount++);
// Add Connected cluster Rigid
cluster.childClusters[i].rigid = cluster.childClusters[i].tm.gameObject.AddComponent<RayfireRigid>();
// Mass by shards mass sum. IMPORTANT should be used because RigidBody SetDensity doesn't work
float mass = RFCluster.GetClusterMass (cluster.childClusters[i]);
// RigidRoot
if (rigidRootHost != null)
{
// Copy Rigid properties
rigidRootHost.CopyPropertiesTo (cluster.childClusters[i].rigid);
// Copy particles
RFParticles.CopyRigidRootParticles (rigidRootHost, cluster.childClusters[i].rigid);
// Copy Sound
RFSound.CopySound (rigidRootHost.sound, cluster.childClusters[i].rigid);
// Set activation layer
RFActivation.SetActivationLayer (cluster.childClusters[i].shards, rigidRootHost.activation, cluster.childClusters[i].tm);
// Per shard ops
for (int s = 0; s < cluster.childClusters[i].shards.Count; s++)
{
// Set dynamic state
cluster.childClusters[i].shards[s].sm = SimType.Dynamic;
// Should not be faded because of offset fade
cluster.childClusters[i].shards[s].fade = -1;
// Destroy rigidbody component
Destroy (cluster.childClusters[i].shards[s].rb);
}
// TODO Clean Up from shards
// TODO Events
// rigidRootHost.activationEvent.InvokeLocalEventRoot (shard, rigidRootHost);
// RFActivationEvent.InvokeGlobalEventRoot (shard, rigidRootHost);
}
// Rigid List
else if (meshRootHost != null)
{
// Copy Rigid properties
cluster.childClusters[i].shards[0].rigid.CopyPropertiesTo (cluster.childClusters[i].rigid);
// Copy particles
RFParticles.CopyRigidParticles (meshRootHost, cluster.childClusters[i].rigid);
// Copy Sound
RFSound.CopySound (meshRootHost.sound, cluster.childClusters[i].rigid);
// Set activation layer
RFActivation.SetActivationLayer (cluster.childClusters[i].shards, meshRootHost.activation, cluster.childClusters[i].tm);
// Destroy components
for (int s = 0; s < cluster.childClusters[i].shards.Count; s++)
{
// Stop all rigid coroutines
//cluster.childClusters[i].shards[s].tm.gameObject.SetActive (false);
//cluster.childClusters[i].shards[s].rigid.corState = true;
cluster.childClusters[i].shards[s].rigid.StopAllCoroutines();
// Destroy rigidbody
Destroy (cluster.childClusters[i].shards[s].rigid.physics.rigidBody);
//cluster.childClusters[i].shards[s].tm.gameObject.SetActive (true);
}
// MeshRoot activation event for clusterized shards
cluster.childClusters[i].rigid.meshRoot.activationEvent.InvokeLocalEventMeshRoot
(cluster.childClusters[i].rigid, meshRootHost);
}
// Set connectivity cluster as main cluster for child clusters to transfer later to demolished connected clusters
cluster.childClusters[i].mainCluster = cluster;
// Set Rigid properties
cluster.childClusters[i].rigid.objectType = ObjectType.ConnectedCluster;
cluster.childClusters[i].rigid.clusterDemolition.cn = showConnections;
cluster.childClusters[i].rigid.clusterDemolition.nd = showNodes;
// Copy Range data for runtime clusters
RFCollapse.CopyRangeData (cluster.childClusters[i], cluster);
// Create connected cluster
RFDemolitionCluster.CreateClusterRuntime (cluster.childClusters[i].rigid, cluster.childClusters[i]);
// Update mass
cluster.childClusters[i].rigid.physics.rigidBody.mass = mass;
// Add initial rotation if still
RFActivation.ActivationRandomRotation (cluster.childClusters[i].rigid.physics.rigidBody);
// Init particles on activation
RFParticles.CreateActivationParticlesRigid (cluster.childClusters[i].rigid);
// Parent by manager
if (RayfireMan.inst.parent != null)
cluster.childClusters[i].rigid.gameObject.transform.parent = RayfireMan.inst.parent.transform;
// Activation fade
if (cluster.childClusters[i].rigid.fading.onActivation == true)
cluster.childClusters[i].rigid.Fade();
}
// Clear child clusters
cluster.childClusters.Clear();
}
// Clear all activated/demolished shards TODO change to CleanUpActivatedShardsRigidRoot method:sm check
static void CleanUpActivatedShardsRigidList(RFCluster cluster)
{
for (int i = cluster.shards.Count - 1; i >= 0; i--)
{
if (cluster.shards[i].rigid == null ||
cluster.shards[i].rigid.activation.cnt == null ||
cluster.shards[i].rigid.limitations.demolished == true)
{
cluster.shards[i].cluster = null;
cluster.shards.RemoveAt (i);
}
}
}
// Collect solo shards, remove from cluster, reinit cluster
static void CheckUnyieldingRigidList(RFCluster cluster)
{
// Has no child clusters, but main cluster has no Uny shards. Send to child cluster
if (cluster.HasChildClusters == false && cluster.UnyieldingByShard == false)
{
// Create new cluster by shards
RFCluster.NewClusterByShards (cluster, cluster.shards);
// Clear shards
cluster.shards.Clear();
}
// Get not connected and not unyielding child cluster
if (cluster.HasChildClusters == true)
{
// Remove all unyielding child clusters
for (int c = cluster.childClusters.Count - 1; c >= 0; c--)
{
if (cluster.childClusters[c].UnyieldingByRigid == true)
{
cluster.shards.AddRange (cluster.childClusters[c].shards);
cluster.childClusters.RemoveAt (c);
}
}
// Set unyielding cluster shards back to original cluster
for (int s = 0; s < cluster.shards.Count; s++)
cluster.shards[s].cluster = cluster;
}
}
// Clear all activated/demolished shards
static void CleanUpActivatedShardsRigidRoot(RFCluster cluster)
{
for (int i = cluster.shards.Count - 1; i >= 0; i--)
{
// TODO check for deactivated/destroyed
if (cluster.shards[i].sm == SimType.Dynamic)
{
cluster.shards[i].cluster = null;
cluster.shards.RemoveAt (i);
}
}
}
// Collect solo shards, remove from cluster, reinit cluster
static void CheckUnyieldingRigidRoot(RFCluster cluster)
{
// Has no child clusters, but main cluster has no Uny shards. Send to child cluster
if (cluster.HasChildClusters == false && cluster.UnyieldingByShard == false)
{
// Create new cluster by shards
RFCluster.NewClusterByShards (cluster, cluster.shards);
// Clear shards
cluster.shards.Clear();
}
// Get not connected and not unyielding child cluster
if (cluster.HasChildClusters == true)
{
// Remove all unyielding child clusters
for (int c = cluster.childClusters.Count - 1; c >= 0; c--)
{
if (cluster.childClusters[c].UnyieldingByShard == true)
{
cluster.shards.AddRange (cluster.childClusters[c].shards);
cluster.childClusters.RemoveAt (c);
}
}
// Set unyielding cluster shards back to original cluster
for (int s = 0; s < cluster.shards.Count; s++)
cluster.shards[s].cluster = cluster;
}
}
/// /////////////////////////////////////////////////////////
/// Children change
/// /////////////////////////////////////////////////////////
// Child removed
void OnTransformChildrenChanged()
{
childrenChanged = true;
//integrityCheck = true;
}
// Connectivity check cor
IEnumerator ChildrenCor()
{
// Stop if running
if (childrenCorState == true)
yield break;
// Set running state
childrenCorState = true;
bool checkChildren = true;
while (checkChildren == true)
{
// Get not connected groups
if (childrenChanged == true)
connectivityCheckNeed = true;
yield return null;
}
// Set state
childrenCorState = false;
}
// Check for children
void ChildrenCheck()
{
for (int s = cluster.shards.Count - 1; s >= 0; s--)
{
if (cluster.shards[s].tm == null)
{
if (cluster.shards[s].neibShards.Count > 0)
{
// Remove itself in neibs
for (int n = 0; n < cluster.shards[s].neibShards.Count; n++)
{
// Check every neib in neib
for (int i = 0; i < cluster.shards[s].neibShards[n].neibShards.Count; i++)
{
if (cluster.shards[s].neibShards[n].neibShards[i] == cluster.shards[s])
{
cluster.shards[s].neibShards[n].RemoveNeibAt (i);
break;
}
}
}
}
cluster.shards.RemoveAt (s);
}
}
childrenChanged = false;
}
/// /////////////////////////////////////////////////////////
/// Reset
/// /////////////////////////////////////////////////////////
// Reset
public void ResetConnectivity()
{
childrenCorState = false;
connectivityCorState = false;
collapse.inProgress = false;
stress.inProgress = false;
clsCount = 1;
}
// Reset cluster data
public void ResetSetup()
{
rigidRootHost = null;
meshRootHost = null;
rigidList = null;
cluster = new RFCluster();
stress.strShards = new List<RFShard>();
stress.supShards = new List<RFShard>();
stress.initialized = false;
joints.DestroyJoints();
}
/// /////////////////////////////////////////////////////////
/// Fracture
/// /////////////////////////////////////////////////////////
// Clusterize group of shards by collider
public void Fracture ()
{
Fracture (triggerCollider, triggerDebris);
}
/// <summary>
/// Clusterize group of shards by collider
/// </summary>
/// <param name="col">Trigger Collider</param>
/// <param name="debris">Debris in percentage</param>
public void Fracture(Collider col, int debris)
{
// Runtime only
if (Application.isPlaying == false)
return;
// Null check
if (col == null)
return;
// Get overlapped colliders hashset
HashSet<Collider> collidersHash = GetOverlappedColliders (col);
// No colliders
if (collidersHash == null || collidersHash.Count == 0)
return;
// Create new cluster with shards
RFCluster newCluster = new RFCluster();
// Set cls props. Main cluster before ID apply to avoid id = 1 which will make is backup cluster
newCluster.mainCluster = cluster;
newCluster.demolishable = demolishable;
newCluster.id = RFCluster.GetUniqClusterId (newCluster);
// Collect shards. Set new cls as cluster to clean from main cluster
for (int i = cluster.shards.Count - 1; i >= 0; i--)
{
if (collidersHash.Contains (cluster.shards[i].col) == true)
{
if (cluster.shards[i].rigid != null)
cluster.shards[i].rigid.activation.cnt = null;
cluster.shards[i].cluster = newCluster;
newCluster.shards.Add (cluster.shards[i]);
cluster.shards.RemoveAt (i);
}
}
// Activate all overlapped shards without clustering
if (clusterize == false)
ActivateShards (newCluster.shards);
// Check new cls connectivity and separate to groups if can be clusterized
else
{
// Reinit neibs to remove neibs from original cluster
RFShard.ReinitNeibs (newCluster.shards);
// Check for solo shards and collect to activate
List<RFShard> detachShards = new List<RFShard>();
RFCluster.DetachSoloShards (newCluster, detachShards);
// Separate all not connected groups to child clusters
RFCluster.ConnectivityCheck (newCluster);
// Define main cluster child clusters for clustering
if (newCluster.HasChildClusters == true)
for (int i = 0; i < newCluster.childClusters.Count; i++)
cluster.AddChildCluster (newCluster.childClusters[i]);
else
cluster.AddChildCluster (newCluster);
// TODO test
RFDemolitionCluster.DetachEdgeShards (cluster, detachShards, debris);
// Activate shards and clusterize groups
ActivateShards (detachShards);
}
// Check for connectivity left shards
if (cluster.shards.Count > 0)
CheckConnectivity();
// TODO divide to defined amount of
}
// Get overlapped colliders hashset
HashSet<Collider> GetOverlappedColliders (Collider col)
{
// Get mask
int layerMask = 1 << gameObject.layer;
// TODO support for scaled colliders TODO ADD OTHER TYPES
// Get Colliders
Collider[] colliders = null;
if (col.GetType() == typeof(BoxCollider))
{
BoxCollider box = col as BoxCollider;
if (box != null)
colliders = Physics.OverlapBox (box.center + col.transform.position, box.size / 2f, col.transform.rotation, layerMask);
}
else if (col.GetType() == typeof(SphereCollider))
{
SphereCollider sphere = col as SphereCollider;
if (sphere != null)
colliders = Physics.OverlapSphere (sphere.center + col.transform.position, sphere.radius, layerMask);
}
else
{
Debug.Log ("RayFire Connectivity: " + name + ". Fracture Collider is not supported. Use Box or Sphere trigger collider.", gameObject);
}
if (colliders == null)
return null;
return new HashSet<Collider> (colliders);
}
/// /////////////////////////////////////////////////////////
/// Get
/// /////////////////////////////////////////////////////////
// CLuster Integrity
public float AmountIntegrity { get { return (float)cluster.shards.Count / initShardAmount * 100f; } }
}
}