예제 #1
0
        public List <int[]> GetBonePairs(bool flipZ = false, int symmetryMargin = 16)
        {
            var sigList = new List <BoneSignature>();

            sigList = CollectBoneSignatures(Bones[0], 0, -1, sigList).OrderBy(item => item.Index).ToList();
            var result = new List <int[]>();

            // Traverse through collected signatures in 2 passes
            for (int i = 0; i <= symmetryMargin; i++)
            {
                foreach (var sig in sigList)
                {
                    // Entry already was paired, bypass it
                    if (result.Any(pair => (pair[0] != -1 && pair[1] == sig.Index) || (pair[1] != -1 && pair[0] == sig.Index)))
                    {
                        continue;
                    }

                    // Find out potentially matching bone signatures
                    var others = sigList.Where(item =>
                                               sig.Index != item.Index &&
                                               sig.ChildIndices.Count == item.ChildIndices.Count &&                         // Child count should always match!
                                               sig.Depth == item.Depth &&                                                   // Own depth should always be the same!
                                               sigList[sig.ParentIndex].Depth == sigList[item.ParentIndex].Depth).ToList(); // Parent depth should always be the same!

                    bool boneAdded = false;
                    foreach (var other in others)
                    {
                        float compX1 = !flipZ ? sig.Pivot.X   :   sig.Pivot.Z;
                        float compX2 = !flipZ ? other.Pivot.X : other.Pivot.Z;
                        float compZ1 = !flipZ ? sig.Pivot.Z   :   sig.Pivot.X;
                        float compZ2 = !flipZ ? other.Pivot.Z : other.Pivot.X;

                        if ((i != symmetryMargin &&

                             // Prioritize bones with same parent index and pivot symmetry.
                             // Pivot symmetry is declared if X pivot position is mirrored for both bones
                             // and bones are well apart on Z axis (to correctly identify models like scorpion or crocodile).
                             // Unfortunately, there's a case when both bones are symmetrical but are pivoted
                             // from single point (e.g. DEMIGOD3 spear), in this case we can't predict symmetry correctly,
                             // so such cases are bypassed.

                             sig.ParentIndex == other.ParentIndex &&
                             Math.Abs(Math.Abs(compX1) - Math.Abs(compX2)) <= i &&
                             Math.Abs(compX1 - compX2) >= i &&
                             MathC.WithinEpsilon(compZ1, compZ2, symmetryMargin)) ||

                            // On last pass, prioritize bones with already found matching parent mesh pairs.

                            (i == symmetryMargin &&
                             ((result.Any(pair => (pair[0] == sig.ParentIndex && pair[1] == other.ParentIndex) ||
                                          (pair[0] == other.ParentIndex && pair[1] == sig.ParentIndex)))) ||

                             // Additionally do final exact comparison of Y/Z values and X distance (fixes TR3 Shiva without breaking anything else)

                             (MathC.WithinEpsilon(compZ1, compZ2, symmetryMargin) &&
                              MathC.WithinEpsilon(sig.Pivot.Y, other.Pivot.Y, symmetryMargin) &&
                              Math.Abs(compX1 - compX2) >= symmetryMargin)))
                        {
                            int p0 = result.IndexOf(pair => pair[0] == sig.Index);
                            int p1 = result.IndexOf(pair => pair[0] == other.Index);

                            if (p0 == -1 && p1 == -1)
                            {
                                result.Add(new int[2] {
                                    sig.Index, other.Index
                                });                                                 // No entry in pair list
                            }
                            else if (p0 == -1)
                            {
                                result[p1] = new int[2] {
                                    sig.Index, other.Index
                                }
                            }
                            ;                                                       // Entry was already in list
                            else if (p1 == -1)
                            {
                                result[p0] = new int[2] {
                                    sig.Index, other.Index
                                }
                            }
                            ;                                                       // Found match was already in list

                            boneAdded = true;
                            break;
                        }
                    }

                    if (!boneAdded && i == symmetryMargin)
                    {
                        result.Add(new int[2] {
                            sig.Index, -1
                        });                                       // Entry isn't found, create new one on last pass
                    }
                }
            }

            // PARANOIA: try to flter out stray decoupled pairs
            result = result.Where(item => !(item[1] == -1 &&
                                            result.Any(item2 => (item2[0] == item[0] || item2[1] == item[0]) && item2[1] != -1))).ToList();

            return(result);
        }