static void initTriInfo(TriInfo pTriInfos[], final int piTriListIn[], final MikkTSpaceContext mikkTSpace, final int iNrTrianglesIn) {

        // pTriInfos[f].flag is cleared in GenerateInitialVerticesIndexList() which is called before this function.
        // generate neighbor info list
        for (int f = 0; f < iNrTrianglesIn; f++) {
            for (int i = 0; i < 3; i++) {
                pTriInfos[f].faceNeighbors[i] = -1;
                pTriInfos[f].assignedGroup[i] = null;

                pTriInfos[f].os.x = 0.0f;
                pTriInfos[f].os.y = 0.0f;
                pTriInfos[f].os.z = 0.0f;
                pTriInfos[f].ot.x = 0.0f;
                pTriInfos[f].ot.y = 0.0f;
                pTriInfos[f].ot.z = 0.0f;
                pTriInfos[f].magS = 0;
                pTriInfos[f].magT = 0;

                // assumed bad
                pTriInfos[f].flag |= GROUP_WITH_ANY;
            }
        }

        // evaluate first order derivatives
        for (int f = 0; f < iNrTrianglesIn; f++) {
            // initial values
            final Vector3f v1 = getPosition(mikkTSpace, piTriListIn[f * 3 + 0]);
            final Vector3f v2 = getPosition(mikkTSpace, piTriListIn[f * 3 + 1]);
            final Vector3f v3 = getPosition(mikkTSpace, piTriListIn[f * 3 + 2]);
            final Vector3f t1 = getTexCoord(mikkTSpace, piTriListIn[f * 3 + 0]);
            final Vector3f t2 = getTexCoord(mikkTSpace, piTriListIn[f * 3 + 1]);
            final Vector3f t3 = getTexCoord(mikkTSpace, piTriListIn[f * 3 + 2]);

            final float t21x = t2.x - t1.x;
            final float t21y = t2.y - t1.y;
            final float t31x = t3.x - t1.x;
            final float t31y = t3.y - t1.y;
            final Vector3f d1 = v2.subtract(v1);
            final Vector3f d2 = v3.subtract(v1);

            final float fSignedAreaSTx2 = t21x * t31y - t21y * t31x;
            //assert(fSignedAreaSTx2!=0);
            Vector3f vOs = d1.mult(t31y).subtract(d2.mult(t21y));  // eq 18
            Vector3f vOt = d1.mult(-t31x).add(d2.mult(t21x));  // eq 19

            pTriInfos[f].flag |= (fSignedAreaSTx2 > 0 ? ORIENT_PRESERVING : 0);

            if (isNotZero(fSignedAreaSTx2)) {
                final float fAbsArea = Math.abs(fSignedAreaSTx2);
                final float fLenOs = vOs.length();
                final float fLenOt = vOt.length();
                final float fS = (pTriInfos[f].flag & ORIENT_PRESERVING) == 0 ? (-1.0f) : 1.0f;
                if (isNotZero(fLenOs)) {
                    pTriInfos[f].os = vOs.multLocal(fS / fLenOs);
                }
                if (isNotZero(fLenOt)) {
                    pTriInfos[f].ot = vOt.multLocal(fS / fLenOt);
                }

                // evaluate magnitudes prior to normalization of vOs and vOt
                pTriInfos[f].magS = fLenOs / fAbsArea;
                pTriInfos[f].magT = fLenOt / fAbsArea;

                // if this is a good triangle
                if (isNotZero(pTriInfos[f].magS) && isNotZero(pTriInfos[f].magT)) {
                    pTriInfos[f].flag &= (~GROUP_WITH_ANY);
                }
            }
        }

        // force otherwise healthy quads to a fixed orientation
        int t = 0;
        while (t < (iNrTrianglesIn - 1)) {
            final int iFO_a = pTriInfos[t].orgFaceNumber;
            final int iFO_b = pTriInfos[t + 1].orgFaceNumber;
            if (iFO_a == iFO_b) {
                // this is a quad
                final boolean bIsDeg_a = (pTriInfos[t].flag & MARK_DEGENERATE) != 0;
                final boolean bIsDeg_b = (pTriInfos[t + 1].flag & MARK_DEGENERATE) != 0;

                // bad triangles should already have been removed by
                // DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false
                if ((bIsDeg_a || bIsDeg_b) == false) {
                    final boolean bOrientA = (pTriInfos[t].flag & ORIENT_PRESERVING) != 0;
                    final boolean bOrientB = (pTriInfos[t + 1].flag & ORIENT_PRESERVING) != 0;
                    // if this happens the quad has extremely bad mapping!!
                    if (bOrientA != bOrientB) {
                        //printf("found quad with bad mapping\n");
                        boolean bChooseOrientFirstTri = false;
                        if ((pTriInfos[t + 1].flag & GROUP_WITH_ANY) != 0) {
                            bChooseOrientFirstTri = true;
                        } else if (calcTexArea(mikkTSpace, Arrays.copyOfRange(piTriListIn, t * 3 + 0, t * 3 + 3)) >= calcTexArea(mikkTSpace, Arrays.copyOfRange(piTriListIn, (t + 1) * 3 + 0, (t + 1) * 3 + 3))) {
                            bChooseOrientFirstTri = true;
                        }

                        // force match
                        {
                            final int t0 = bChooseOrientFirstTri ? t : (t + 1);
                            final int t1 = bChooseOrientFirstTri ? (t + 1) : t;
                            pTriInfos[t1].flag &= (~ORIENT_PRESERVING);  // clear first
                            pTriInfos[t1].flag |= (pTriInfos[t0].flag & ORIENT_PRESERVING);  // copy bit
                        }
                    }
                }
                t += 2;
            } else {
                ++t;
            }
        }

        // match up edge pairs
        {
            //Edge * pEdges = (Edge *) malloc(sizeof(Edge)*iNrTrianglesIn*3);
            Edge[] pEdges = new Edge[iNrTrianglesIn * 3];

            //TODO nehon weird... original algorithm checked if pEdges is null but it's just been allocated... weirder, it does something different if the edges are null...
            //    if (pEdges==null)
            //      BuildNeighborsSlow(pTriInfos, piTriListIn, iNrTrianglesIn);
            //    else
            //    {
            buildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn);

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