ThrowBall/Assets/Plugins/RayFire/Scripts/Components/RayfireRigidRoot.cs

883 lines
32 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace RayFire
{
[Serializable]
public class RFRigidRootDemolition
{
public RFLimitations limitations = new RFLimitations();
[FormerlySerializedAs ("clusterDemolition")] public RFDemolitionCluster clsDemol = new RFDemolitionCluster();
}
[SelectionBase]
[DisallowMultipleComponent]
[AddComponentMenu ("RayFire/Rayfire Rigid Root")]
[HelpURL ("https://rayfirestudios.com/unity-online-help/components/unity-rigid-root-component/")]
public class RayfireRigidRoot : MonoBehaviour
{
public enum InitType
{
ByMethod = 0,
AtStart = 1
}
// UI
public InitType initialization = InitType.AtStart;
[FormerlySerializedAs ("simulationType")] public SimType simTp = SimType.Dynamic;
public RFPhysic physics = new RFPhysic();
public RFActivation activation = new RFActivation();
[FormerlySerializedAs ("demolition")] public RFRigidRootDemolition dml = new RFRigidRootDemolition();
public RFFade fading = new RFFade();
public RFReset reset = new RFReset();
// Hidden
public bool initialized;
public bool cached;
public Transform tm;
public RFCluster cluster;
public List<RayfireRigid> meshRoots;
public List<RayfireRigid> connClusters;
public List<Collider> collidersList;
public List<RFShard> meshRootShards;
public List<RFShard> rigidRootShards;
public List<RFShard> connClusterShards;
// Non Serialized
[NonSerialized] public float sizeSum;
[NonSerialized] public RayfireSound sound;
[NonSerialized] public List<RFCluster> clusters;
[NonSerialized] public List<RFShard> inactiveShards;
[NonSerialized] public List<RFShard> offsetFadeShards;
[NonSerialized] List<RFShard> destroyShards; // TODO remove or use. not in use right now
[NonSerialized] List<RFShard> meshRigidShards;
[NonSerialized] public Transform[] parentList;
[NonSerialized] public List<RayfireDebris> debrisList;
[NonSerialized] public List<RayfireDust> dustList;
[NonSerialized] public RayfireUnyielding[] unyList;
[NonSerialized] public List<Transform> particleList;
[NonSerialized] public bool corState;
[NonSerialized] public HashSet<Collider> collidersHash;
// Events
public RFActivationEvent activationEvent = new RFActivationEvent();
// Static
static readonly string strRoot = "RayFire RigidRoot: ";
/// /////////////////////////////////////////////////////////
/// Common
/// /////////////////////////////////////////////////////////
// Awake
void Awake()
{
if (initialization == InitType.AtStart)
{
Initialize();
}
}
/// /////////////////////////////////////////////////////////
/// Enable/Disable
/// /////////////////////////////////////////////////////////
// Disable
void OnDisable()
{
// Set coroutines states
corState = false;
activation.inactiveCorState = false;
fading.offsetCorState = false;
}
// Activation
void OnEnable()
{
if (gameObject.activeSelf == true && initialized == true && corState == false)
StartAllCoroutines();
}
/// /////////////////////////////////////////////////////////
/// Awake ops
/// /////////////////////////////////////////////////////////
// Initialize
public void Initialize()
{
// Deactivated
if (gameObject.activeSelf == false)
return;
// No children
if (transform.childCount == 0)
{
RayfireMan.Log (strRoot + name + " has no children. RigidRoot should be used on object with children.", gameObject);
return;
}
// Not initialized
if (initialized == false)
{
// Init Awake methods
AwakeMethods();
// Init sound
RFSound.InitializationSound(sound, cluster.bound.size.magnitude);
}
}
// Init connectivity if has
void InitConnectivity()
{
activation.cnt = GetComponent<RayfireConnectivity>();
if (activation.cnt != null)
{
activation.cnt.cluster.shards.Clear();
activation.cnt.rigidRootHost = this;
// Cached RigidRoot but no Connectivity
if (RayfireMan.debugStateStatic == true)
if (cached == true && activation.cnt.cluster.cachedHost == false)
RayfireMan.Log (strRoot + name + " object has Editor Setup but its connection data is not cached. Reset Setup and use Editor Setup again.", gameObject);
// Init connectivity
activation.cnt.Initialize();
// Clear shards list in Editor setup to avoid prefab double shard list
if (Application.isPlaying == false)
activation.cnt.cluster.shards.Clear();
}
// Warnings
if (RayfireMan.debugStateStatic == true)
{
if (activation.con == true && activation.cnt == null)
RayfireMan.Log (strRoot + name + " object has enabled Connectivity activation but has no Connectivity component.", gameObject);
if (activation.con == false && activation.cnt != null)
RayfireMan.Log (strRoot + name + " object has Connectivity component but activation by Connectivity is disabled.", gameObject);
}
}
// Reset object
public void ResetRigidRoot()
{
RFReset.RigidRootReset (this);
}
/// /////////////////////////////////////////////////////////
/// Setup
/// /////////////////////////////////////////////////////////
// Editor Setup
public void EditorSetup()
{
// Check if manager should be destroyed after setup
bool destroyMan = RayfireMan.inst == null;
// Create RayFire manager if not created
RayfireMan.RayFireManInit();
// Reset
ResetSetup();
// Set components
SetComponents();
// Set new cluster and set shards components
SetShards();
// Set shard colliders
SetColliders();
// Set unyielding shards
RayfireUnyielding.SetUnyielding(this);
// Init connectivity component.
InitConnectivity();
// Ignore collision. Editor mode
RFPhysic.SetIgnoreColliders(physics, cluster.shards);
// Destroy manager
if (destroyMan == true)
DestroyImmediate (RayfireMan.inst.transform.gameObject);
cached = true;
}
// Editor Reset. EDITOR only
public void ResetSetup()
{
/* TODO
// Reset MeshRoot
for (int i = 0; i < meshRoots.Count; i++)
{
meshRoots[i].rigidroot = null;
meshRoots[i].debrisList = null;
meshRoots[i].dustList = null;
}
for (int i = 0; i < rigids.Count; i++)
{
rigids[i].rigidroot = null;
rigids[i].debrisList = null;
rigids[i].dustList = null;
}
*/
// Reset connectivity shards
if (activation.cnt != null)
activation.cnt.ResetSetup();
activation.cnt = null;
// Destroy editor defined colliders
if (collidersList != null && collidersList.Count > 0)
{
collidersHash = new HashSet<Collider>(collidersList);
collidersList.Clear();
for (int i = 0; i < rigidRootShards.Count; i++)
if (rigidRootShards[i].col != null)
if (collidersHash.Contains (rigidRootShards[i].col) == true)
DestroyImmediate (rigidRootShards[i].col);
for (int i = 0; i < meshRootShards.Count; i++)
if (meshRootShards[i].col != null)
if (collidersHash.Contains (meshRootShards[i].col) == true)
DestroyImmediate (meshRootShards[i].col);
}
// Reset
cluster = new RFCluster();
inactiveShards = new List<RFShard>();
destroyShards = new List<RFShard>();
meshRoots = new List<RayfireRigid>();
connClusters = new List<RayfireRigid>();
physics.ign = null;
sound = null;
debrisList = null;
dustList = null;
unyList = null;
destroyShards = null;
cached = false;
// TODO Reset colliders
}
/// /////////////////////////////////////////////////////////
/// Init methods
/// /////////////////////////////////////////////////////////
// Awake ops
void AwakeMethods()
{
// Create RayFire manager if not created
RayfireMan.RayFireManInit();
// Objects null check
NullCheck();
// Set components
SetComponents();
// Set shards components
SetShards();
// Set shard colliders
SetColliders();
// Set colliders material
SetCollidersMaterial();
// Ignore collision
RFPhysic.SetIgnoreColliders (physics, cluster.shards);
// Set unyielding shards. Should be before SetPhysics to change simType
RayfireUnyielding.SetUnyielding(this);
// Set physics properties for shards
RFPhysic.SetPhysics (this);
// Set particles. After Physics set collider material
if (Application.isPlaying == true)
RFPoolingParticles.InitializeParticles (this);
// Setup list for activation. After set simState because collect Inactive and Kinematic
SetInactiveList ();
// Setup list with fade by offset shards // TODO add conn cls roots
RFFade.SetOffsetFadeList (this);
// Init Rigid shards
if (Application.isPlaying == true)
for (int i = 0; i < meshRigidShards.Count; i++)
meshRigidShards[i].rigid.Initialize();
// Start all necessary coroutines
StartAllCoroutines();
// Initialize connectivity
InitConnectivity();
// Object initialized
initialized = true;
// TODO Fade destroyShards
}
// Define basic components
void SetComponents()
{
tm = GetComponent<Transform>();
unyList = GetComponents<RayfireUnyielding>();
}
// Check MeshRoots
bool MeshRootCheck()
{
if (meshRoots != null && meshRoots.Count > 0)
for (int i = 0; i < meshRoots.Count; i++)
if (meshRoots[i] == null)
return false;
return true;
}
// Set shards components
void SetShards()
{
// Set lists
clusters = new List<RFCluster>();
// Already cached: set changed properties
if (cached == true)
{
// Custom Shards Lists
SetCustomShardsLists();
// Set simulation type
SetShardsSimulationType();
// Set parent list for all shards
SetParentList();
// Save tm
cluster.pos = tm.position;
cluster.rot = tm.rotation;
cluster.scl = tm.localScale;
return;
}
// Set lists
meshRoots = new List<RayfireRigid>();
connClusters = new List<RayfireRigid>();
destroyShards = new List<RFShard>();
// Set new cluster
cluster = new RFCluster
{
childClusters = new List<RFCluster>(),
pos = tm.position,
rot = tm.rotation,
scl = tm.localScale
};
// Get children
Transform[] children = new Transform[tm.childCount];
for (int i = 0; i < tm.childCount; i++)
children[i] = tm.GetChild (i);
// Convert children to shards
for (int i = 0; i < children.Length; i++)
{
// Skip inactive children
if (children[i].gameObject.activeSelf == false)
continue;
// Check if already has rigid
RayfireRigid rigid = children[i].gameObject.GetComponent<RayfireRigid>();
// Has no own rigid
if (rigid == null)
{
// Has no children. Collect as shard
if (children[i].childCount == 0)
AddShard (children[i]);
// Has children. Collect its children as shards
else
for (int m = 0; m < children[i].childCount; m++)
AddShard (children[i].GetChild (m));
}
// Has own rigid
else
{
// Set own rigidroot
rigid.rigidRoot = this;
rigid.reset.action = reset.action;
rigid.initialization = RayfireRigid.InitType.ByMethod;
// Mesh
if (rigid.objTp == ObjectType.Mesh)
AddMeshRigidShard (rigid);
// Mesh Root
else if (rigid.objTp == ObjectType.MeshRoot)
{
// Collect
meshRoots.Add (rigid);
// Bake getter properties
RFPhysic.BakeProperties (rigid.physics);
// Get mesh root children
List<Transform> meshRootChildren = new List<Transform>(rigid.transform.childCount);
for (int m = 0; m < rigid.transform.childCount; m++)
meshRootChildren.Add (rigid.transform.GetChild (m));
// Convert mesh root children to shards
for (int m = 0; m < meshRootChildren.Count; m++)
{
// Check if already has rigid
RayfireRigid meshRootRigid = meshRootChildren[m].GetComponent<RayfireRigid>();
// Has own rigid
if (meshRootRigid != null)
{
// Set own rigidroot
meshRootRigid.rigidRoot = this;
meshRootRigid.reset.action = reset.action;
meshRootRigid.initialization = RayfireRigid.InitType.ByMethod;
// Mesh
if (meshRootRigid.objTp == ObjectType.Mesh)
AddMeshRigidShard (meshRootRigid);
}
// Add MeshRoot children shard. Set MeshRoot as Rigid for shard to use its physics, activation, fade
else
AddShard (meshRootChildren[m], rigid);
}
}
// Connected cluster
else if (rigid.objTp == ObjectType.ConnectedCluster)
{
// Collect
connClusters.Add (rigid);
// Bake getter properties
RFPhysic.BakeProperties (rigid.physics);
// Disable runtime demolition TODO temp. Issues with later id change
// rigid.demolitionType = DemolitionType.None;
// Init
rigid.Initialize();
// Stop coroutines. Rigid Root runs own coroutines
// rigid.StopAllCoroutines();
// Set shards cls rigid
for (int r = 0; r < rigid.clsDemol.cluster.shards.Count; r++)
rigid.clsDemol.cluster.shards[r].rigid = rigid;
// Collect to all shards TODO create new shards
cluster.shards.AddRange (rigid.clsDemol.cluster.shards);
}
}
}
// Set shards id
for (int id = 0; id < cluster.shards.Count; id++)
cluster.shards[id].id = id;
// Custom Shards Lists
SetCustomShardsLists();
// Set simulation type. Should be before SetUnyielding because it changes simType.
SetShardsSimulationType();
// Set parent list for all shards
SetParentList();
// Set bound if has not
cluster.bound = RFCluster.GetShardsBound (cluster.shards);
}
// Set Custom Shards List
void SetCustomShardsLists()
{
rigidRootShards = new List<RFShard>();
meshRigidShards = new List<RFShard>();
meshRootShards = new List<RFShard>();
connClusterShards = new List<RFShard>();
for (int i = 0; i < cluster.shards.Count; i++)
if (cluster.shards[i].rigid == null)
rigidRootShards.Add (cluster.shards[i]);
else
{
if (cluster.shards[i].rigid.objTp == ObjectType.MeshRoot)
meshRootShards.Add (cluster.shards[i]);
else if (cluster.shards[i].rigid.objTp == ObjectType.Mesh)
meshRigidShards.Add (cluster.shards[i]);
else if (cluster.shards[i].rigid.objTp == ObjectType.ConnectedCluster)
connClusterShards.Add (cluster.shards[i]);
}
// Backup original layer in case shard will change layer after activation
RFActivation.BackupActivationLayer (this);
}
// Set physics properties
void SetShardsSimulationType()
{
// Set sim type in case of change
for (int i = 0; i < rigidRootShards.Count; i++)
rigidRootShards[i].sm = simTp;
for (int i = 0; i < meshRootShards.Count; i++)
meshRootShards[i].sm = meshRootShards[i].rigid.simTp;
for (int i = 0; i < meshRigidShards.Count; i++)
meshRigidShards[i].sm = meshRigidShards[i].rigid.simTp;
for (int i = 0; i < connClusterShards.Count; i++)
connClusterShards[i].sm = connClusterShards[i].rigid.simTp;
}
// Set parent list for all shards
void SetParentList()
{
parentList = new Transform[cluster.shards.Count];
for (int i = 0; i < cluster.shards.Count; i++)
parentList[i] = cluster.shards[i].tm.parent;
}
/// /////////////////////////////////////////////////////////
/// Add shards
/// /////////////////////////////////////////////////////////
// Add shard without rigid component
void AddShard(Transform shardTm, RayfireRigid rigid = null)
{
// Has children
if (shardTm.childCount > 0)
return;
// Create shard
RFShard shard = new RFShard (shardTm);
// Filter
if (ShardFilter(shard, this) == true)
{
// Set host rigid
shard.rigid = rigid;
// Collect
cluster.shards.Add (shard);
}
}
// Add shard with rigid component
void AddMeshRigidShard(RayfireRigid rigid)
{
// Disable runtime demolition TODO temp
rigid.dmlTp = DemolitionType.None;
// Init
rigid.Initialize();
// Stop coroutines. Rigid Root runs own coroutines
rigid.StopAllCoroutines();
// TODO check for exclude and missing components
// Collect
cluster.shards.Add (new RFShard (rigid));
}
/// /////////////////////////////////////////////////////////
/// Collider ops
/// /////////////////////////////////////////////////////////
// Define collider
void SetColliders()
{
// Add colliders if RigidRoot not cached
if (cached == false)
{
collidersList = new List<Collider>();
for (int i = 0; i < rigidRootShards.Count; i++)
RFPhysic.SetRigidRootCollider (this, physics, rigidRootShards[i]);
for (int i = 0; i < meshRootShards.Count; i++)
RFPhysic.SetRigidRootCollider (this, meshRootShards[i].rigid.physics, meshRootShards[i]);
collidersHash = new HashSet<Collider>(collidersList);
}
}
// Define components
void SetCollidersMaterial()
{
// Bake getter properties
RFPhysic.BakeProperties (physics);
// Set Collider material
for (int i = 0; i < rigidRootShards.Count; i++)
RFPhysic.SetColliderMaterial (physics, rigidRootShards[i]);
for (int i = 0; i < meshRootShards.Count; i++)
RFPhysic.SetColliderMaterial (meshRootShards[i].rigid.physics, meshRootShards[i]);
}
/// /////////////////////////////////////////////////////////
/// Activation ops
/// /////////////////////////////////////////////////////////
// Setup inactive shards
public void SetInactiveList()
{
if (inactiveShards == null)
inactiveShards = new List<RFShard>();
else
inactiveShards.Clear();
for (int s = 0; s < cluster.shards.Count; s++)
{
if (cluster.shards[s].InactiveOrKinematic == true)
{
cluster.shards[s].pos = cluster.shards[s].tm.position;
cluster.shards[s].rot = cluster.shards[s].tm.rotation;
cluster.shards[s].los = cluster.shards[s].tm.localPosition;
inactiveShards.Add (cluster.shards[s]);
}
}
}
// Start all coroutines
public void StartAllCoroutines()
{
// Stop if static
if (simTp == SimType.Static)
return;
// Inactive
if (gameObject.activeSelf == false)
return;
// Prevent physics cors
if (physics.exclude == true)
return;
// Init inactive every frame update coroutine TODO activation check per shard properties
if (inactiveShards.Count > 0)
StartCoroutine (activation.InactiveCor(this));
// Offset fade
if (offsetFadeShards.Count > 0)
{
fading.offsetEnum = RFFade.FadeOffsetCor (this);
StartCoroutine (fading.offsetEnum);
}
// All coroutines are running
corState = true;
}
/*
////////////////////////////////////////////////////////////
/// Children change
////////////////////////////////////////////////////////////
[NonSerialized] bool childrenChanged;
// Children change
void OnTransformChildrenChanged()
{
childrenChanged = 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;
}
*/
/// /////////////////////////////////////////////////////////
/// Static
/// /////////////////////////////////////////////////////////
// Copy rigid root properties to rigid
public void CopyPropertiesTo (RayfireRigid toScr)
{
// Set self as rigidRoot
toScr.rigidRoot = this;
// Object type
toScr.objTp = ObjectType.ConnectedCluster;
toScr.dmlTp = DemolitionType.None;
toScr.simTp = SimType.Dynamic;
// Copy physics
toScr.physics.CopyFrom (physics);
toScr.activation.CopyFrom (activation);
toScr.limitations.CopyFrom (dml.limitations);
// toScr.meshDemolition.CopyFrom (demolition.meshDemolition);
toScr.clsDemol.CopyFrom (dml.clsDemol);
// toScr.materials.CopyFrom (demolition.materials);
// toScr.damage.CopyFrom (damage);
toScr.fading.CopyFrom (fading);
toScr.reset.CopyFrom (reset, toScr.objTp);
}
/// /////////////////////////////////////////////////////////
/// Checks
/// /////////////////////////////////////////////////////////
// Check if root is nested cluster
static bool IsNestedCluster (Transform trans)
{
for (int c = 0; c < trans.childCount; c++)
if (trans.GetChild (c).childCount > 0)
return true;
return false;
}
// Objects null checks
void NullCheck()
{
// Cluster Integrity check
if (RFCluster.IntegrityCheck (cluster) == false)
{
RayfireMan.Log (strRoot + name + " has missing shards. Reset Setup and use Editor Setup again.", gameObject);
ResetSetup();
}
// MeshRoots check
if (MeshRootCheck() == false)
{
RayfireMan.Log (strRoot + name + " has missing Rigid component with MeshRoot object type. Reset Setup and use Editor Setup again.", gameObject);
ResetSetup();
}
// TODO Connected cluster check
}
// Shard filter
static bool ShardFilter(RFShard shard, RayfireRigidRoot scr)
{
// No mesh filter
if (shard.mf == null)
{
RayfireMan.Log (strRoot + shard.tm.name + " has no MeshFilter. Shard won't be simulated.", shard.tm.gameObject);
scr.destroyShards.Add (shard);
return false;
}
// No mesh
if (shard.mf.sharedMesh == null)
{
RayfireMan.Log (strRoot + shard.tm.name + " has no mesh. Shard won't be simulated.", shard.tm.gameObject);
scr.destroyShards.Add (shard);
return false;
}
// Low vert check
if (shard.mf.sharedMesh.vertexCount <= 3)
{
RayfireMan.Log (strRoot + shard.tm.name + " has 3 or less vertices. Shard can't get Mesh Collider and won't be simulated.", shard.tm.gameObject);
scr.destroyShards.Add (shard);
return false;
}
// Size check
if (RayfireMan.colliderSizeStatic > 0)
{
if (shard.sz < RayfireMan.colliderSizeStatic)
{
RayfireMan.Log (strRoot + shard.tm.name + " is very small and won't be simulated.", shard.tm.gameObject);
scr.destroyShards.Add (shard);
return false;
}
}
// Optional coplanar check
if (scr.physics.pc == true && shard.mf.sharedMesh.vertexCount < RayfireMan.coplanarVertLimit)
{
if (RFShatterAdvanced.IsCoplanar (shard.mf.sharedMesh, RFShatterAdvanced.planarThreshold) == true)
{
RayfireMan.Log (strRoot + shard.tm.name + " has planar low poly mesh. Shard can't get Mesh Collider and won't be simulated.", shard.tm.gameObject);
scr.destroyShards.Add (shard);
return false;
}
}
return true;
}
/// /////////////////////////////////////////////////////////
/// Getters
/// /////////////////////////////////////////////////////////
public bool HasClusters { get { return clusters != null && clusters.Count > 0; } }
public bool HasDebris { get { return debrisList != null && debrisList.Count > 0; } }
public bool HasDust { get { return dustList != null && dustList.Count > 0; } }
public bool HasUny { get { return unyList != null && unyList.Length > 0; } }
public void CollideTest()
{
/*
List<Transform> tmList = new List<Transform>();
for (int i = 0; i < transform.childCount; i++)
tmList.Add (transform.GetChild (i));
List<Collider> colliders = new List<Collider>();
foreach (var tm in tmList)
{
Collider col = tm.GetComponent<Collider>();
if (col == null)
{
col = tm.gameObject.AddComponent<MeshCollider>();
(col as MeshCollider).convex = true;
}
colliders.Add (col);
}
*/
// Physics.Simulate (0.01f);
// Physics.autoSimulation = true;
// Physics.autoSyncTransforms = false;
// https://forum.unity.com/threads/physics-simulate-for-a-single-object-possible.614404/
// https://forum.unity.com/threads/separating-physics-scenes.597697/
// https://stackoverflow.com/questions/50693509/can-we-detect-when-a-rigid-body-collides-using-physics-simulate-in-unity
}
}
}