static void buildNeighborsFast(TriInfo pTriInfos[], Edge[] pEdges, final int piTriListIn[], final int iNrTrianglesIn) {
        // build array of edges
        long uSeed = INTERNAL_RND_SORT_SEED;        // could replace with a random seed?
        
        for (int f = 0; f < iNrTrianglesIn; f++) {
            for (int i = 0; i < 3; i++) {
                final int i0 = piTriListIn[f * 3 + i];
                final int i1 = piTriListIn[f * 3 + (i < 2 ? (i + 1) : 0)];
                pEdges[f * 3 + i] = new Edge();
                pEdges[f * 3 + i].setI0(i0 < i1 ? i0 : i1);      // put minimum index in i0
                pEdges[f * 3 + i].setI1(!(i0 < i1) ? i0 : i1);    // put maximum index in i1
                pEdges[f * 3 + i].setF(f);              // record face number
            }
        }

        // sort over all edges by i0, this is the pricey one.
        quickSortEdges(pEdges, 0, iNrTrianglesIn * 3 - 1, 0, uSeed);  // sort channel 0 which is i0

        // sub sort over i1, should be fast.
        // could replace this with a 64 bit int sort over (i0,i1)
        // with i0 as msb in the quicksort call above.
        int iEntries = iNrTrianglesIn * 3;
        int iCurStartIndex = 0;
        for (int i = 1; i < iEntries; i++) {
            if (pEdges[iCurStartIndex].getI0() != pEdges[i].getI0()) {
                final int iL = iCurStartIndex;
                final int iR = i - 1;
                //final int iElems = i-iL;
                iCurStartIndex = i;
                quickSortEdges(pEdges, iL, iR, 1, uSeed);  // sort channel 1 which is i1
            }
        }

        // sub sort over f, which should be fast.
        // this step is to remain compliant with BuildNeighborsSlow() when
        // more than 2 triangles use the same edge (such as a butterfly topology).
        iCurStartIndex = 0;
        for (int i = 1; i < iEntries; i++) {
            if (pEdges[iCurStartIndex].getI0() != pEdges[i].getI0() || pEdges[iCurStartIndex].getI1() != pEdges[i].getI1()) {
                final int iL = iCurStartIndex;
                final int iR = i - 1;
                //final int iElems = i-iL;
                iCurStartIndex = i;
                quickSortEdges(pEdges, iL, iR, 2, uSeed);  // sort channel 2 which is f
            }
        }

        // pair up, adjacent triangles
        for (int i = 0; i < iEntries; i++) {
            final int i0 = pEdges[i].getI0();
            final int i1 = pEdges[i].getI1();
            final int g = pEdges[i].getF();
            boolean bUnassigned_A;

            int[] i0_A = new int[1];
            int[] i1_A = new int[1];
            int[] edgenum_A = new int[1];
            int[] edgenum_B = new int[1];
            //int edgenum_B=0;  // 0,1 or 2
            int[] triList = new int[3];
            System.arraycopy(piTriListIn, g * 3, triList, 0, 3);
            getEdge(i0_A, i1_A, edgenum_A, triList, i0, i1);  // resolve index ordering and edge_num
            bUnassigned_A = pTriInfos[g].faceNeighbors[edgenum_A[0]] == -1;

            if (bUnassigned_A) {
                // get true index ordering
                int j = i + 1, t;
                boolean bNotFound = true;
                while (j < iEntries && i0 == pEdges[j].getI0() && i1 == pEdges[j].getI1() && bNotFound) {
                    boolean bUnassigned_B;
                    int[] i0_B = new int[1];
                    int[] i1_B = new int[1];
                    t = pEdges[j].getF();
                    // flip i0_B and i1_B
                    System.arraycopy(piTriListIn, t * 3, triList, 0, 3);
                    getEdge(i1_B, i0_B, edgenum_B, triList, pEdges[j].getI0(), pEdges[j].getI1());  // resolve index ordering and edge_num
                    //assert(!(i0_A==i1_B && i1_A==i0_B));
                    bUnassigned_B = pTriInfos[t].faceNeighbors[edgenum_B[0]] == -1;
                    if (i0_A[0] == i0_B[0] && i1_A[0] == i1_B[0] && bUnassigned_B) {
                        bNotFound = false;
                    } else {
                        ++j;
                    }
                }

                if (!bNotFound) {
                    int t2 = pEdges[j].getF();
                    pTriInfos[g].faceNeighbors[edgenum_A[0]] = t2;
                    //assert(pTriInfos[t].FaceNeighbors[edgenum_B]==-1);
                    pTriInfos[t2].faceNeighbors[edgenum_B[0]] = g;
                }
            }
        }
    }
A private constructor to inhibit instantiation of this class.
jme3-core/src/main/java/com/jme3/util/mikktspace/MikktspaceTangentGenerator.java