Files
2025-05-29 22:31:40 +03:00

928 lines
34 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.AnimationTools
{
public static class SkeletonRecognize
{
public enum EWhatIsIt
{
Unknown, Humanoidal, Quadroped, Creature
}
public class SkeletonInfo
{
public Transform AnimatorTransform;
public float LowestVsHighestLen;
public float MostLeftVsMostRightLen;
public float MostForwVsMostBackLen;
public float AverageLen;
public Transform ProbablyRootBone;
public Transform ProbablyHips;
public Transform ProbablyChest;
public Transform ProbablyHead;
public List<Transform> TrReachingGround = new List<Transform>();
public List<Transform> TrReachingSides = new List<Transform>();
public List<Transform> TrEnds = new List<Transform>();
public List<Transform> ProbablySpineChain = new List<Transform>();
public List<Transform> ProbablySpineChainShort = new List<Transform>();
public List<List<Transform>> ProbablyRightArms = new List<List<Transform>>();
public List<List<Transform>> ProbablyLeftArms = new List<List<Transform>>();
public List<List<Transform>> ProbablyLeftLegs = new List<List<Transform>>();
public List<Transform> ProbablyLeftLegRoot = new List<Transform>();
public List<List<Transform>> ProbablyRightLegs = new List<List<Transform>>();
public List<Transform> ProbablyRightLegRoot = new List<Transform>();
public Vector3 LocalSpaceHighest = Vector3.zero;
public Vector3 LocalSpaceMostRight = Vector3.zero;
public Vector3 LocalSpaceMostForward = Vector3.zero;
public Vector3 LocalSpaceMostBack = Vector3.zero;
public Vector3 LocalSpaceMostLeft = Vector3.zero;
public Vector3 LocalSpaceLowest = Vector3.zero;
public int SpineChainLength { get { return ProbablySpineChain.Count; } }
public int LeftArms { get { return ProbablyLeftArms.Count; } }
public int LeftLegs { get { return ProbablyLeftLegs.Count; } }
public int RightArms { get { return ProbablyRightArms.Count; } }
public int RightLegs { get { return ProbablyRightLegs.Count; } }
public int Legs { get { return RightLegs + LeftLegs; } }
public int Arms { get { return LeftArms + RightArms; } }
public EWhatIsIt WhatIsIt = EWhatIsIt.Unknown;
public SkeletonInfo(Transform t, List<Transform> checkOnly = null, Transform pelvisHelp = null)
{
AnimatorTransform = t;
Transform[] childTransforms;
if (checkOnly != null)
{
childTransforms = new Transform[checkOnly.Count];
for (int i = 0; i < checkOnly.Count; i++)
{
childTransforms[i] = checkOnly[i];
}
}
else
childTransforms = AnimatorTransform.GetComponentsInChildren<Transform>(true);
if (childTransforms.Length > 0)
{
Vector3 l = AnimatorTransform.InverseTransformPoint(childTransforms[0].position);
LocalSpaceHighest = l;
LocalSpaceMostRight = l;
LocalSpaceMostForward = l;
LocalSpaceMostBack = l;
LocalSpaceMostLeft = l;
LocalSpaceLowest = l;
}
List<Transform> childT = new List<Transform>();
for (int i = 0; i < childTransforms.Length; i++)
{
Transform c = childTransforms[i];
SkinnedMeshRenderer skin = c.GetComponentInChildren<SkinnedMeshRenderer>();
if (skin != null) continue;
childT.Add(c);
}
//UnityEngine.Debug.Log("ChildTransforms = " + childTransforms.Length);
#region Defining Skeleton Bounds Guides
for (int i = 0; i < childT.Count; i++)
{
Transform c = childT[i];
if (c.GetComponent<SkinnedMeshRenderer>()) continue;
Vector3 local = AnimatorTransform.InverseTransformPoint(c.position);
if (local.x > LocalSpaceMostRight.x) LocalSpaceMostRight = local;
else if (local.x < LocalSpaceMostLeft.x) LocalSpaceMostLeft = local;
if (local.z > LocalSpaceMostForward.z) LocalSpaceMostForward = local;
else if (local.z < LocalSpaceMostBack.z) LocalSpaceMostBack = local;
if (local.y > LocalSpaceHighest.y) LocalSpaceHighest = local;
else if (local.y < LocalSpaceLowest.y) LocalSpaceLowest = local;
}
#endregion
// Helper Measures
LowestVsHighestLen = Mathf.Abs(LocalSpaceLowest.y - LocalSpaceHighest.y);
MostLeftVsMostRightLen = Mathf.Abs(LocalSpaceMostLeft.x - LocalSpaceMostRight.x);
MostForwVsMostBackLen = Mathf.Abs(LocalSpaceMostForward.z - LocalSpaceMostBack.z);
AverageLen = (LowestVsHighestLen + MostLeftVsMostRightLen + MostForwVsMostBackLen) / 3f;
float limbMinimumLength = LowestVsHighestLen * 0.55f;
#region Initial finding name based
// arms
for (int c = 0; c < childT.Count; c++)
{
Transform ct = childT[c];
if (NameContains(ct.name, ShouldersNames))
{
Transform getCh = GetBottomMostChildTransform(ct);
if (NotContainedYetByLimbs(getCh)) TrReachingSides.Add(getCh);
}
else
{
if (NameContains(ct.name, ElbowNames))
{
Transform getCh = GetBottomMostChildTransform(ct);
if (NotContainedYetByLimbs(getCh)) TrReachingSides.Add(getCh);
}
}
}
// legs
for (int c = 0; c < childT.Count; c++)
{
Transform ct = childT[c];
if (NameContains(ct.name, UpperLegNames))
{
Transform getCh = GetBottomMostChildTransform(ct);
if (NotContainedYetByLimbs(getCh)) TrReachingGround.Add(getCh);
}
else
{
if (NameContains(ct.name, KneeNames))
{
Transform getCh = GetBottomMostChildTransform(ct);
if (NotContainedYetByLimbs(getCh)) TrReachingGround.Add(getCh);
}
}
}
// pelvis
bool hipsByName = false;
for (int c = 0; c < childT.Count; c++)
{
Transform ct = childT[c];
if (NameContains(ct.name, PelvisNames))
{
hipsByName = true;
ProbablyHips = ct;
break;
}
}
// chest
bool chestByName = false;
for (int c = 0; c < childT.Count; c++)
{
Transform ct = childT[c];
if (NameContains(ct.name, ChestNames))
{
chestByName = true;
ProbablyChest = ct;
break;
}
}
// head
bool headByName = false;
for (int c = 0; c < childT.Count; c++)
{
Transform ct = childT[c];
if (NameContains(ct.name, HeadNames))
{
headByName = true;
ProbablyHead = ct;
break;
}
}
if (ProbablyHead != null)
if (ProbablyHips != null)
{
if (IsChildOf(ProbablyHead, ProbablyHips) == false)
{
ProbablyHead = null;
}
}
// root
for (int c = 0; c < childT.Count; c++)
{
Transform ct = childT[c];
if (NameContains(ct.name, RootNames))
{
ProbablyRootBone = ct;
break;
}
}
#endregion
#region Defining End Transforms for Arms / Legs / Head
if (childT.Count > 2)
{
for (int i = 1; i < childT.Count; i++)
{
Transform tr = childT[i];
if (tr.childCount == 0)
{
TrEnds.Add(tr);
Vector3 l = Loc(tr);
if (l.y < LocalSpaceLowest.y + LowestVsHighestLen * 0.1f)
{
if (NotContainedYetByLimbs(tr)) TrReachingGround.Add(tr);
}
else
{
if (l.y > LocalSpaceLowest.y + LowestVsHighestLen * 0.2f)
{
if (l.x < MostLeftVsMostRightLen * -0.1f || l.x > MostLeftVsMostRightLen * 0.1f)
{
if (NotContainedYetByLimbs(tr)) TrReachingSides.Add(tr);
}
}
}
}
}
}
#endregion
#region Chest Basing on Left / Right Sides Limbs
if (!chestByName)
{
List<Transform> probablyChestOnes = new List<Transform>();
for (int i = 0; i < TrReachingSides.Count; i++)
{
if (childT[i].GetComponent<SkinnedMeshRenderer>()) continue;
Transform par = TrReachingSides[i].parent;
while (par != null)
{
if (par.childCount > 2)
{
Vector3 loc = Loc(par);
if (loc.x > -MostLeftVsMostRightLen * 0.03f && loc.x < MostLeftVsMostRightLen * 0.03f)
{
probablyChestOnes.Add(par);
break;
}
}
par = par.parent;
}
}
if (probablyChestOnes.Count == 1) ProbablyChest = probablyChestOnes[0];
else if (probablyChestOnes.Count > 1)
{
if (probablyChestOnes[0] == probablyChestOnes[1])
ProbablyChest = probablyChestOnes[0];
}
}
#endregion
#region Pelvis Basing On Left / Right Low Limbs
if (!hipsByName)
{
List<Transform> probablyHipsOnes = new List<Transform>();
for (int i = 0; i < TrReachingGround.Count; i++)
{
Transform par = TrReachingGround[i].parent;
while (par != null)
{
if (par.childCount > 2)
{
Vector3 loc = Loc(par);
if (loc.y > LocalSpaceLowest.y + LowestVsHighestLen * 0.04f)
if (loc.x > -MostLeftVsMostRightLen * 0.02f && loc.x < MostLeftVsMostRightLen * 0.02f)
{
probablyHipsOnes.Add(par);
break;
}
}
par = par.parent;
}
}
if (probablyHipsOnes.Count == 1) ProbablyChest = probablyHipsOnes[0];
else if (probablyHipsOnes.Count > 1)
{
if (probablyHipsOnes[0] == probablyHipsOnes[1])
ProbablyHips = probablyHipsOnes[0];
}
}
if (ProbablyHips == null) ProbablyHips = pelvisHelp;
#endregion
#region correcting chest if required
if (ProbablyChest == null || ProbablyChest == ProbablyHips || (ProbablyHips != null && IsChildOf(ProbablyChest, ProbablyHips) == false))
{
if (ProbablyHips) if (ProbablyHead)
{
Transform checkT = ProbablyHead.parent;
bool found = false;
while (checkT.parent != null && checkT.parent != ProbablyHips)
{
if (checkT.childCount > 2)
{
// Check if some side limbs are child bones of chest check bone
for (int s = 0; s < TrReachingSides.Count; s++)
{
Transform side = TrReachingSides[s];
if (IsChildOf(side, checkT))
{
found = true;
break;
}
}
}
if (found) break;
checkT = checkT.parent;
}
if (found) ProbablyChest = checkT;
}
}
if (ProbablyHips == null) ProbablyHips = pelvisHelp;
#endregion
// Probably correctly detected chest and hips
if (ProbablyChest && ProbablyHips)
{
if (MostForwVsMostBackLen > LowestVsHighestLen * 0.9f) // If forward legth is bigger than model's height
{
// In most cases chest is more in front than hips
if (Loc(ProbablyChest).z < Loc(ProbablyHips).z) // Chest is behind hips - swap!
{
Transform swap = ProbablyChest;
ProbablyChest = ProbablyHips;
ProbablyHips = swap;
UnityEngine.Debug.Log("Hips - Chest - Reversed Detection Swap!");
}
}
#region Trying To Detect Head
if (!headByName)
{
Vector3 highestForHead = Vector3.zero;
for (int c = 0; c < ProbablyChest.childCount; c++)
{
// checking all probably chest child transforms
Transform ch = ProbablyChest.GetChild(c);
Vector3 lc;
if (ch.childCount > 0) // Going through
{
for (int c2 = 0; c2 < ch.childCount; c2++)
{
Transform ch2 = ch.GetChild(c2);
lc = Loc(ch2);
if (lc.x > -MostLeftVsMostRightLen * 0.04f && lc.x < MostLeftVsMostRightLen * 0.04f)
{
if (Loc(ch2).y > highestForHead.y)
{ highestForHead = Loc(ch2); ProbablyHead = ch2; }
}
}
}
lc = Loc(ch);
if (lc.x > -MostLeftVsMostRightLen * 0.04f && lc.x < MostLeftVsMostRightLen * 0.04f)
if (lc.y > highestForHead.y)
{ highestForHead = Loc(ch); ProbablyHead = ch; }
}
if (ProbablyChest && ProbablyHead && ProbablyHips)
{
float chestToPelvis = Vector3.Distance(Loc(ProbablyChest), Loc(ProbablyHips));
if ((ProbablyChest.childCount < 3 || chestToPelvis < AverageLen * 0.12f) && ProbablyHead.childCount > 1)
{
ProbablyChest = ProbablyHead;
ProbablyHead = GetHighestChild(ProbablyHead, AnimatorTransform, MostLeftVsMostRightLen * 0.05f);
if (ProbablyHead == ProbablyChest) ProbablyHead = ProbablyChest.GetChild(0);
}
}
}
#endregion
#region Eliminating wrong detected arms (it can be ear bones)
if (ProbablyHead)
{
for (int i = TrReachingSides.Count - 1; i >= 0; i--)
{
if (IsChildOf(TrReachingSides[i], ProbablyHead)) TrReachingSides.RemoveAt(i);
}
}
for (int i = TrReachingSides.Count - 1; i >= 0; i--)
{
if (GetDepth(TrReachingSides[i], AnimatorTransform) < 5)
{
TrReachingSides.RemoveAt(i);
}
}
#endregion
#region Detecting Spine Chain
Transform headC = null;
if (ProbablyHead)
{
//if (ProbablyHead.parent) headC = ProbablyHead.parent;
ProbablySpineChain.Add(ProbablyHead);
headC = ProbablyHead.parent;
}
while (headC != null && headC != ProbablyHips)
{
ProbablySpineChain.Add(headC);
headC = headC.parent;
}
ProbablySpineChain.Reverse();
for (int i = 0; i < Mathf.Min(4, ProbablySpineChain.Count); i++)
{
ProbablySpineChainShort.Add(ProbablySpineChain[i]);
}
#endregion
#region Detecting Legs
List<Transform> confirmedLegs = new List<Transform>();
for (int i = 0; i < TrReachingGround.Count; i++)
{
Transform start = TrReachingGround[i];
Vector3 startLoc = Loc(start);
List<Transform> fullChain = new List<Transform>();
Transform untilHips = start;
while (untilHips != null && (untilHips != ProbablyHips && untilHips != ProbablyChest))
{
fullChain.Add(untilHips);
untilHips = untilHips.parent;
}
if (fullChain.Count >= 3)
{
List<Transform> legChain = new List<Transform>();
legChain.Add(fullChain[fullChain.Count - 1]);
legChain.Add(fullChain[fullChain.Count - 2]);
legChain.Add(fullChain[fullChain.Count - 3]);
confirmedLegs.Add(start);
if (startLoc.x < MostLeftVsMostRightLen * 0.02f)
{
ProbablyLeftLegs.Add(legChain);
ProbablyLeftLegRoot.Add(untilHips);
}
else
{
ProbablyRightLegs.Add(legChain);
ProbablyRightLegRoot.Add(untilHips);
}
}
}
#endregion
#region Detecting Arms
for (int i = 0; i < TrReachingSides.Count; i++)
{
Transform start = TrReachingSides[i];
Vector3 startLoc = Loc(start);
List<Transform> fullChain = new List<Transform>();
Transform untilChest = start;
while (untilChest != null && untilChest != ProbablyChest)
{
fullChain.Add(untilChest);
untilChest = untilChest.parent;
}
if (fullChain.Count >= 4)
{
List<Transform> armChain = new List<Transform>();
armChain.Add(fullChain[fullChain.Count - 1]);
armChain.Add(fullChain[fullChain.Count - 2]);
armChain.Add(fullChain[fullChain.Count - 3]);
armChain.Add(fullChain[fullChain.Count - 4]);
if (startLoc.x < MostLeftVsMostRightLen * 0.02f)
ProbablyLeftArms.Add(armChain);
else
ProbablyRightArms.Add(armChain);
}
}
#endregion
#region Removing Duplicates (resulting by fingers counts)
ClearDuplicates(ProbablyLeftArms, null);
ClearDuplicates(ProbablyRightArms, null);
ClearDuplicates(ProbablyLeftLegs, ProbablyLeftLegRoot);
ClearDuplicates(ProbablyRightLegs, ProbablyRightLegRoot);
#endregion
if (Legs == 2 && Arms == 2)
{
WhatIsIt = EWhatIsIt.Humanoidal;
}
else if (Legs == 4 && Arms == 0)
{
WhatIsIt = EWhatIsIt.Quadroped;
}
else if (Legs > 0 || Arms > 0)
{
WhatIsIt = EWhatIsIt.Creature;
}
else
{
WhatIsIt = EWhatIsIt.Unknown;
}
}
float middleHeight = Mathf.Lerp(LocalSpaceLowest.y, LocalSpaceHighest.y, 0.5f);
UnityEngine.Debug.DrawLine(t.TransformPoint(new Vector3(LocalSpaceMostLeft.x, LocalSpaceHighest.y, LocalSpaceMostForward.z)), t.TransformPoint(new Vector3(LocalSpaceMostLeft.x, LocalSpaceLowest.y, LocalSpaceMostForward.z)), Color.green, 12);
UnityEngine.Debug.DrawLine(t.TransformPoint(new Vector3(LocalSpaceMostLeft.x, middleHeight, LocalSpaceMostForward.z)), t.TransformPoint(new Vector3(LocalSpaceMostRight.x, middleHeight, LocalSpaceMostForward.z)), Color.red, 12);
UnityEngine.Debug.DrawLine(t.TransformPoint(new Vector3(LocalSpaceMostRight.x, middleHeight, LocalSpaceMostForward.z)), t.TransformPoint(new Vector3(LocalSpaceMostRight.x, middleHeight, LocalSpaceMostBack.z)), Color.blue, 12);
}
bool NotContainedYetByAny(Transform t)
{
return (!TrReachingSides.Contains(t) && !TrReachingGround.Contains(t) && !TrEnds.Contains(t)
&& t != ProbablyChest && t != ProbablyHips && t != ProbablyHead && t != ProbablyChest && t != ProbablyRootBone && t != AnimatorTransform);
}
bool NotContainedYetByLimbs(Transform t)
{
return (!TrReachingSides.Contains(t) && !TrReachingGround.Contains(t));
}
public Transform GetHighestChild(Transform t, Transform root, float inCenterRangeFactor)
{
if (t == null) return null;
Transform highT = t;
Vector3 highest = root.InverseTransformPoint(t.position);
foreach (var ct in t.GetComponentsInChildren<Transform>(true))
{
Vector3 pos = root.InverseTransformPoint(ct.position);
if (pos.x > -inCenterRangeFactor && pos.x < inCenterRangeFactor)
if (pos.y > highest.y)
{
highest.y = pos.y;
highT = ct;
}
}
return highT;
}
//float ComputeLength(Transform p, int parentBack)
//{
// float len = 0f;
// if (p != null)
// for (int i = 0; i < parentBack; i++)
// {
// if (p.parent != null)
// {
// len += Vector3.Distance(p.position, p.parent.position);
// p = p.parent;
// }
// else
// break;
// }
// return len;
//}
void ClearDuplicates(List<List<Transform>> limbs, List<Transform> roots)
{
if (limbs.Count > 1)
{
for (int main = 0; main < limbs.Count; main++) // Checking all limb chains
{
if (main >= limbs.Count) return;
var limb = limbs[main];
// Checking if some other limbs contains duplicate bones of each other
// It can be caused by finger bones - how many fingers -> that many hands detected
for (int i = limbs.Count - 1; i >= 0; i--)
{
if (i == main) continue; // Don't check self
var otherLimb = limbs[i];
bool remove = false;
for (int p = 0; p < otherLimb.Count; p++)
{
if (limb.Contains(otherLimb[p]))
{
remove = true;
break;
}
}
if (remove)
{
limbs.RemoveAt(i);
}
}
}
}
}
Vector3 Loc(Transform t)
{
return AnimatorTransform.InverseTransformPoint(t.position);
}
#region Debug Log Report
public string GetLog()
{
string log = "< " + AnimatorTransform.name + " >\n";
log += "\nGenerate Guides:\n";
log += "Highest: " + LocalSpaceHighest + " ";
log += "Lowest: " + LocalSpaceLowest + " ";
log += "Left: " + LocalSpaceMostLeft + " ";
log += "Right: " + LocalSpaceMostRight + " ";
log += "Forward: " + LocalSpaceMostForward + " ";
log += "Back: " + LocalSpaceMostBack + " ";
log += "\n\nGenerated Helper Measurements: \n";
log += "UpDown: " + LowestVsHighestLen + " ";
log += "LeftRight: " + MostLeftVsMostRightLen + " ";
log += "ForwBack: " + MostForwVsMostBackLen + " ";
log += "Avr: " + AverageLen + " ";
log += "\n\nDetected Propabilities: \n";
log += "ProbablyHips: " + ProbablyHips + " ";
log += "ProbablyChest: " + ProbablyChest + " ";
log += "ProbablyHead: " + ProbablyHead + " ";
log += "\n\nLimb End Detections: \n";
log += "Reaching Ground: " + TrReachingGround.Count + " ";
log += "Reaching Sides: " + TrReachingSides.Count + " ";
log += "Spine Chain Length: " + ProbablySpineChain.Count + " (" + ProbablySpineChainShort.Count + ") ";
log += "\n\nDetected Propabilities: \n";
log += "Probably Left Arms: " + ProbablyLeftArms.Count + " ";
log += "Probably Right Arms: " + ProbablyRightArms.Count + " ";
log += "Probably Left Legs: " + ProbablyLeftLegs.Count + " ";
log += "Probably Right Legs: " + ProbablyRightLegs.Count + " ";
log += "\n\n\nTr Ends: \n";
for (int i = 0; i < TrEnds.Count; i++)
{
if (TrEnds[i] == null) continue;
log += TrEnds[i].name + " ";
}
log += "\n\nTr Reaching Ground: \n";
for (int i = 0; i < TrReachingGround.Count; i++)
{
if (TrReachingGround[i] == null) continue;
log += TrReachingGround[i].name + " ";
}
log += "\n\nTr Reaching Sides: \n";
for (int i = 0; i < TrReachingSides.Count; i++)
{
if (TrReachingSides[i] == null) continue;
log += TrReachingSides[i].name + " ";
}
if (ProbablyLeftArms.Count > 0)
{
log += "\n\nDebug Left Arms: \n";
for (int i = 0; i < ProbablyLeftArms.Count; i++)
{
if (ProbablyLeftArms[i] == null) continue;
log += "[" + i + "] ";
for (int l = 0; l < ProbablyLeftArms[i].Count; l++)
{
log += ProbablyLeftArms[i][l].name + " ";
}
log += "\n";
}
}
if (ProbablySpineChainShort.Count > 0)
{
log += "\n\nDebug Spine Chain: \n";
for (int i = 0; i < ProbablySpineChainShort.Count; i++)
{
if (ProbablySpineChainShort[i] == null) continue;
log += ProbablySpineChainShort[i].name + " ";
}
}
log += "\n\n";
return log;
}
#endregion
public static int GetDepth(Transform t, Transform skelRootBone)
{
int depth = 0;
if (t == skelRootBone) return 0;
if (t == null) return 0;
if (t.parent == null) return 0;
while (t != null && t != skelRootBone)
{
t = t.parent;
depth += 1;
}
return depth;
}
}
#region Transforms Utils
public static bool IsChildOf(Transform child, Transform parent)
{
Transform p = child;
while (p != null)
{
if (p == parent) return true;
p = p.parent;
}
return false;
}
public static Transform GetBottomMostChildTransform(Transform parent)
{
var allCh = parent.GetComponentsInChildren<Transform>(true);
int lowest = 0;
Transform lowestT = parent;
for (int c = 0; c < allCh.Length; c++)
{
if (allCh[c] == parent) continue;
Transform ch = allCh[c];
int depth = 0;
while (ch.parent != parent && ch.parent != null)
{
depth += 1;
ch = ch.parent;
}
if (depth > lowest)
{
lowest = depth;
lowestT = allCh[c];
}
}
return lowestT;
}
#endregion
#region Name Based Search Utils
public static readonly string[] SpineNames = new string[] { "spine" };
public static readonly string[] NeckNames = new string[] { "neck" };
public static readonly string[] HeadNames = new string[] { "head" };
public static readonly string[] RootNames = new string[] { "root", "origin", "skel" };
public static readonly string[] PelvisNames = new string[] { "pelvis", "hips", "pelv" };
public static readonly string[] ChestNames = new string[] { "chest", "upperspine" };
public static readonly string[] ShouldersNames = new string[] { "shoulde", "collarbon", "clavicl" };
public static readonly string[] UpperLegNames = new string[] { "upperleg", "thigh" };
public static readonly string[] KneeNames = new string[] { "knee", "calf", "lowerleg" };
public static readonly string[] ElbowNames = new string[] { "elbow", "lowerarm" };
public static bool NameContains(string name, string[] names)
{
string nm = name.ToLower();
nm = nm.Replace("-", "");
nm = nm.Replace(" ", "");
nm = nm.Replace("_", "");
nm = nm.Replace("|", "");
nm = nm.Replace("@", "");
for (int n = 0; n < names.Length; n++)
{
if (nm.Contains(names[n])) return true;
}
return false;
}
#endregion
}
}