ThrowBall/Assets/Plugins/RayFire/Scripts/Classes/RFShard.cs

630 lines
22 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using System;
using Random = UnityEngine.Random;
namespace RayFire
{
[Serializable]
public class RFShard : IComparable<RFShard>
{
// Main
public int id;
public float sz; // Size
public bool uny; // Unyielding
public bool act; // Activatable
public Bounds bnd;
public SimType sm;
public float dm;
// Neib info
public int nAm; // Initial amount of neibs
public List<int> nIds;
public List<float> nArea;
// Stress info
public List<int> sIds; // Shards id which support this shard
public List<float> nSt; // Dir; // 0.0 - UP; 0.5 - SIDE; 1.0 - DOWN; // Rat; // < 1 - Neib less; > 1 - Neib bigger
public float sSt; // Size stress multiplier
// Components
public Transform tm;
public MeshFilter mf;
public Collider col;
public Rigidbody rb;
public RayfireRigid rigid;
// Non Serialized
[NonSerialized] public Quaternion rot;
[NonSerialized] public Vector3 scl;
[NonSerialized] public Vector3 pos; // global position.
[NonSerialized] public Vector3 los; // local position. Used for Local offset activation
[NonSerialized] public float m; // Mass, calculates once
[NonSerialized] public int lb; // Layer backup to restore if shard has activation layer
[NonSerialized] public int fade; // 1-Living, 2-Fading, 3-Faded
[NonSerialized] public float fo; // Fade offset distance
[NonSerialized] public List<RFTriangle> tris;
[NonSerialized] public List<RFFace> poly;
[NonSerialized] public List<RFShard> neibShards;
[NonSerialized] public RFCluster cluster;
[NonSerialized] public bool check;
[NonSerialized] public bool[] sCheck;
/*
[NonSerialized] public float v; // Velocity activation
[NonSerialized] public float o; // Offset activation,
[NonSerialized] public int imp; // Impact activation
[NonSerialized] public int acv; // Activator activation,
*/
/// Static
public static float neibPosThreshold = 0.01f;
public static float neibAreaThreshold = 0.005f;
public static float bBoxSharedAreaDiv = 4f;
/// /////////////////////////////////////////////////////////
/// Constructor
/// /////////////////////////////////////////////////////////
// Constructor
public RFShard()
{
scl = Vector3.one;
sSt = 1f;
}
// Constructor
public RFShard(RFShard source)
{
// Main
id = source.id;
sz = source.sz;
uny = source.uny;
act = source.act;
bnd = source.bnd;
pos = source.pos;
rot = source.rot;
scl = source.scl;
sm = source.sm;
los = source.los;
// Neib
nAm = source.nAm;
if (source.nIds != null)
{
nIds = new List<int> (source.nIds.Count);
for (int i = 0; i < source.nIds.Count; i++)
nIds.Add (source.nIds[i]);
nArea = new List<float> (source.nArea.Count);
for (int i = 0; i < source.nArea.Count; i++)
nArea.Add (source.nArea[i]);
neibShards = new List<RFShard> (source.nArea.Count);
}
// Stress
if (source.sIds != null)
{
sIds = new List<int> (source.sIds.Count);
for (int i = 0; i < source.sIds.Count; i++)
sIds.Add (source.sIds[i]);
nSt = new List<float>();
for (int i = 0; i < source.nSt.Count; i++)
nSt.Add (source.nSt[i]);
sSt = source.sSt;
}
// Components
tm = source.tm;
col = source.col;
rb = source.rb;
rigid = source.rigid;
mf = source.mf;
// IMPORTANT: Cluster, neibShards init after all shards copied
}
// Constructor
public RFShard(Transform Tm)
{
// Get mesh filter
mf = Tm.GetComponent<MeshFilter>();
tm = Tm;
pos = Tm.position;
rot = Tm.rotation;
scl = Tm.localScale;
los = Tm.localPosition;
// Set bounds
Renderer mr = Tm.GetComponent<Renderer>();
if (mr != null)
bnd = mr.bounds;
sz = bnd.size.magnitude;
}
// Constructor
public RFShard(RayfireRigid scr)
{
sz = scr.limitations.bboxSize;
uny = scr.activation.uny;
act = scr.activation.atb;
bnd = scr.limitations.bound;
pos = scr.tsf.position;
rot = scr.tsf.rotation;
scl = scr.tsf.localScale;
los = scr.tsf.localPosition;
sm = scr.simTp;
tm = scr.tsf;
mf = scr.mFlt;
col = scr.physics.mc;
rb = scr.physics.rb;
rigid = scr;
}
// Compare by size
public int CompareTo(RFShard otherShard)
{
if (sz > otherShard.sz)
return -1;
if (sz < otherShard.sz)
return 1;
return 0;
}
/// /////////////////////////////////////////////////////////
/// Set Mesh data
/// /////////////////////////////////////////////////////////
// Set Triangles and Faces data
public static void SetMeshData(List<RFShard> shards, ConnectivityType type)
{
if (type != ConnectivityType.ByBoundingBox)
for (int i = 0; i < shards.Count; i++)
RFTriangle.SetTriangles (shards[i]);
if (type == ConnectivityType.ByPolygons || type == ConnectivityType.ByBoundingBoxAndPolygons)
for (int i = 0; i < shards.Count; i++)
RFFace.SetPolys (shards[i]);
}
// Set Triangles and Faces data
public static void SetMeshData(RFShard shard, ConnectivityType type)
{
if (type != ConnectivityType.ByBoundingBox)
RFTriangle.SetTriangles (shard);
if (type == ConnectivityType.ByPolygons || type == ConnectivityType.ByBoundingBoxAndPolygons)
RFFace.SetPolys (shard);
}
/// /////////////////////////////////////////////////////////
/// Set Shards
/// /////////////////////////////////////////////////////////
// Prepare shards. Set bounds, set neibs
public static void SetConnectedClusterShards(RFCluster cluster, ConnectivityType connectivity, bool setRigid = false)
{
// Get all children tms
Transform[] tmList = new Transform[cluster.tm.childCount];
for (int i = 0; i < cluster.tm.childCount; i++)
tmList[i] = cluster.tm.GetChild (i);
// Connected cluster with deep children TODO: reset empty roots.
// List<Transform> tmList = cluster.tm.GetComponentsInChildren<Transform>().ToList();
// Get child shards
SetShardsByTransformList (cluster, tmList, connectivity, setRigid);
}
// Prepare shards. Set bounds, set neibs
public static void SetShards(RFCluster cluster, ConnectivityType connectivity, bool setRigid = false)
{
// Get all children tms
Transform[] tmList = new Transform[cluster.tm.childCount];
for (int i = 0; i < cluster.tm.childCount; i++)
tmList[i] = cluster.tm.GetChild (i);
// Connected cluster with deep children TODO: reset empty roots.
// List<Transform> tmList = cluster.tm.GetComponentsInChildren<Transform>().ToList();
// Get child shards
SetShardsByTransformList (cluster, tmList, connectivity, setRigid);
}
// Prepare shards. Set bounds, set neibs. For Connected Cluster
public static void SetShardsByTransformList(RFCluster cluster, Transform[] tmList, ConnectivityType connectivity, bool setRigid = false)
{
cluster.shards = new List<RFShard> (tmList.Length);
for (int i = 0; i < tmList.Length; i++)
{
// Create new shard
RFShard shard = new RFShard (tmList[i]);
// Child has no mesh
if (shard.mf == null || shard.mf.sharedMesh == null)
continue;
shard.id = i;
shard.cluster = cluster;
// Set mesh data
SetMeshData (shard, connectivity);
// Collect shard
cluster.shards.Add (shard);
}
// Set rigid component
if (setRigid == true)
for (int i = 0; i < cluster.shards.Count; i++)
cluster.shards[i].rigid = cluster.shards[i].tm.GetComponent<RayfireRigid>();
}
// Prepare shards. Set bounds, set neibs. For Mesh Root
public static void SetShardsByRigidList(RFCluster cluster, List<RayfireRigid> rigids, ConnectivityType connectivity)
{
for (int i = 0; i < rigids.Count; i++)
{
// Create new shard
RFShard shard = new RFShard (rigids[i])
{
cluster = cluster,
id = i
};
// Set mesh data
SetMeshData (shard, connectivity);
// Collect shard
cluster.shards.Add (shard);
}
}
/// /////////////////////////////////////////////////////////
/// Neibs
/// /////////////////////////////////////////////////////////
// Get shared area with another shard TODO add pos difference check
static float NeibAreaByPoly(RFShard thisShard, RFShard otherShard)
{
float areaDif;
float area = 0f;
for (int i = 0; i < thisShard.poly.Count; i++)
{
for (int j = 0; j < otherShard.poly.Count; j++)
{
// Area check
areaDif = Mathf.Abs (thisShard.poly[i].area - otherShard.poly[j].area);
if (areaDif < neibAreaThreshold)
{
// Normal check
if (thisShard.poly[i].normal == -otherShard.poly[j].normal)
{
area += thisShard.poly[i].area;
break;
}
}
}
}
return area;
}
// Get shared area with another shard
static float NeibAreaByTris(RFShard thisShard, RFShard otherShard)
{
float posDif;
float areaDif;
float area = 0f;
for (int i = 0; i < thisShard.tris.Count; i++)
{
for (int j = 0; j < otherShard.tris.Count; j++)
{
// Area check
areaDif = Mathf.Abs (thisShard.tris[i].area - otherShard.tris[j].area);
if (areaDif < neibAreaThreshold)
{
// Position check
posDif = Vector3.Distance (thisShard.tris[i].pos, otherShard.tris[j].pos);
if (posDif < neibPosThreshold)
{
area += thisShard.tris[i].area;
break;
}
}
}
}
return area;
}
// Set shard neibs
public static void SetShardNeibs(List<RFShard> shards, ConnectivityType type, float minArea = 0, float minSize = 0, int perc = 0, int seed = 0)
{
// Set list
for (int i = 0; i < shards.Count; i++)
{
shards[i].neibShards = new List<RFShard>();
shards[i].nArea = new List<float>();
shards[i].nIds = new List<int>();
shards[i].nAm = 0;
}
//float t1 = Time.realtimeSinceStartup;
// Random of fixed connections seed
if (seed > 0)
Random.InitState (seed);
// Set neib and area info
float area;
for (int i = 0; i < shards.Count; i++)
{
// Skip by size
if (minSize > 0 && shards[i].sz < minSize)
continue;
for (int s = 0; s < shards.Count; s++)
{
// Skip itself
if (s == i)
continue;
// Skip by size
if (minSize > 0 && shards[s].sz < minSize)
continue;
// Set random state for same pair
if (perc > 0 && Random.Range (0, 100) < perc)
continue;
// Check if shard was not added as neib before
if (shards[s].nIds.Contains (shards[i].id) == false)
{
// Bounding box intersection check
if (shards[i].bnd.Intersects (shards[s].bnd) == true)
{
// Get areas
area = 0;
if (type == ConnectivityType.ByBoundingBox)
area = (shards[i].sz + shards[s].sz) / bBoxSharedAreaDiv;
else if (type == ConnectivityType.ByTriangles || type == ConnectivityType.ByBoundingBoxAndTriangles)
area = NeibAreaByTris (shards[i], shards[s]);
else if (type == ConnectivityType.ByPolygons || type == ConnectivityType.ByBoundingBoxAndPolygons)
area = NeibAreaByPoly (shards[i], shards[s]);
// Area still 0, get by bounds if ByBoundingBoxAndMesh
if (area == 0)
if (type == ConnectivityType.ByBoundingBoxAndTriangles || type == ConnectivityType.ByBoundingBoxAndPolygons)
area = (shards[i].sz + shards[s].sz) / bBoxSharedAreaDiv;
// Skip low area neibs TODO filter after all connected, leave one biggest ??
if (minArea > 0 && area < minArea)
continue;
// Collect
if (area > 0)
{
// Roundup for 3 digits after comma
area = (int)(area * 1000.0f) / 1000.0f;
shards[i].neibShards.Add (shards[s]);
shards[i].nArea.Add (area);
shards[i].nIds.Add (shards[s].id);
shards[s].neibShards.Add (shards[i]);
shards[s].nArea.Add (area);
shards[s].nIds.Add (shards[i].id);
}
}
}
}
// Set original neib amount to know if neibs was removed
shards[i].nAm = shards[i].nIds.Count;
}
//float t2 = Time.realtimeSinceStartup;
//Debug.Log ("Time " + (t2 - t1));
//Debug.Log ("Checks " + checks);
// Clear triangles data
if (type == ConnectivityType.ByTriangles || type == ConnectivityType.ByBoundingBoxAndTriangles)
for (int i = 0; i < shards.Count; i++)
RFTriangle.Clear (shards[i]);
if (type == ConnectivityType.ByPolygons || type == ConnectivityType.ByBoundingBoxAndPolygons)
for (int i = 0; i < shards.Count; i++)
RFFace.Clear (shards[i]);
}
// Remove neib shards which are not in current cluster anymore
public static void ReinitNeibs(List<RFShard> shards)
{
if (shards.Count > 0)
// Remove detach shards from neib. Edit neib shards data
for (int i = 0; i < shards.Count; i++)
// Check very neib shard
for (int n = shards[i].neibShards.Count - 1; n >= 0; n--)
// Neib shard was detached
if (shards[i].neibShards[n].cluster != shards[i].cluster)
shards[i].RemoveNeibAt (n);
}
// Clear neib lists
public void RemoveNeibAt(int ind)
{
nIds.RemoveAt (ind);
nArea.RemoveAt (ind);
neibShards.RemoveAt (ind);
if (StressState == true)
{
nSt.RemoveAt (ind * 3 + 2);
nSt.RemoveAt (ind * 3 + 1);
nSt.RemoveAt (ind * 3);
}
}
// Clear neib lists
public void ClearNeib()
{
nIds.Clear();
nArea.Clear();
neibShards.Clear();
if (StressState == true)
nSt.Clear();
}
// Set positive id for shards for checks
public static void SetUnchecked(List<RFShard> shards)
{
for (int i = 0; i < shards.Count; i++)
shards[i].check = false;
}
/// /////////////////////////////////////////////////////////
/// Slice
/// /////////////////////////////////////////////////////////
// Get slice plane at middle of longest bound edge
public static Plane GetSlicePlane(Bounds bound)
{
Vector3 normal;
Vector3 size = bound.size;
Vector3 point = bound.center;
if (size.x >= size.y && size.x >= size.z)
normal = Vector3.right;
else if (size.y >= size.x && size.y >= size.z)
normal = Vector3.up;
else
normal = Vector3.forward;
return new Plane (normal, point);
}
/// /////////////////////////////////////////////////////////
/// Sort by distance
/// /////////////////////////////////////////////////////////
// Sort list by distance to point
public static RFShard[] SortByDistanceToPoint(RFShard[] shards, Vector3 point, int amount)
{
int shardsCount = shards.Length;
float[] distances = new float[shardsCount];
for (int s = 1; s < shardsCount; s++)
distances[s] = Vector3.Distance (point, shards[s].tm.position);
Array.Sort (distances, shards);
Array.Resize (ref shards, amount);
return shards;
}
// Sort list by distance to point
public static RFShard[] SortByDistanceToPlane(RFShard[] shards, Vector3 point, Vector3 normal, int amount)
{
int shardsCount = shards.Length;
float[] distances = new float[shardsCount];
Plane plane = new Plane (normal, point);
for (int s = 1; s < shardsCount; s++)
distances[s] = Math.Abs (plane.GetDistanceToPoint (shards[s].tm.position));
Array.Sort (distances, shards);
Array.Resize (ref shards, amount);
return shards;
}
/// /////////////////////////////////////////////////////////
/// Set Unyielding
/// /////////////////////////////////////////////////////////
// Check if cluster has unyielding shards
public static bool UnyieldingByShard(List<RFShard> shards)
{
for (int i = 0; i < shards.Count; i++)
if (shards[i].uny == true)
return true;
return false;
}
// Check if cluster has unyielding shards
public static bool AllUnyShards(List<RFShard> shards)
{
for (int i = 0; i < shards.Count; i++)
if (shards[i].uny == false)
return false;
return true;
}
/// /////////////////////////////////////////////////////////
/// Getters
/// /////////////////////////////////////////////////////////
// Has supported shards
public bool StressState
{
get
{
return nSt != null && nSt.Count > 0;
}
}
// Has supported shards
public bool SupportState
{
get
{
return sIds.Count > 0;
}
}
// Activatable type
public bool InactiveOrKinematic
{
get
{
return (sm == SimType.Inactive || sm == SimType.Kinematic);
}
}
// Check if visible
public bool Visible
{
get
{
if (tm != null)
{
MeshRenderer mr = tm.GetComponent<MeshRenderer>();
if (mr != null)
return mr.isVisible;
}
return false;
}
}
/// /////////////////////////////////////////////////////////
/// Static
/// /////////////////////////////////////////////////////////
// Get biggest cluster
public static RFShard GetShardByCollider(List<RFShard> shards, Collider collider)
{
for (int i = 0; i < shards.Count; i++)
if (shards[i].col == collider)
return shards[i];
return null;
}
// Get biggest cluster
public static List<RFShard> GetShardsByColliders(List<RFShard> shards, List<Collider> colliders)
{
List<RFShard> colliderShards = new List<RFShard>();
HashSet<Collider> collidersHash = new HashSet<Collider> (colliders);
for (int i = 0; i < shards.Count; i++)
if (collidersHash.Contains (shards[i].col) == true)
colliderShards.Add (shards[i]);
return colliderShards;
}
}
}