static int build4RuleGroups(TriInfo pTriInfos[], Group pGroups[], int piGroupTrianglesBuffer[], final int piTriListIn[], final int iNrTrianglesIn) {
        final int iNrMaxGroups = iNrTrianglesIn * 3;
        int iNrActiveGroups = 0;
        int iOffset = 0;
        // (void)iNrMaxGroups;  /* quiet warnings in non debug mode */
        for (int f = 0; f < iNrTrianglesIn; f++) {
            for (int i = 0; i < 3; i++) {
                // if not assigned to a group
                if ((pTriInfos[f].flag & GROUP_WITH_ANY) == 0 && pTriInfos[f].assignedGroup[i] == null) {
                    boolean bOrPre;                    
                    final int vert_index = piTriListIn[f * 3 + i];
                    assert (iNrActiveGroups < iNrMaxGroups);
                    pTriInfos[f].assignedGroup[i] = new Group(); 
                    pGroups[iNrActiveGroups] = pTriInfos[f].assignedGroup[i];
                    pTriInfos[f].assignedGroup[i].vertexRepresentative = vert_index;
                    pTriInfos[f].assignedGroup[i].orientationPreserving = (pTriInfos[f].flag & ORIENT_PRESERVING) != 0;
                    pTriInfos[f].assignedGroup[i].nrFaces = 0;
                    
                    ++iNrActiveGroups;

                    addTriToGroup(pTriInfos[f].assignedGroup[i], f);
                    bOrPre = (pTriInfos[f].flag & ORIENT_PRESERVING) != 0;
                    int neigh_indexL = pTriInfos[f].faceNeighbors[i];
                    int neigh_indexR = pTriInfos[f].faceNeighbors[i > 0 ? (i - 1) : 2];
                    if (neigh_indexL >= 0) {
                        // neighbor
                        final boolean bAnswer
                                = assignRecur(piTriListIn, pTriInfos, neigh_indexL,
                                        pTriInfos[f].assignedGroup[i]);

                        final boolean bOrPre2 = (pTriInfos[neigh_indexL].flag & ORIENT_PRESERVING) != 0;
                        final boolean bDiff = bOrPre != bOrPre2;
                        assert (bAnswer || bDiff);
                        //(void)bAnswer, (void)bDiff;  /* quiet warnings in non debug mode */
                    }
                    if (neigh_indexR >= 0) {
                        // neighbor
                        final boolean bAnswer
                                = assignRecur(piTriListIn, pTriInfos, neigh_indexR,
                                        pTriInfos[f].assignedGroup[i]);

                        final boolean bOrPre2 = (pTriInfos[neigh_indexR].flag & ORIENT_PRESERVING) != 0;
                        final boolean bDiff = bOrPre != bOrPre2;
                        assert (bAnswer || bDiff);
                        //(void)bAnswer, (void)bDiff;  /* quiet warnings in non debug mode */
                    }

                    int[] faceIndices = new int[pTriInfos[f].assignedGroup[i].nrFaces];
                    //pTriInfos[f].assignedGroup[i].faceIndices.toArray(faceIndices);
                    for (int j = 0; j < faceIndices.length; j++) {
                        faceIndices[j] = pTriInfos[f].assignedGroup[i].faceIndices.get(j);
                    }
                    
                    //Nehon: copy back the faceIndices data into the groupTriangleBuffer.
                    System.arraycopy( faceIndices, 0, piGroupTrianglesBuffer, iOffset, pTriInfos[f].assignedGroup[i].nrFaces);
                    // update offset
                    iOffset += pTriInfos[f].assignedGroup[i].nrFaces;
                    // since the groups are disjoint a triangle can never
                    // belong to more than 3 groups. Subsequently something
                    // is completely screwed if this assertion ever hits.
                    assert (iOffset <= iNrMaxGroups);
                }
            }
        }

        return iNrActiveGroups;
    }
A private constructor to inhibit instantiation of this class.
jme3-core/src/main/java/com/jme3/util/mikktspace/MikktspaceTangentGenerator.java