/*
 *
 * VRML to RW converter plug-in
 */
/****************************************************************************
 * 
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1997 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : GroupNodes.c
 *                                                                         
 * Purpose : Converts the Group nodes :-
 * (Anchor, Billboard, Collision, Group, & Transform)
 *                                                                         
 ****************************************************************************/

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "rpplugin.h"
#include "groupnodes.h"
#include "converter.h"
#include "builder.h"
#include "route.h"
#include "animation.h"
#include "rpvrmlanim.h"
#include "rpvrml.h"
#include "animfuncttab.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: groupnodes.c,v 1.55 2001/02/05 11:44:12 johns Exp $";

extern float        GscaleFactor;
static sfvec3f      Gcenter = { 0.0f, 0.0f, 0.0f };
static RwInt32      GanimNodeRefCnt = 0; /* carefull to initiate */

/****************************************************************************
 Local (Static) Prototypes
 */

static RwMatrix    *Matrix_Get(RpVRMLAnimTransformState * compMatrix,
                               RwMatrix * matrix);

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Anchor methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RwBool
Anchor_Convert(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("Anchor_Convert"));
    RWASSERT(an);

    if (an)
    {
        AbstractField      *af;
        RwChar             *nodeName;

        nodeName = AbstractNode_GetBaseName(an);
        if ((rwstrcmp(nodeName, RWSTRING("Anchor")) != 0))
        {
            RWASSERT(FALSE);
            RWRETURN(FALSE);
        }

        if (!Build_PushPivotOffset(&Gcenter))
        {
            RWRETURN(FALSE);
        }

        /* get children */
        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("children"))))
        {
            Field              *field;
            int                 numChildren, i;

            field = AbstractField_GetField(af);
            numChildren = Field_NumElements(field);

            for (i = 0; i < numChildren; i++)
            {
                AbstractNode       *anChild;

                anChild = FieldMfnode_GetValue(field, i);
                if (!Convert_Children(anChild))
                {
                    RWRETURN(FALSE);
                }
            }
        }

        Build_PopPivotOffset();

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Billboard methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RwBool
Billboard_Convert(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("Billboard_Convert"));
    RWASSERT(an);

    if (an)
    {
        AbstractField      *af;
        RwChar             *nodeName;

        nodeName = AbstractNode_GetBaseName(an);
        if (rwstrcmp(nodeName, RWSTRING("Billboard")) != 0)
        {
            RWASSERT(FALSE);
            RWRETURN(FALSE);
        }

        if (!Build_PushPivotOffset(&Gcenter))
        {
            RWRETURN(FALSE);
        }

        /* get children */
        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("children"))))
        {
            Field              *field;
            int                 numChildren, i;

            field = AbstractField_GetField(af);
            numChildren = Field_NumElements(field);

            for (i = 0; i < numChildren; i++)
            {
                AbstractNode       *anChild;

                anChild = FieldMfnode_GetValue(field, i);
                if (!Convert_Children(anChild))
                {
                    RWRETURN(FALSE);
                }
            }
        }

        Build_PopPivotOffset();

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Collision methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RwBool
Collision_Convert(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("Collision_Convert"));
    RWASSERT(an);

    if (an)
    {
        AbstractField      *af;
        RwChar             *nodeName;

        nodeName = AbstractNode_GetBaseName(an);
        if (rwstrcmp(nodeName, RWSTRING("Collision")) != 0)
        {
            RWASSERT(FALSE);
            RWRETURN(FALSE);
        }

        if (!Build_PushPivotOffset(&Gcenter))
        {
            RWRETURN(FALSE);
        }

        /* get children */
        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("children"))))
        {
            Field              *field;
            RwInt32             numChildren, i;

            field = AbstractField_GetField(af);
            numChildren = Field_NumElements(field);

            for (i = 0; i < numChildren; i++)
            {
                AbstractNode       *anChild;

                anChild = FieldMfnode_GetValue(field, i);
                if (!Convert_Children(anChild))
                {
                    RWRETURN(FALSE);
                }
            }
        }

        Build_PopPivotOffset();

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Group methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RwBool
Group_Convert(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("Group_Convert"));
    RWASSERT(an);

    if (an)
    {
        AbstractField      *af;
        RwChar             *nodeName;

        nodeName = AbstractNode_GetBaseName(an);
        if (rwstrcmp(nodeName, RWSTRING("Group")) != 0)
        {
            RWASSERT(FALSE);
            RWRETURN(FALSE);
        }

        if (!Build_PushPivotOffset(&Gcenter))
        {
            RWRETURN(FALSE);
        }

        /* get children */
        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("children"))))
        {
            Field              *field;
            RwInt32             numChildren, i;

            field = AbstractField_GetField(af);
            numChildren = Field_NumElements(field);

            for (i = 0; i < numChildren; i++)
            {
                AbstractNode       *anChild;

                anChild = FieldMfnode_GetValue(field, i);
                if (!Convert_Children(anChild))
                {
                    RWRETURN(FALSE);
                }
            }
        }

        Build_PopPivotOffset();

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Dummy methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RwBool
Dummy_Convert(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("Dummy_Convert"));
    RWASSERT(an);

    if (an)
    {
        RwChar             *nodeName;

        nodeName = AbstractNode_GetBaseName(an);
        if (rwstrcmp(nodeName, RWSTRING("Shape")) != 0)
        {
            RWASSERT(FALSE);
            RWRETURN(FALSE);
        }

        if (!Build_PushPivotOffset(&Gcenter))
        {
            RWRETURN(FALSE);
        }

        if (!Convert_Children(an))
        {
            RWRETURN(FALSE);
        }

        Build_PopPivotOffset();

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(FALSE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Transform methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

RwBool
Transform_Convert(AbstractNode * an)
{
    RwBool              isStatic;

    /* push transform onto the Build structure */
    RwMatrix           *matrix;
    RwChar             *nodeName;

    RWFUNCTION(RWSTRING("Transform_Convert"));
    RWASSERT(an);

    nodeName = AbstractNode_GetBaseName(an);
    /*RWASSERT(rwstrcmp(nodeName, RWSTRING("Transform")) == 0); */

    /* are we in a static or dynamic branch of the scene graph? */
    isStatic = Route_IsNodeStatic(an);
    if (!isStatic)
    {
        if (GanimNodeRefCnt == 0)
        {
            /* set static branch toggle */
            Build_SetGeomType(GEOM_DYNAMIC);

        }
        GanimNodeRefCnt++;
    }

    matrix = Build_PushTransform();;
    Build_PushPivotOffset(&Gcenter);

    /* Construct the transform matrix */
    if (matrix)
    {
        RpVRMLAnimTransformState compMatrix;
        AbstractField      *af;
        RwBool              animPlugin = FALSE;
        buildType           bt = Build_GetType();

        /* is the anim plugin attached */
        if ((bt == CLUMP)
            && (RwEngineGetPluginOffset(rwID_VRMLANIMPLUGIN) > 0))
        {
            animPlugin = TRUE;
        }
        else
        {
            animPlugin = FALSE;
        }

        /* we need to init compMatrix to some defaults */
        RwMatrixSetIdentity(&compMatrix.rotationMat);
        RwMatrixSetIdentity(&compMatrix.scaleOrientMat);
        compMatrix.offset.x = 0.0f;
        compMatrix.offset.y = 0.0f;
        compMatrix.offset.z = 0.0f;
        compMatrix.translation.x = 0.0f;
        compMatrix.translation.y = 0.0f;
        compMatrix.translation.z = 0.0f;
        compMatrix.scale.x = 1.0f;
        compMatrix.scale.y = 1.0f;
        compMatrix.scale.z = 1.0f;

        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("center"))))
        {
            RwV3d               transScale;
            Field              *field;
            sfvec3f            *center;

            field = AbstractField_GetField(af);
            center = FieldSfvec3f_GetValue(field);

            transScale.x = center->x * GscaleFactor;
            transScale.y = center->y * GscaleFactor;
            transScale.z = center->z * GscaleFactor;

            compMatrix.offset = transScale;
        }

        if ((af =
             AbstractNode_GetAbstractField(an,
                                           RWSTRING("translation"))))
        {
            RwV3d               transScale;
            Field              *field;
            sfvec3f            *translation;

            field = AbstractField_GetField(af);
            translation = FieldSfvec3f_GetValue(field);

            transScale.x = translation->x * GscaleFactor;
            transScale.y = translation->y * GscaleFactor;
            transScale.z = translation->z * GscaleFactor;

            compMatrix.translation = transScale;
        }

        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("rotation"))))
        {
            RwMatrix            rotateMat;
            Field              *field;
            RwV3d               axis;
            RwReal              angle;
            sfrotation         *rotation;

            field = AbstractField_GetField(af);
            rotation = FieldSfrotation_GetValue(field);

            axis.x = (RwReal) (rotation->x);
            axis.y = (RwReal) (rotation->y);
            axis.z = (RwReal) (rotation->z);
            angle = (((rotation->angle)) * ((RwReal) (180.0 / rwPI)));

            RwMatrixSetIdentity(&rotateMat);
            RwMatrixRotate(&rotateMat, &axis, angle, rwCOMBINEREPLACE);
            compMatrix.rotationMat = rotateMat;
        }

        if ((af = AbstractNode_GetAbstractField(an, RWSTRING("scale"))))
        {
            Field              *field;
            sfvec3f            *scale;

            field = AbstractField_GetField(af);
            scale = FieldSfvec3f_GetValue(field);
            compMatrix.scale = *(RwV3d *) scale;
        }

        if ((af =
             AbstractNode_GetAbstractField(an,
                                           RWSTRING
                                           ("scaleOrientation"))))
        {
            RwMatrix            scaleOrientMat;
            Field              *field;
            RwV3d               axis;
            RwReal              angle;
            sfrotation         *scaleOrientation;

            field = AbstractField_GetField(af);
            scaleOrientation = FieldSfrotation_GetValue(field);

            axis.x = (RwReal) (scaleOrientation->x);
            axis.y = (RwReal) (scaleOrientation->y);
            axis.z = (RwReal) (scaleOrientation->z);
            angle =
                (((scaleOrientation->angle)) *
                 ((RwReal) (180.0 / rwPI)));

            RwMatrixSetIdentity(&scaleOrientMat);
            RwMatrixRotate(&scaleOrientMat, &axis, angle,
                           rwCOMBINEREPLACE);

            compMatrix.scaleOrientMat = scaleOrientMat;
        }

        Matrix_Get(&compMatrix, matrix);

        /* get children */
        if ((af =
             AbstractNode_GetAbstractField(an, RWSTRING("children"))))
        {
            Field              *field;
            int                 numChildren, i;

            field = AbstractField_GetField(af);
            numChildren = Field_NumElements(field);

            for (i = 0; i < numChildren; i++)
            {
                AbstractNode       *anChild;

                anChild = FieldMfnode_GetValue(field, i);
                if (!Convert_Children(anChild))
                {
                    RWRETURN(FALSE);
                }
            }
        }

        if (animPlugin)
        {
            RpVRMLAnimFunctions *animFuncts;
            RwFrame            *frame;
            RwChar             *name;

            frame = Build_GetCurrentFrame();
            animFuncts = _rpVrmAnimFunctionsGet();

            *animFuncts->fpFrameAnimGetMatrixComp(frame) = compMatrix;
            AnimationSetup(an, frame);

            name = AbstractNode_GetSymName(an);
            if (name)
            {
                animFuncts->fpFrameAnimSetName(frame, name);
            }
        }

        Build_PopPivotOffset();
        Build_PopTransform();
    }

    if (!isStatic)
    {
        GanimNodeRefCnt--;
        if (GanimNodeRefCnt == 0)
        {
            /* reset static branch toggle */
            Build_SetGeomType(GEOM_STATIC);
        }
    }

    RWRETURN(TRUE);
}

sfvec3f            *
Transform_GetOffset(void)
{
    RWFUNCTION(RWSTRING("Transform_GetOffset"));

    RWRETURN((&Gcenter));
}

void
Transform_ResetOffset(void)
{
    RWFUNCTION(RWSTRING("Transform_ResetOffset"));

    Gcenter.x = 0.0f;
    Gcenter.y = 0.0f;
    Gcenter.z = 0.0f;

    RWRETURNVOID();
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   private Matrix methods

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

static void
_matrixSetTranslation(RwMatrix * matrix, RwV3d * translation,
                      RwBool inv)
{
    RWFUNCTION(RWSTRING("_matrixSetTranslation"));
    RWASSERT(matrix);
    RWASSERT(translation);

    if (matrix && translation)
    {
        RwV3d               newTranslation;

        newTranslation = *translation;
        if (inv)
        {
            RwV3dScale(&newTranslation, &newTranslation, -1.0f);
        }
        RwMatrixTranslate(matrix, &newTranslation, rwCOMBINEPOSTCONCAT);
    }

    RWRETURNVOID();
}

static void
_matrixSetRotation(RwMatrix * matrix, RwMatrix * rotation, RwBool inv)
{
    RWFUNCTION(RWSTRING("_matrixSetRotation"));
    RWASSERT(matrix);
    RWASSERT(rotation);

    if (matrix && rotation)
    {
        if (inv)
        {
            RwMatrix            invRotation;

            RwMatrixInvert(&invRotation, rotation);
            RwMatrixTransform(matrix, &invRotation,
                              rwCOMBINEPOSTCONCAT);
        }
        else
        {
            RwMatrixTransform(matrix, rotation, rwCOMBINEPOSTCONCAT);
        }
    }

    RWRETURNVOID();
}

static void
_matrixSetScale(RwMatrix * matrix, RwV3d * scale, RwBool inv)
{
    RWFUNCTION(RWSTRING("_matrixSetScale"));
    RWASSERT(matrix);
    RWASSERT(scale);

    if (matrix && scale)
    {
        RwV3d               newScale;

        newScale = *scale;
        if (inv)
        {
            RwV3dScale(&newScale, &newScale, -1.0f);
        }
        RwMatrixScale(matrix, &newScale, rwCOMBINEPOSTCONCAT);
    }

    RWRETURNVOID();
}

static RwMatrix    *
Matrix_Get(RpVRMLAnimTransformState * compMatrix, RwMatrix * matrix)
{
    RWFUNCTION(RWSTRING("Matrix_Get"));
    RWASSERT(compMatrix);
    RWASSERT(matrix);

    if (compMatrix && matrix)
    {
        RwMatrixSetIdentity(matrix);

        /* P' = T * C * R * SR * S * -SR * -C * P */

        _matrixSetTranslation(matrix, &compMatrix->offset, TRUE);
        _matrixSetRotation(matrix, &compMatrix->scaleOrientMat, TRUE);
        _matrixSetScale(matrix, &compMatrix->scale, FALSE);
        _matrixSetRotation(matrix, &compMatrix->scaleOrientMat, FALSE);
        _matrixSetRotation(matrix, &compMatrix->rotationMat, FALSE);
        _matrixSetTranslation(matrix, &compMatrix->offset, FALSE);
        _matrixSetTranslation(matrix, &compMatrix->translation, FALSE);

        RWRETURN(matrix);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RwMatrix *)NULL);
}
