/*
 * Converting no hs worlds to real binary worlds (with bsps).
 * No HS worlds are used in the generation process of worlds
 *
 * Copyright (c) 1998 Criterion Software Ltd.
 */

/****************************************************************************
 Includes
 */

#include "rwcore.h"
#include "rpworld.h"
#include "rpdbgerr.h"

#include "nhsbary.h"

static const char   rcsid[] __RWUNUSED__ =
    "@@(#)$Id: nhsbary.c,v 1.24 2001/07/09 15:38:31 johns Exp $";

/****************************************************************************
 Local Types
 */

#define _ASSERT_EPSILON (((RwReal)1)/((RwReal)(1<<8)))

RwBool
_rtImportBarycentricFromCartesianMatrix(RtDM4d m,
                                        RwReal * const area,
                                        const RwV3d * const c0,
                                        const RwV3d * const c1,
                                        const RwV3d * const c2)
{
    RwDReal             area2;
    RwBool              hasBarySupport;
    RwDReal             normalize;
    RwDV3d              b0, b1;
    RwDV3d              centroid;
    RwDV3d              e0, e1;
    RwDV3d              n;

    RWFUNCTION(RWSTRING("_rtImportBarycentricFromCartesianMatrix"));

    /* Find edges
     *
     *         C0 o---------------o C1
     *             \             /
     *              \           /
     *               \ __    | /
     *            e1 |\      |/__  e0
     *               | \     /
     *                  \   /
     *                   \ /
     *                    o
     *                   C2
     *
     */

    RwV3dSubMacro(&e0, c1, c2);
    RwV3dSubMacro(&e1, c2, c0);

    RwV3dCrossProductMacro(&n, &e0, &e1);
    area2 = RwV3dDotProductMacro(&n, &n);
    hasBarySupport = (_AREA2_EPSILON < area2);
    if (hasBarySupport)
    {
        normalize = sqrt(area2);
        *area = (RwReal)normalize;
        normalize = 1 / normalize;
    }
    else
    {
        normalize = 0;
        *area = (RwReal)normalize;
    }

    RwV3dScaleMacro(&n, &n, normalize);

    RwV3dCrossProductMacro(&b0, &e0, &n);
    RwV3dScaleMacro(&b0, &b0, normalize);

    RwV3dCrossProductMacro(&b1, &e1, &n);
    RwV3dScaleMacro(&b1, &b1, normalize);

    RwV3dAddMacro(&centroid, c0, c1);
    RwV3dAddMacro(&centroid, &centroid, c2);
    RwV3dScaleMacro(&centroid, &centroid, (RwDReal) (1.0 / 3.0));

    m[0][0] = b0.x;
    m[0][1] = b1.x;
    m[0][2] = -(b0.x + b1.x);
    m[0][3] = n.x;

    m[1][0] = b0.y;
    m[1][1] = b1.y;
    m[1][2] = -(b0.y + b1.y);
    m[1][3] = n.y;

    m[2][0] = b0.z;
    m[2][1] = b1.z;
    m[2][2] = -(b0.z + b1.z);
    m[2][3] = n.z;

    m[3][0] = -RwV3dDotProductMacro(&b0, c2);
    m[3][1] = -RwV3dDotProductMacro(&b1, c0);
    m[3][2] = ((RwDReal) 1) - (m[3][0] + m[3][1]);
    m[3][3] = -RwV3dDotProductMacro(&n, &centroid);

#if ( 0 && defined(RWDEBUG) && defined(RWVERBOSE) )
    {
        RtDV4d              weight;

        if (hasBarySupport)
        {

            _rtImportWorldBaryFromCart(weight, m, c0);
            weight[0] -= 1;

            if (!(WithinEpsilonOfZero(weight[0], _ASSERT_EPSILON)))
                RWMESSAGE(("%f == area2 %f == weight[0]", area2,
                           weight[0]));
            if (!(WithinEpsilonOfZero(weight[1], _ASSERT_EPSILON)))
                RWMESSAGE(("%f == area2 %f == weight[1]", area2,
                           weight[1]));
            if (!(WithinEpsilonOfZero(weight[2], _ASSERT_EPSILON)))
                RWMESSAGE(("%f == area2 %f == weight[2]", area2,
                           weight[2]));
            if (!(WithinEpsilonOfZero(weight[3], _ASSERT_EPSILON)))
                RWMESSAGE(("%f == area2 %f == weight[3]", area2,
                           weight[3]));

            _rtImportWorldBaryFromCart(weight, m, c1);
            weight[1] -= 1;
            if (!(WithinEpsilonOfZero(weight[0], _ASSERT_EPSILON)))
                RWMESSAGE(("%f == area2 %f == weight[0]", area2,
                           weight[0]));
            if (!(WithinEpsilonOfZero(weight[1], _ASSERT_EPSILON)))
                RWMESSAGE(("%f == area2 %f == weight[1]", area2,
                           weight[1]));
            if (!(WithinEpsilonOfZero(weight[2], _ASSERT_EPSILON)))
                RWMESSAGE(("%f == area2 %f == weight[2]", area2,
                           weight[2]));
            if (!(WithinEpsilonOfZero(weight[3], _ASSERT_EPSILON)))
                RWMESSAGE(("%f == area2 %f == weight[3]", area2,
                           weight[3]));
        }

        _rtImportWorldBaryFromCart(weight, m, c2);
        weight[2] -= 1;
        if (!(WithinEpsilonOfZero(weight[0], _ASSERT_EPSILON)))
            RWMESSAGE(("%f == area2 %f == weight[0]", area2,
                       weight[0]));
        if (!(WithinEpsilonOfZero(weight[1], _ASSERT_EPSILON)))
            RWMESSAGE(("%f == area2 %f == weight[1]", area2,
                       weight[1]));
        if (!(WithinEpsilonOfZero(weight[2], _ASSERT_EPSILON)))
            RWMESSAGE(("%f == area2 %f == weight[2]", area2,
                       weight[2]));
        if (!(WithinEpsilonOfZero(weight[3], _ASSERT_EPSILON)))
            RWMESSAGE(("%f == area2 %f == weight[3]", area2,
                       weight[3]));

    }
#endif /* ( 0 && defined(RWDEBUG) && defined(RWVERBOSE) )  */

    RWRETURN(hasBarySupport);
}

#if ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) )

void
_rtImportWorldCartFromBary(RwV3d * out, RtDV4d weights,
                           RwV3d * v0, RwV3d * v1, RwV3d * v2)
{

    RWFUNCTION(RWSTRING("_rtImportWorldCartFromBary"));

    _rtImportWorldCartFromBaryMacro(out, weights, v0, v1, v2);

    RWRETURNVOID();
}

void
_rtImportWorldBaryFromCart(RtDV4d out, RtDM4d mat,
                           const RwV3d * const in)
{
    RWFUNCTION(RWSTRING("_rtImportWorldBaryFromCart"));

    _rtImportWorldBaryFromCartMacro(out, mat, in);

    RWRETURNVOID();
}

void
_rtImportWorldBaryFromEdge(RtDV4d out, RtDM4d mat,
                           const RwV3d * const in)
{
    RWFUNCTION(RWSTRING("_rtImportWorldBaryFromEdge"));

    _rtImportWorldBaryFromEdgeMacro(out, mat, in);

    RWRETURNVOID();
}

#endif /* ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) ) */
