OldBlueWater/BlueWater/Assets/RayFire/Scripts/Classes/Rigid/RFDemolitionCluster.cs
2023-08-22 14:31:24 +09:00

1448 lines
63 KiB
C#

using System;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using Random = UnityEngine.Random;
namespace RayFire
{
[Serializable]
public class RFDemolitionCluster
{
public enum RFDetachType
{
RatioToSize = 0,
WorldUnits = 3
}
// UI
public ConnectivityType connectivity;
public float minimumArea;
public float minimumSize;
public int percentage;
public int seed;
public RFDetachType type;
public int ratio;
public float units;
public int shardArea;
public bool shardDemolition;
public int minAmount;
public int maxAmount;
public bool demolishable;
public RFCollapse collapse;
public int clsCount;
public RFCluster cluster;
public List<RFCluster> minorClusters;
public bool cn;
public bool nd;
public int am; // initial amount, for amount integrity getter
// Non serialized
[NonSerialized] public RFBackupCluster backup;
[NonSerialized] public float damageRadius;
// New cluster name appendix
public static string nameApp = "_cls_";
/// /////////////////////////////////////////////////////////
/// Constructor
/// /////////////////////////////////////////////////////////
// Constructor
public RFDemolitionCluster()
{
InitValues();
LocalReset();
}
void InitValues()
{
connectivity = ConnectivityType.ByBoundingBox;
minimumArea = 0f;
minimumSize = 0f;
percentage = 0;
seed = 0;
type = RFDetachType.RatioToSize;
ratio = 15;
units = 1f;
shardArea = 100;
shardDemolition = false;
minAmount = 3;
maxAmount = 6;
demolishable = true;
collapse = null;
clsCount = 1;
cluster = null;
minorClusters = null;
cn = false;
nd = false;
am = 0;
}
// Reset
public void LocalReset()
{
damageRadius = 0f;
}
// Pool Reset
public void GlobalReset()
{
InitValues();
LocalReset();
backup = null;
}
// Copy from
public void CopyFrom (RFDemolitionCluster source)
{
connectivity = source.connectivity;
minimumArea = source.minimumArea;
minimumSize = source.minimumSize;
percentage = source.percentage;
seed = source.seed;
type = source.type;
ratio = source.ratio;
units = source.units;
shardArea = source.shardArea;
shardDemolition = source.shardDemolition;
maxAmount = source.maxAmount;
minAmount = source.minAmount;
demolishable = source.demolishable;
cn = source.cn;
nd = source.nd;
LocalReset();
}
/// /////////////////////////////////////////////////////////
/// Clusterize
/// /////////////////////////////////////////////////////////
// Reset Connected cluster editor setup
public static void ResetClusterize (RayfireRigid scr)
{
RFPhysic.DestroyColliders (scr);
scr.physics.ignoreList = null;
scr.physics.clusterColliders = null;
scr.clusterDemolition.cluster = new RFCluster();
scr.clusterDemolition.clsCount = 1;
scr.clusterDemolition.minorClusters = null;
scr.transForm = null;
}
// Editor Clusterize
public static void ClusterizeEditor (RayfireRigid scr)
{
// Reset
ResetClusterize (scr);
// Basic components
scr.SetComponentsBasic();
// Set particles
RFParticles.SetParticleComponents(scr);
// Clusterize and return state
Clusterize (scr);
}
// Clusterize
public static void Clusterize (RayfireRigid scr)
{
// TODO skip if minor nested cluster
if (scr.objectType == ObjectType.NestedCluster)
if (scr.clusterDemolition.cluster.id > 1)
return;
// No children
if (scr.transForm.childCount == 0)
{
// Fail and warning
scr.physics.exclude = true;
Debug.Log ("RayFire Rigid: " + scr.name + " has no children with mesh. Object Excluded from simulation.", scr.gameObject);
return;
}
// Missing shards check TODO test with nested cluster
if (RFCluster.IntegrityCheck (scr.clusterDemolition.cluster) == false)
scr.clusterDemolition.cluster = new RFCluster();
// Clusterize
if (scr.objectType == ObjectType.NestedCluster)
ClusterizeNested (scr);
else if (scr.objectType == ObjectType.ConnectedCluster)
ClusterizeConnected (scr);
// Reinit connected cluster shards non serialized fields if main cluster not initialized
RFCluster.InitCluster (scr, scr.clusterDemolition.cluster);
// Set colliders
RFPhysic.SetClusterCollidersByShards (scr);
// Ignore collision
RFPhysic.SetIgnoreColliders(scr.physics, scr.clusterDemolition.cluster.shards);
// Set unyielding state
RayfireUnyielding.ClusterSetup (scr);
// Save backup if cluster will be restored
RFBackupCluster.BackupConnectedCluster (scr);
}
// Clusterize connected cluster
static void ClusterizeConnected (RayfireRigid scr)
{
if (scr.clusterDemolition.cluster.shards.Count == 0)
{
// Create main cluster
scr.clusterDemolition.cluster = new RFCluster();
scr.clusterDemolition.cluster.id = RFCluster.GetUniqClusterId (scr.clusterDemolition.cluster);
scr.clusterDemolition.cluster.tm = scr.transForm;
scr.clusterDemolition.cluster.pos = scr.transForm.position;
scr.clusterDemolition.cluster.rot = scr.transForm.rotation;
scr.clusterDemolition.cluster.depth = 0;
scr.clusterDemolition.cluster.initialized = true;
scr.clusterDemolition.cluster.demolishable = true;
scr.clusterDemolition.cluster.rigid = scr;
// Set shards for main cluster
RFShard.SetShards(scr.clusterDemolition.cluster, scr.clusterDemolition.connectivity, true);
// Set shard neibs
RFShard.SetShardNeibs (scr.clusterDemolition.cluster.shards, scr.clusterDemolition.connectivity,
scr.clusterDemolition.minimumArea, scr.clusterDemolition.minimumSize,
scr.clusterDemolition.percentage, scr.clusterDemolition.seed);
// Set range for area and size
RFCollapse.SetRangeData (scr.clusterDemolition.cluster, scr.clusterDemolition.percentage);
// Set initial shards amount
scr.clusterDemolition.am = scr.clusterDemolition.cluster.shards.Count;
}
}
// Check if minor clusters tm is child of parent cluster and set as child cluster if so
static void InitNestedCluster(RFCluster parentCluster, List<RFCluster> minorClusters)
{
// Define child clusters
for (int i = 0; i < minorClusters.Count; i++)
{
if (minorClusters[i].tm.parent == parentCluster.tm)
{
if (parentCluster.childClusters == null)
parentCluster.childClusters = new List<RFCluster>();
parentCluster.childClusters.Add (minorClusters[i]);
}
}
// Repeat for new child clusters
if (parentCluster.childClusters != null && parentCluster.childClusters.Count > 0)
for (int i = 0; i < parentCluster.childClusters.Count; i++)
InitNestedCluster(parentCluster.childClusters[i], minorClusters);
}
// Create one cluster which includes only children meshes, not children of children meshes.
static void ClusterizeNested (RayfireRigid scr)
{
// Unpack Editor Setup cluster. RUNTIME ONLY
if (Application.isPlaying == true && scr.clusterDemolition.HasMinorClusters == true)
{
// Reinit child clusters
InitNestedCluster(scr.clusterDemolition.cluster, scr.clusterDemolition.minorClusters);
// Reinit main cluster
for (int i = 0; i < scr.clusterDemolition.minorClusters.Count; i++)
scr.clusterDemolition.minorClusters[i].mainCluster = scr.clusterDemolition.cluster;
}
// Has not minor cluster. Never was Clusterized. DO NOT REPEAT FOR MINOR CLUSTERS
if (scr.clusterDemolition.HasMinorClusters == false && scr.clusterDemolition.cluster.id == -1)
{
// Create main cluster
scr.clusterDemolition.cluster = new RFCluster();
scr.clusterDemolition.cluster.id = RFCluster.GetUniqClusterId (scr.clusterDemolition.cluster);
scr.clusterDemolition.cluster.tm = scr.transForm;
scr.clusterDemolition.cluster.pos = scr.transForm.position;
scr.clusterDemolition.cluster.rot = scr.transForm.rotation;
scr.clusterDemolition.cluster.depth = 0;
scr.clusterDemolition.cluster.initialized = true;
scr.clusterDemolition.cluster.demolishable = true;
// List to store all clusters for prefabs
scr.clusterDemolition.cluster.rigid = scr;
scr.clusterDemolition.minorClusters = new List<RFCluster>();
// Create child clusters and their child clusters
ClusterizeNestedRecursive(scr, scr.transForm, scr.clusterDemolition.cluster, scr.clusterDemolition.connectivity);
}
}
// Setup shards and child clusters by children
static void ClusterizeNestedRecursive(RayfireRigid scr, Transform transform, RFCluster cluster, ConnectivityType connectivity)
{
// Get shards and clusters transforms
Transform tm;
List<Transform> tmShards = new List<Transform>();
List<Transform> tmClusters = new List<Transform>();
for (int i = 0; i < transform.childCount; i++)
{
tm = transform.GetChild (i);
if (tm.childCount == 0)
tmShards.Add (tm);
else
tmClusters.Add (tm);
}
// Setup shards
if (tmShards.Count > 0)
RFShard.SetShardsByTransformList (cluster, tmShards.ToArray(), connectivity, true);
// Setup child Clusters
if (tmClusters.Count > 0)
{
for (int i = tmClusters.Count - 1; i >= 0; i--)
{
// TODO check if children have meshfilter
// Create main cluster
RFCluster newCluster = new RFCluster();
newCluster.mainCluster = scr.clusterDemolition.cluster;
newCluster.id = RFCluster.GetUniqClusterId (newCluster);
newCluster.tm = tmClusters[i];
newCluster.pos = tmClusters[i].position;
newCluster.rot = tmClusters[i].rotation;
newCluster.depth = 0;
newCluster.initialized = true;
newCluster.demolishable = true;
// Other properties
newCluster.rigid = newCluster.tm.GetComponent<RayfireRigid>();
newCluster.bound = RFCluster.GetChildrenBound (newCluster.tm);
// Save in minor cluster
scr.clusterDemolition.minorClusters.Add (newCluster);
// Create Child Clusters and shards for new cluster
ClusterizeNestedRecursive (scr, tmClusters[i], newCluster, connectivity);
// Add new child cluster
cluster.AddChildCluster (newCluster);
}
}
}
/// /////////////////////////////////////////////////////////
/// Demolition
/// /////////////////////////////////////////////////////////
// Demolish cluster to children nodes
public static bool DemolishCluster (RayfireRigid scr)
{
if (scr.objectType != ObjectType.NestedCluster && scr.objectType != ObjectType.ConnectedCluster)
return false;
// Skip if not runtime
if (scr.demolitionType != DemolitionType.Runtime)
return true;
// TODO inherit original cluster velocity
// Cluster demolition
if (scr.objectType == ObjectType.NestedCluster)
DemolishNestedCluster (scr);
else if (scr.objectType == ObjectType.ConnectedCluster)
DemolishConnectedCluster (scr);
// Demolition executed
scr.limitations.demolitionShould = false;
// Delete if cluster was completely demolished
if (scr.limitations.demolished == true)
RayfireMan.DestroyFragment (scr, null);
return true;
}
/// /////////////////////////////////////////////////////////
/// Connected Cluster Demolition
/// /////////////////////////////////////////////////////////
// Demolish connected cluster and return detached shards
public static List<RFShard> DemolishConnectedCluster (RayfireRigid scr, Collider[] detachColliders = null)
{
// Get colliders to detach
if (detachColliders == null)
detachColliders = GetDetachColliders (scr);
// No colliders to detach
if (detachColliders.Length == 0)
return null;
// Get detach shards area and remove them from cluster. Includes not connected solo shards from cluster
List<RFShard> detachShards = DetachShardsByColliders (scr, detachColliders);
// No shards to detach. Cluster not demolished
if (detachShards.Count == 0)
return null;
// Get amount of clusters to create and amount of edge shards
int clusterAmount = Random.Range (scr.clusterDemolition.maxAmount, scr.clusterDemolition.minAmount + 1);
// All Shards was detached TODO
// if (scr.clusterDemolition.cluster.shards.Count == 0)
// return;
// All shards should be clusterized to one cluster. Stop
// if (SameClusterCheck(scr, detachShards, shardAmount, clusterAmount) == true)
// return;
// 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. If not main cluster then set biggest child cluster shards to original cluster.
RFCluster.ReduceChildClusters (scr.clusterDemolition.cluster);
}
// Kinematic/ Inactive cluster, Connectivity check if cluster has uny shards. Main cluster keeps all not activated
if (scr.simulationType == SimType.Kinematic || scr.simulationType == SimType.Inactive)
{
// Connectivity check. Separate not connected groups to child clusters
RFCluster.ConnectivityUnyCheck (scr.clusterDemolition.cluster);
}
// TODO Set solo uny detached shards back to cluster or prevent from collection
// Clusterize detached shards if needed. Update child clusters and detached solo shards list
ClusterizeDetachShards (scr, detachShards, clusterAmount, 0);
// Init final cluster ops
PostDemolitionCluster (scr, detachShards);
return detachShards;
}
// Get colliders to detach
static Collider[] GetDetachColliders (RayfireRigid scr)
{
// TODO instead overlap, get contact shard, go through all neibs and collect all in radius,
// TODO exclude and mark all not in radius, stop when there is no grow anymore
// Get damaged colliders
if (scr.objectType == ObjectType.ConnectedCluster && scr.damage.shr == true)
{
List<Collider> damagedCollider = new List<Collider>();
for (int i = 0; i < scr.clusterDemolition.cluster.shards.Count; i++)
if (scr.clusterDemolition.cluster.shards[i].dm > scr.damage.max)
damagedCollider.Add (scr.clusterDemolition.cluster.shards[i].col);
// Detach damaged shards
if (damagedCollider.Count > 0)
{
scr.clusterDemolition.damageRadius = 0f;
return damagedCollider.ToArray();
}
}
// Get colliders by damage radius and reset it
if (scr.clusterDemolition.damageRadius > 0)
{
Collider[] colliders = Physics.OverlapSphere (scr.limitations.contactVector3, scr.clusterDemolition.damageRadius, 1 << scr.gameObject.layer);
scr.clusterDemolition.damageRadius = 0f;
return colliders;
}
// Get detach colliders by manual damage radius
if (scr.clusterDemolition.type == RFDetachType.WorldUnits)
if (scr.clusterDemolition.units > 0)
return Physics.OverlapSphere (scr.limitations.contactVector3, scr.clusterDemolition.units, 1 << scr.gameObject.layer);
// Use all colliders if contactRadius is 100%
if (scr.clusterDemolition.ratio == 100)
return scr.physics.clusterColliders.ToArray();
// Get colliders by contactRadius by overlap
float contactRadius = scr.limitations.bboxSize / 100f * scr.clusterDemolition.ratio;
return Physics.OverlapSphere (scr.limitations.contactVector3, contactRadius, 1 << scr.gameObject.layer);
}
// Create runtime clusters
static List<RFShard> DetachShardsByColliders (RayfireRigid scr, Collider[] detachColliders)
{
// Collect detach shards. Mark removed shards
List<RFShard> detachShards = new List<RFShard>();
HashSet<Collider> detachCollidersHash = new HashSet<Collider>(detachColliders);
// Detach shards for dynamic/sleeping cluster. Detach all
for (int i = scr.physics.clusterColliders.Count - 1; i >= 0; i--)
{
// Skip not activatable uny shard
if (scr.clusterDemolition.cluster.shards[i].uny == true && scr.clusterDemolition.cluster.shards[i].act == false)
continue;
// Collect other shards
if (detachCollidersHash.Contains (scr.physics.clusterColliders[i]) == true)
{
detachShards.Add (scr.clusterDemolition.cluster.shards[i]);
scr.clusterDemolition.cluster.shards.RemoveAt (i);
scr.physics.clusterColliders.RemoveAt (i);
}
}
// No detach shards. Cluster was not demolished
if (detachShards.Count == 0)
return detachShards;
// Original cluster has only one shard left. Add it to all detached shards
if (scr.clusterDemolition.cluster.shards.Count == 1)
{
detachShards.Add (scr.clusterDemolition.cluster.shards[0]);
scr.clusterDemolition.cluster.shards.Clear();
scr.physics.clusterColliders.Clear();
}
// Update shards cluster data before reinit left and detached shards neib data
for (int i = 0; i < detachShards.Count; i++)
detachShards[i].cluster = null;
// Original cluster still has shards
if (scr.clusterDemolition.cluster.shards.Count > 0)
{
// Remove neib shards which are not in current cluster anymore
RFShard.ReinitNeibs (scr.clusterDemolition.cluster.shards);
// TODO detach shards with one neib now which had detached shards as neibs
// Collect solo shards, remove from cluster
RFCluster.DetachSoloShards (scr.clusterDemolition.cluster, detachShards);
}
return detachShards;
}
/// /////////////////////////////////////////////////////////
/// Nested cluster demolition
/// /////////////////////////////////////////////////////////
// Demolish nested cluster
static void DemolishNestedCluster (RayfireRigid scr)
{
// Set demolished state. IMPORTANT should be here
scr.limitations.demolished = true;
List<RFShard> detachShards = new List<RFShard>(scr.clusterDemolition.cluster.shards.Count);
for (int i = 0; i < scr.clusterDemolition.cluster.shards.Count; i++)
detachShards.Add (scr.clusterDemolition.cluster.shards[i]);
// Clear list if not going to be used
if (scr.reset.action == RFReset.PostDemolitionType.DestroyWithDelay)
scr.clusterDemolition.cluster.shards.Clear();
// Create child clusters and shards
PostDemolitionCluster (scr, detachShards);
}
/// /////////////////////////////////////////////////////////
/// Post Demolition
/// /////////////////////////////////////////////////////////
// Final ops at connected cluster demolition, slice, collapse
public static void PostDemolitionCluster (RayfireRigid scr, List<RFShard> detachShards)
{
// Prepare
if (scr.fragments == null)
scr.fragments = new List<RayfireRigid>();
else
scr.fragments.Clear();
// Create Rigid Shards
SetupDetachShards (scr, detachShards);
// Create Rigid Child cluster
CreateChildClusters (scr, scr.clusterDemolition.cluster.childClusters);
// Update properties
UpdateOriginalCluster (scr);
// Set velocity
RFPhysic.SetFragmentsVelocity (scr);
// Fading
RFFade.FadeClusterShards(scr, detachShards);
// Init particles
RFParticles.InitDemolitionParticles(scr);
// Init sound
RFSound.DemolitionSound(scr.sound, scr.limitations.bboxSize);
// Event
RFDemolitionEvent.RigidDemolitionEvent (scr);
// Set layer and tag
RFFragmentProperties.SetLayer(scr);
RFFragmentProperties.SetTag(scr);
}
// Create runtime clusters
static void SetupDetachShards (RayfireRigid scr, List<RFShard> detachShards)
{
// No shards to create
if (detachShards.Count == 0)
return;
// Add rigid component to detached children in case they have no RigidRoot parent
if (scr.rigidRoot == null)
AddRigidComponent (scr, detachShards);
else
SetRigidRootShard (scr, detachShards);
}
// Create Rigid Child cluster
static void CreateChildClusters (RayfireRigid scr, List<RFCluster> childClusters)
{
// No child clusters to create
if (childClusters == null || childClusters.Count == 0)
return;
// Create child clusters
for (int i = 0; i < childClusters.Count; i++)
{
// Set demolishable state
childClusters[i].demolishable = scr.clusterDemolition.demolishable;
// Copy Range data for runtime clusters
RFCollapse.CopyRangeData (childClusters[i], scr.clusterDemolition.cluster);
// Create cluster
CreateClusterRuntime (scr, childClusters[i]);
}
}
// Demolish connected cluster by colliders
static void UpdateOriginalCluster (RayfireRigid scr)
{
// All shards were detached. Set demolished state
if (scr.clusterDemolition.cluster.shards.Count == 0)
{
scr.physics.rigidBody.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
scr.physics.rigidBody.isKinematic = true;
scr.limitations.demolished = true;
// Remove from minor cluster list
if (scr.objectType == ObjectType.ConnectedCluster)
if (scr.clusterDemolition.cluster.mainCluster != null && scr.clusterDemolition.cluster.mainCluster.rigid != null)
scr.clusterDemolition.cluster.mainCluster.rigid.clusterDemolition.minorClusters.Remove (scr.clusterDemolition.cluster);
return;
}
// Original cluster shards not going to be changed anymore. Reinit colliders list
RFPhysic.CollectClusterColliders (scr, scr.clusterDemolition.cluster);
// Update mass
RFPhysic.SetDensity (scr);
// Set dynamic if cluster inherit from kinematic/inactive but has no uny shards
if (scr.simulationType == SimType.Kinematic || scr.simulationType == SimType.Inactive)
if(scr.activation.con == true && scr.clusterDemolition.cluster.UnyieldingByShard == false)
scr.Activate();
// Cluster was demolished but not completely, reuse what left
scr.limitations.birthTime = Time.time;
// Update reduced bound
scr.clusterDemolition.cluster.bound = RFCluster.GetShardsBound (scr.clusterDemolition.cluster.shards);
scr.limitations.bboxSize = scr.clusterDemolition.cluster.bound.size.magnitude;
// Reset original cluster center of mass because of detached colliders: solo shards and child clusters
scr.physics.rigidBody.ResetCenterOfMass();
// Set velocity after demolition
scr.physics.rigidBody.velocity = scr.physics.velocity * scr.physics.dm;
}
/// /////////////////////////////////////////////////////////
/// Slice
/// /////////////////////////////////////////////////////////
// Slice connected cluster
public static void SliceConnectedCluster (RayfireRigid scr)
{
// Get detach distance
float detachDistance = 0f;
if (scr.clusterDemolition.type == RFDetachType.WorldUnits)
if (scr.clusterDemolition.units > 0)
detachDistance = scr.clusterDemolition.units;
// Use all colliders if contactRadius is 100%
if (scr.clusterDemolition.type == RFDetachType.RatioToSize)
detachDistance = scr.limitations.bboxSize / 100f * scr.clusterDemolition.ratio;
// Get two clusters for planes sides
List<RFShard> detachShards = new List<RFShard>();
List<RFShard> cluster1Shards = new List<RFShard>();
List<RFShard> cluster2Shards = new List<RFShard>();
// Separate shards by slice plane
Vector3 shardPos;
Plane plane = new Plane(scr.limitations.slicePlanes[1], scr.limitations.slicePlanes[0]);
scr.limitations.slicePlanes.Clear();
for (int s = 0; s < scr.clusterDemolition.cluster.shards.Count; s++)
{
// Save position
shardPos = scr.clusterDemolition.cluster.shards[s].tm.position;
// Check distance and add to detach shards if too close
if (detachDistance > 0)
{
if (Math.Abs(plane.GetDistanceToPoint (shardPos)) < detachDistance)
{
detachShards.Add (scr.clusterDemolition.cluster.shards[s]);
scr.clusterDemolition.cluster.shards[s].cluster = null;
continue;
}
}
// Get plane side
if (plane.GetSide (shardPos) == true)
cluster1Shards.Add (scr.clusterDemolition.cluster.shards[s]);
else
cluster2Shards.Add (scr.clusterDemolition.cluster.shards[s]);
}
// Check clusters for solo shards and send them to detach shards
if (cluster1Shards.Count == 1)
{
detachShards.Add (cluster1Shards[0]);
cluster1Shards.Clear();
}
if (cluster2Shards.Count == 1)
{
detachShards.Add (cluster2Shards[0]);
cluster2Shards.Clear();
}
// No detach shards and One of the cluster equal to original cluster. Stop
if (detachShards.Count == 0)
if (cluster1Shards.Count == scr.clusterDemolition.cluster.shards.Count ||
cluster2Shards.Count == scr.clusterDemolition.cluster.shards.Count)
return;
// Dynamic cluster connectivity check, all clusters are equal, pick biggest to keep as original
if (scr.simulationType == SimType.Dynamic || scr.simulationType == SimType.Sleeping)
{
// Prepare child clusters list
if (cluster1Shards.Count >= 2 || cluster2Shards.Count >= 2)
if (scr.clusterDemolition.cluster.childClusters == null)
scr.clusterDemolition.cluster.childClusters = new List<RFCluster>();
else
scr.clusterDemolition.cluster.childClusters.Clear();
// Setup cluster by one slice plane side shards. Connectivity check
SetupPlaneShards (scr, cluster1Shards, detachShards);
SetupPlaneShards (scr, cluster2Shards, detachShards);
// Cluster is not connected. If not main cluster then set biggest child cluster shards to original cluster.
RFCluster.ReduceChildClusters (scr.clusterDemolition.cluster);
}
// Kinematic/ Inactive cluster, Connectivity check if cluster has uny shards. Main cluster keeps all not activated
if (scr.simulationType == SimType.Kinematic || scr.simulationType == SimType.Inactive)
{
// Remove detach shards and child clusters shards from main cluster shards
for (int i = scr.clusterDemolition.cluster.shards.Count - 1; i >= 0; i--)
if (scr.clusterDemolition.cluster.shards[i].cluster != scr.clusterDemolition.cluster)
scr.clusterDemolition.cluster.shards.RemoveAt (i);
// Reset neibs
RFShard.ReinitNeibs (scr.clusterDemolition.cluster.shards);
// Check for uny connectivity
RFCluster.ConnectivityUnyCheck (scr.clusterDemolition.cluster);
}
// Detach shards not going to be changed anymore. Reinit detach shards neib.
if (detachShards.Count > 0)
{
// DO LATER
RFShard.ReinitNeibs (detachShards);
// Get point in other way
scr.limitations.contactVector3 = plane.ClosestPointOnPlane (detachShards[0].tm.position);
scr.limitations.contactNormal = plane.normal;
// Get amount of clusters to create and amount of edge shards
int clusterAmount = Random.Range (scr.clusterDemolition.maxAmount, scr.clusterDemolition.minAmount + 1);
// Clusterize detach shards but over plane
ClusterizeDetachShards (scr, detachShards, clusterAmount, 1);
}
// Init final cluster ops
PostDemolitionCluster (scr, detachShards);
}
// Setup cluster by one slice plane side shards. Connectivity check
static void SetupPlaneShards (RayfireRigid scr, List<RFShard> clusterShards, List<RFShard> detachShards)
{
// Reinit cluster neibs
if (clusterShards.Count >= 2)
{
RFCluster cluster = new RFCluster();
cluster.id = 2;
cluster.shards = clusterShards;
cluster.demolishable = true;
for (int i = 0; i < cluster.shards.Count; i++)
cluster.shards[i].cluster = cluster;
// Set main cluster
cluster.mainCluster = scr.clusterDemolition.cluster;
// Reset neibs
RFShard.ReinitNeibs (cluster.shards);
// Remove not connected solo shards, shards without neibs
for (int i = cluster.shards.Count - 1; i >= 0; i--)
{
if (cluster.shards[i].neibShards.Count == 0)
{
detachShards.Add (cluster.shards[i]);
cluster.shards[i].cluster = null;
cluster.shards.RemoveAt (i);
}
}
// Cluster had only solo shards
if (cluster.shards.Count == 0)
return;
// Check connectivity, store all less not connected clusters as child cluster
RFCluster.ConnectivityCheck (cluster);
// Cluster is not connected. Set biggest child cluster shards to original cluster. Cant be 1 child cluster here
RFCluster.ReduceChildClusters (cluster);
// Collect original or not connected clusters
scr.clusterDemolition.cluster.childClusters.Add (cluster);
if (cluster.HasChildClusters == true)
{
for (int c = 0; c < cluster.childClusters.Count; c++)
scr.clusterDemolition.cluster.childClusters.Add (cluster.childClusters[c]);
cluster.childClusters.Clear();
}
}
}
/// /////////////////////////////////////////////////////////
/// Demolition /Slicing common
/// /////////////////////////////////////////////////////////
// Demolish connected cluster by colliders
static void ClusterizeDetachShards (RayfireRigid scr, List<RFShard> detachShards, int clusterAmount, int sortType)
{
// Clustering disabled or one solo shard. Nothing to clusterize
if (scr.clusterDemolition.shardArea == 100 || detachShards.Count <= 1)
return;
// TODO complete clusterization
if (scr.clusterDemolition.shardArea == 0)
{
}
// Amount of solo shards
int centerShardsAmount = detachShards.Count * scr.clusterDemolition.shardArea / 100;
// Not enough solo shards for clustering
if (detachShards.Count - centerShardsAmount <= 1)
return;
// Shards less than clusters needed or group is too small to be clustered. Stop
if (detachShards.Count <= clusterAmount)
return;
// Set up child cluster
if (scr.clusterDemolition.cluster.childClusters == null)
scr.clusterDemolition.cluster.childClusters = new List<RFCluster>();
int startIndex = scr.clusterDemolition.cluster.childClusters.Count;
// Preserve center shards
RFShard[] center = null;
if (centerShardsAmount > 0)
{
// Get center shards
if (sortType == 0)
center = RFShard.SortByDistanceToPoint (detachShards.ToArray(), scr.limitations.contactVector3, centerShardsAmount);
else if (sortType == 1)
center = RFShard.SortByDistanceToPlane (detachShards.ToArray(), scr.limitations.contactVector3, scr.limitations.contactNormal, centerShardsAmount);
if (center != null)
{
// Remove center shards from detach shards before slice them
HashSet<RFShard> centerHash = new HashSet<RFShard>(center);
for (int i = detachShards.Count - 1; i >= 0; i--)
if (centerHash.Contains (detachShards[i]) == true)
detachShards.RemoveAt (i);
// Change center shards cluster to reinit detach shards neibs
for (int i = 0; i < center.Length; i++)
center[i].cluster = scr.clusterDemolition.cluster;
}
}
// Separate group of shards to several child clusters
DivideAllShards (scr.clusterDemolition.cluster, detachShards, clusterAmount);
// Disable colliders TODO prevent empty clusters
// DetachEdgeShards (scr, scr.clusterDemolition.cluster, detachShards, scr.clusterDemolition.edgeShardArea);
// Detach shards with one neib from clusters
DetachOneNeibShards (scr.clusterDemolition.cluster.childClusters, detachShards, centerShardsAmount, startIndex);
// Add center shards back
if (center != null)
{
// Nullify center shards cluster back
for (int i = 0; i < center.Length; i++)
center[i].cluster = null;
detachShards.AddRange (center);
}
}
// Create runtime clusters
public static void CreateClusterRuntime (RayfireRigid scr, RFCluster cluster)
{
if (cluster.shards.Count == 1)
Debug.Log ("Solo cluster warning: " + scr.name);
// Register in main cluster
if (scr.objectType == ObjectType.ConnectedCluster)
{
if (scr.clusterDemolition.cluster != null && scr.clusterDemolition.cluster.mainCluster != null)
{
cluster.mainCluster = scr.clusterDemolition.cluster.mainCluster;
scr.clusterDemolition.cluster.mainCluster.childClusters.Add (cluster);
}
if (cluster.mainCluster != null && cluster.mainCluster.rigid != null)
{
if (cluster.mainCluster.rigid.clusterDemolition.minorClusters == null)
cluster.mainCluster.rigid.clusterDemolition.minorClusters = new List<RFCluster>();
cluster.mainCluster.rigid.clusterDemolition.minorClusters.Add (cluster);
}
}
// Set bound if has not
if (cluster.bound.size.magnitude == 0)
{
if (scr.objectType == ObjectType.ConnectedCluster)
cluster.bound = RFCluster.GetShardsBound (cluster.shards);
else if (scr.objectType == ObjectType.NestedCluster)
cluster.bound = RFCluster.GetClusterBound(cluster);
}
// Create root for new connected cluster, set it's parent and register in storage
if (cluster.tm == null)
{
// Create root
RFCluster.CreateClusterRoot (cluster, cluster.shards[0].tm.position, scr.transForm.rotation,
scr.gameObject.layer, scr.gameObject.tag, scr.gameObject.name + nameApp + cluster.id);
// Set parent and register
RayfireMan.SetParentByManager (cluster.tm, scr.transForm);
}
// Set parent for nested cluster root, register in storage if not resettable
else
RayfireMan.SetParentByManager (cluster.tm, scr.transForm,
scr.reset.action == RFReset.PostDemolitionType.DeactivateToReset);
// Parent to main root. Nested cluster already has all shards rooted
if (scr.objectType == ObjectType.ConnectedCluster)
{
for (int s = 0; s < cluster.shards.Count; s++)
{
// Important. Disable and enable colliders before and after reparent
cluster.shards[s].col.enabled = false;
cluster.shards[s].tm.parent = cluster.tm;
cluster.shards[s].col.enabled = true;
// Set cluster tm as parent for Rigid to track parent tm later
if (cluster.shards[s].rigid != null)
cluster.shards[s].rigid.rootParent = cluster.tm;
}
}
// Not for RigidRoot operations. RigidRoot has same Rigid as source scr and cluster Rigid
if (scr != cluster.rigid)
{
// Check if already has rigid but it was not referenced, add if has not
if (cluster.rigid == null)
{
cluster.rigid = cluster.tm.gameObject.GetComponent<RayfireRigid>();
if (cluster.rigid == null)
cluster.rigid = cluster.tm.gameObject.AddComponent<RayfireRigid>();
}
// Collect fragment
if (scr.fragments == null)
scr.fragments = new List<RayfireRigid>();
scr.fragments.Add (cluster.rigid);
// Copy properties from parent to fragment node
scr.CopyPropertiesTo (cluster.rigid);
// Copy particles
RFParticles.CopyRigidParticles (scr, cluster.rigid);
}
// Source Rigid has RigidRoot parent
if (scr.rigidRoot != null)
{
// Set parent RigidRoot and Collect cluster in RigidRoot to delete at reset
cluster.rigid.rigidRoot = scr.rigidRoot;
cluster.rigid.rigidRoot.clusters.Add (cluster);
// Register in storage to delete cluster tm in case of its demolition
RayfireMan.inst.storage.Register (cluster.tm);
}
// Set to mesh TODO why?
cluster.rigid.physics.ct = RFColliderType.Mesh;
// Set dynamic if cluster inherit from kinematic/inactive but has no uny shards
if (cluster.rigid.simulationType == SimType.Kinematic || cluster.rigid.simulationType == SimType.Inactive)
if(cluster.rigid.activation.con == true && cluster.UnyieldingByShard == false)
cluster.rigid.simulationType = SimType.Dynamic;
// Set demolishable state for detached area clusters
cluster.rigid.demolitionType = cluster.demolishable == true
? DemolitionType.Runtime
: DemolitionType.None;
// Do not destroy fragment because cluster could be reused
if (scr.reset.action == RFReset.PostDemolitionType.DeactivateToReset)
cluster.rigid.reset.action = RFReset.PostDemolitionType.DeactivateToReset;
// Set cluster
cluster.initialized = true;
cluster.rigid.clusterDemolition.cluster = cluster;
// Set colliders list
RFPhysic.CollectClusterColliders (cluster.rigid, cluster.rigid.clusterDemolition.cluster);
// Set initial shards amount
cluster.rigid.clusterDemolition.am = cluster.rigid.clusterDemolition.cluster.shards.Count;
// Turn on
cluster.rigid.Initialize();
// Nested cluster
if (cluster.rigid.objectType == ObjectType.NestedCluster)
{
// Copy depth
cluster.rigid.limitations.depth = scr.limitations.depth;
// Set current depth
cluster.rigid.limitations.currentDepth = scr.limitations.currentDepth + 1;
// last demolition depth
if (scr.limitations.depth != 0 && cluster.rigid.limitations.currentDepth >= scr.limitations.depth)
cluster.rigid.demolitionType = DemolitionType.None;
}
}
/// /////////////////////////////////////////////////////////
/// Cluster Init
/// /////////////////////////////////////////////////////////
// Separate group of shards to several clusters by half
static void DivideAllShards (RFCluster cluster, List<RFShard> detachShards, int amount)
{
// Get starting search index to exclude first child cluster from Connectivity check
int startIndex = cluster.childClusters.Count;
// Remove neib shards which still belongs to some cluster. Detach shards has no cluster
RFShard.ReinitNeibs (detachShards);
// Create base child cluster with detach shards
RFCluster baseCLuster = new RFCluster();
// Set main cluster
baseCLuster.mainCluster = cluster.mainCluster == null
? cluster
: cluster.mainCluster;
// Set uniq id after main cluster defined
baseCLuster.id = RFCluster.GetUniqClusterId (baseCLuster);
// Set shards
for (int i = detachShards.Count - 1; i >= 0; i--)
{
if (detachShards[i].neibShards.Count > 0)
{
baseCLuster.shards.Add (detachShards[i]);
detachShards[i].cluster = baseCLuster;
detachShards.RemoveAt (i);
}
}
cluster.childClusters.Add (baseCLuster);
// Base cluster neib check
RFShard.ReinitNeibs (baseCLuster.shards);
// SLice to half amount - 1 times
for (int i = 0; i < amount - 1; i++)
{
// Get biggest child cluster
int biggestInd = 0;
int biggestAmount = 0;
for (int b = startIndex; b < cluster.childClusters.Count; b++)
if (cluster.childClusters[b].shards.Count > biggestAmount)
{
biggestInd = b;
biggestAmount = cluster.childClusters[b].shards.Count;
}
// Biggest child cluster is very small. Stop
if (cluster.childClusters[biggestInd].shards.Count < 4)
break;
// Slice biggest group
DivideShards (cluster, cluster.childClusters[biggestInd]);
// Biggest cluster was not separated. stop
if (biggestAmount == cluster.childClusters[biggestInd].shards.Count)
break;
}
// Check new child clusters for solo shards
for (int c = cluster.childClusters.Count - 1; c >= startIndex; c--)
{
for (int s = cluster.childClusters[c].shards.Count - 1; s >= 0; s--)
{
if (cluster.childClusters[c].shards[s].neibShards.Count == 0)
{
detachShards.Add (cluster.childClusters[c].shards[s]);
cluster.childClusters[c].shards[s].cluster = null;
cluster.childClusters[c].shards.RemoveAt (s);
}
}
// Remove clusters with no shards. All was solo and removed
if (cluster.childClusters[c].shards.Count == 0)
cluster.childClusters.RemoveAt (c);
}
// Check for connectivity of child cluster
for (int c = startIndex; c < cluster.childClusters.Count; c++)
{
// Connectivity
RFCluster.ConnectivityCheck (cluster.childClusters[c]);
// Cluster is not connected. Set biggest child cluster shards to original cluster. Cant be 1 child cluster here
RFCluster.ReduceChildClusters (cluster.childClusters[c]);
}
// Set their child cluster as current child cluster and clear list
for (int c = cluster.childClusters.Count - 1; c >= startIndex; c--)
{
if (cluster.childClusters[c].HasChildClusters == true)
{
cluster.childClusters.AddRange (cluster.childClusters[c].childClusters);
cluster.childClusters[c].childClusters = null;
}
}
}
// Separate group of shards to half. Do not return solo shards
static void DivideShards (RFCluster mainCluster, RFCluster childCluster)
{
// Get group bound
childCluster.bound = RFCluster.GetShardsBoundByPosition (childCluster.shards);
// Get slice plane at middle of longest bound edge
Plane plane = RFShard.GetSlicePlane (childCluster.bound);
// Separate by plane and collect indexes of separated shards
List<int> indexList = new List<int>(childCluster.shards.Count / 2);
for (int i = 0; i < childCluster.shards.Count; i++)
if (plane.GetSide (childCluster.shards[i].tm.position) == true)
indexList.Add (i);
// One of the group contains only one shard. Group is too small. Stop.
if (indexList.Count <= 1 || indexList.Count > childCluster.shards.Count - 2)
return;
// Create new group list and remove from input list
RFCluster newChildCluster = new RFCluster();
// Set main cluster
newChildCluster.mainCluster = mainCluster.mainCluster == null
? mainCluster
: mainCluster.mainCluster;
// Set uniq id after main cluster defined
newChildCluster.id = RFCluster.GetUniqClusterId (newChildCluster);
// Set shards
newChildCluster.shards = new List<RFShard>(indexList.Count);
for (int i = indexList.Count - 1; i >= 0; i--)
{
newChildCluster.shards.Add (childCluster.shards[indexList[i]]);
childCluster.shards[indexList[i]].cluster = newChildCluster;
childCluster.shards.RemoveAt (indexList[i]);
}
// Collect new cluster
mainCluster.childClusters.Add (newChildCluster);
// Remove neib shards which still belongs to some cluster. Detach solo shards
RFShard.ReinitNeibs (newChildCluster.shards);
RFShard.ReinitNeibs (childCluster.shards);
}
// Detach shards from child clusters at edges
public static void DetachEdgeShards (RFCluster cluster, List<RFShard> detachShards, int edgeShardArea)
{
if (edgeShardArea == 0)
return;
for (int i = 0; i < cluster.childClusters.Count; i++)
{
if (cluster.childClusters[i].shards.Count >= 5)
{
int amount = cluster.childClusters[i].shards.Count * edgeShardArea / 100;
if (amount > 0)
{
// TODO detach in better way: farthest, lowest shard area, most lost neibs, ect
int startAmount = cluster.childClusters[i].shards.Count;
for (int j = cluster.childClusters[i].shards.Count - 1; j >= 0; j--)
{
// Stop if limit reached
if (amount == 0)
break;
// Detach edge shard TODO random order
if (cluster.childClusters[i].shards[j].neibShards.Count < cluster.childClusters[i].shards[j].nAm)
{
amount--;
detachShards.Add (cluster.childClusters[i].shards[j]);
cluster.childClusters[i].shards[j].cluster = null;
cluster.childClusters[i].shards.RemoveAt (j);
}
}
// Reinit cluster
if (startAmount > cluster.childClusters[i].shards.Count)
RFShard.ReinitNeibs (cluster.childClusters[i].shards);
}
}
}
}
// Detach one shard with one neib from clusters
static void DetachOneNeibShards (List<RFCluster> childClusters, List<RFShard> detachShards, int edgeAmount, int startIndex)
{
// Check every detached child cluster
while (edgeAmount >= detachShards.Count)
{
// Detach one shard with one neib from every cluster
int detachAmount = detachShards.Count;
for (int c = childClusters.Count - 1; c >= startIndex; c--)
{
// Detach one shard with one neib
DetachOneNeibShard (childClusters[c], detachShards);
// Enough edge shards
if (edgeAmount >= detachShards.Count)
return;
}
// No cluster with shard with one neib. Stop while
if (detachShards.Count == detachAmount)
return;
}
}
// Detach one shard with one neib
static void DetachOneNeibShard (RFCluster cls, List<RFShard> detachShards)
{
if (cls.shards.Count >= 3)
{
// Check every shard
for (int s = cls.shards.Count - 1; s >= 0; s--)
{
// Check amount of neibs
if (cls.shards[s].neibShards.Count == 1)
{
// Collect shard with one neib
detachShards.Add (cls.shards[s]);
cls.shards[s].cluster = null;
// Clean up neib shard
for (int i = cls.shards[s].neibShards[0].neibShards.Count - 1; i >= 0; i--)
if (cls.shards[s].neibShards[0].neibShards[i].cluster == null)
cls.shards[s].neibShards[0].RemoveNeibAt (i);
// Remove from cluster
cls.shards.RemoveAt (s);
// Enough detach shards
if (cls.shards.Count <= 2)
return;
}
}
}
}
/// /////////////////////////////////////////////////////////
/// Add rigid
/// /////////////////////////////////////////////////////////
// Add rigid component to transform list
static void AddRigidComponent (RayfireRigid scr, List<RFShard> shardList)
{
// Get parent for connected cluster detached shards
Transform tm = RayfireMan.GetParentByManager (scr);
// Add Rigid component to detached shards
for (int i = 0; i < shardList.Count; i++)
AddRigidComponent (scr, shardList[i], tm);
}
// Add rigid component to transform list
static void AddRigidComponent (RayfireRigid scr, RFShard shard, Transform parent)
{
// Set parent
shard.tm.parent = parent;
// Add new component if shard has not rigid
if (shard.rigid == null)
{
// Add Rigid
shard.rigid = shard.tm.gameObject.AddComponent<RayfireRigid>();
// Copy properties from parent to fragment node
scr.CopyPropertiesTo (shard.rigid);
// Turn off demolition for solo fragments
if (scr.clusterDemolition.shardDemolition == false)
shard.rigid.demolitionType = DemolitionType.None;
}
else
{
// Add rigid body to Rigid if it was deleted because of clustering
if (shard.rigid.physics.rigidBody == null)
shard.rigid.physics.rigidBody = shard.rigid.gameObject.AddComponent<Rigidbody>();
// Set density. After collider defined TODO save mass at first apply, reuse now
RFPhysic.SetDensity (shard.rigid);
// Set drag properties
RFPhysic.SetDrag (shard.rigid);
}
// Skip excluded
if (shard.rigid.physics.exclude == true)
return;
// Collect fragment
scr.fragments.Add (shard.rigid);
// Set unyielding
shard.rigid.activation.uny = shard.uny;
shard.rigid.activation.atb = shard.act;
// Copy particles
RFParticles.CopyRigidParticles (scr, shard.rigid);
// Set to mesh
shard.rigid.objectType = ObjectType.Mesh;
shard.rigid.physics.ct = RFColliderType.Mesh;
// Set dynamic if cluster inherit from kinematic/inactive but has no uny shards
if (shard.rigid.simulationType == SimType.Kinematic || shard.rigid.simulationType == SimType.Inactive)
if(shard.rigid.activation.con == true && shard.uny == false)
shard.rigid.simulationType = SimType.Dynamic;
// Do not destroy fragment because cluster could be reused
if (scr.reset.action == RFReset.PostDemolitionType.DeactivateToReset)
shard.rigid.reset.action = RFReset.PostDemolitionType.DeactivateToReset;
// Update depth level and amount
shard.rigid.limitations.currentDepth = scr.limitations.currentDepth + 1;
// Turn on
shard.rigid.Initialize();
// Set rb to track later in case of rigidRoot reset with connectivity + clusterize + demolishable
shard.rb = shard.rigid.physics.rigidBody;
// IMPORTANT. Set mesh collider convex for gun impact detection
if (shard.rigid.objectType == ObjectType.Mesh)
if (shard.rigid.physics.meshCollider != null)
if (shard.rigid.physics.meshCollider is MeshCollider == true)
(shard.rigid.physics.meshCollider as MeshCollider).convex = true;
//Debug.Log (shard.rb, shard.rb.gameObject);
}
/// /////////////////////////////////////////////////////////
/// RigidRoot shards -> Connected Cluster -> RigidRoot shards
/// /////////////////////////////////////////////////////////
// Set demolished Connected CLuster shards back to parent RigidRoot
static void SetRigidRootShard(RayfireRigid scr, List<RFShard> shards)
{
for (int i = 0; i < shards.Count; i++)
{
// Set parent to rigidroot
shards[i].tm.parent = scr.rigidRoot.tm;
// Set as dynamic
shards[i].sm = SimType.Dynamic;
}
// Add RigidBody and set physics properties TODO won't affect shards with Rigid
RFPhysic.SetPhysics(shards, scr.physics);
// TODO inherit velocity
// TODO Init demolition particles
// TODO Init Fade
}
/// /////////////////////////////////////////////////////////
/// Get
/// /////////////////////////////////////////////////////////
// All shards should be clusterized to one cluster. Stop
static bool SameClusterCheck(RayfireRigid scr, List<RFShard> detachShards, int shardAmount, int clusterAmount)
{
if (shardAmount == detachShards.Count && clusterAmount == 1)
{
scr.limitations.demolitionShould = false;
scr.clusterDemolition.cluster.shards = detachShards;
for (int i = 0; i < scr.clusterDemolition.cluster.shards.Count; i++)
scr.clusterDemolition.cluster.shards[i].cluster = scr.clusterDemolition.cluster;
RFPhysic.CollectClusterColliders (scr, scr.clusterDemolition.cluster);
return true;
}
return false;
}
// Had child cluster
public bool HasChildClusters { get { return cluster.childClusters != null && cluster.childClusters.Count > 0; } }
public bool HasMinorClusters { get { return minorClusters != null && minorClusters.Count > 0; } }
}
}