
/****************************************************************************
 *                                                                         
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1997 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : Builder.c
 *                                                                         
 * Purpose : Used by the Converter to construct the actual RW3.0 object
 *
 *                                                                         
 ****************************************************************************/

/****************************************************************************
 Includes
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "rpplugin.h"
#include "builder.h"
#include "stack.h"
#include "converter.h"
#include "material.h"
#include "rpvrmlanim.h"
#include "rpvrml.h"
#include "utilities.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: builder.c,v 1.58 2001/08/28 10:58:00 mattt Exp $";

RwReal              GscaleFactor = 1.0f;

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

/****************************************************************************
 Local (static) Globals
 */

static geomType     GgeomType = GEOM_STATIC;
static buildType    GbuildType = CLUMP;

/* stores the current converted clump */
static RpClump     *clump = (RpClump *)NULL;
static RwFrame     *rootFrame = (RwFrame *)NULL;

/* stores the current converted world */
static RpWorld     *world = (RpWorld *)NULL;
static RtWorldImport *nhsWorld = (RtWorldImport *)NULL;

/* used for creating hierachical objects */
static LLStack      framePtrStack; /* of [RwFrame *] */

/* used for calculating current world object's LTM */
static LLStack      matrixPtrStack; /* of [RwMatrix *] */
static RwMatrix    *currentLTM;

static RwInt32      maxFrameStackRec = 0;
static RwInt32      currentFrameStackDepth = 0;

static RwInt32      GfaceIndOffset = 0;
static RwInt32      GvertexIndOffset = 0;
static RwInt32      nhsFlags;

static LLStack      pivotOffsetStack; /* of [RwV3d] */

static LLinkList    clumpList;  /* of *RpClump */
static LLinkList    lightList;  /* of *RpLight */

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

   Builder.c functions

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

RwBool
Build_Init(void)
{
    RWFUNCTION(RWSTRING("Build_Init"));

    currentLTM = RwMatrixCreate();
    if (!currentLTM)
    {
        RWRETURN(FALSE);
    }

    Stack_Init(&pivotOffsetStack);

    if (GbuildType == CLUMP)
    {
        Stack_Init(&framePtrStack);

        clump = RpClumpCreate();
        if (!clump)
        {
            RWRETURN(FALSE);
        }

        rootFrame = RwFrameCreate();
        if (!rootFrame)
        {
            RWRETURN(FALSE);
        }

        if (!Stack_Push(&framePtrStack, rootFrame))
        {
            RWRETURN(FALSE);
        }

        RpClumpSetFrame(clump, rootFrame);
    }
    else if ((GbuildType == WORLD) || (GbuildType == SCENE))
    {
        Stack_Init(&matrixPtrStack);

        /* allocate a structure for the NoHS world */
        nhsWorld = RtWorldImportCreate();
        if (!nhsWorld)
        {
            RWRETURN(FALSE);
        }

        if (GbuildType == SCENE)
        {
            Stack_Init(&framePtrStack);
            LLinkList_Init(&clumpList);
        }
    }

    /* light list */
    LLinkList_Init(&lightList);

    RWRETURN(TRUE);
}

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

    clump = (RpClump *)NULL;
    rootFrame = (RwFrame *)NULL;
    world = (RpWorld *)NULL;
    nhsWorld = (RtWorldImport *)NULL;
    nhsFlags = 0;

    RWRETURNVOID();
}

void
Build_WorldAccumulateFlags(RwInt32 flags)
{
    RWFUNCTION(RWSTRING("Build_WorldAccumulateFlags"));

    nhsFlags |= flags;

    RWRETURNVOID();
}

RwBool
Build_ConvertScene(void)
{
    RWFUNCTION(RWSTRING("Build_ConvertScene"));

    if ((GbuildType == WORLD) || (GbuildType == SCENE))
    {
        RtWorldImportParameters worldConvert;

        /* copy the material list */
        Material_CopyList(&nhsWorld->matList);

        RtWorldImportParametersInitialize(&worldConvert);
        worldConvert.flags = nhsFlags | rpWORLDPRELIT;

        if (nhsWorld->numPolygons != 0)
        {
            world = RtWorldImportCreateWorld(nhsWorld, &worldConvert);
        }
        /* temp hack */
        else
        {
            RwBBox              bb;

            /* Create a world (just make it BIG!) */
            bb.inf.x = bb.inf.y = bb.inf.z = -1000.0f;
            bb.sup.x = bb.sup.y = bb.sup.z = 1000.0f;
            world = RpWorldCreate(&bb);
        }

        if (world)
        {
            const RwV3d        *worldOrigin;

            worldOrigin = RpWorldGetOrigin(world);

            /* add any clumps */
            if ((GbuildType == SCENE))
            {
                RpClump            *newClump;

                LLinkList_IteratorReset(&clumpList);
                while ((newClump =
                        (RpClump *) LLinkList_IteratorNext(&clumpList)))
                {
                    RwFrame            *frame;

                    frame = RpClumpGetFrame(newClump);
                    RwFrameTranslate(frame, worldOrigin,
                                     rwCOMBINEPOSTCONCAT);
                    RpWorldAddClump(world, newClump);
                }
            }

            /* add any lights */
            {
                RpLight            *light;

                LLinkList_IteratorReset(&lightList);
                while ((light =
                        (RpLight *) LLinkList_IteratorNext(&lightList)))
                {
                    RwFrame            *frame;

                    frame = RpLightGetFrame(light);
                    RwFrameTranslate(frame, worldOrigin,
                                     rwCOMBINEPOSTCONCAT);
                    RpWorldAddLight(world, light);
                }
            }

        }

        /* set it up for next time */
        if (GbuildType == SCENE)
        {
            LLinkList_Destroy(&clumpList, (linkCallback) NULL);
            LLinkList_Init(&clumpList);
        }

        /* set up the light list for next time */
        LLinkList_Destroy(&lightList, (linkCallback) NULL);
        LLinkList_Init(&lightList);

        RtWorldImportDestroy(nhsWorld);
    }

    RWRETURN(TRUE);
}

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

    if (currentLTM)
    {
        RwMatrixDestroy(currentLTM);
    }

    RWRETURNVOID();
}

RwFrame            *
Build_PushFrame(RwFrame * frame)
{
    RWFUNCTION(RWSTRING("Build_PushFrame"));
    RWASSERT(frame);

    if (frame)
    {
        if (!Stack_Push(&framePtrStack, frame))
        {
            RWRETURN(FALSE);
        }

        currentFrameStackDepth++;
        if (currentFrameStackDepth > maxFrameStackRec)
        {
            maxFrameStackRec = currentFrameStackDepth;
        }

        RWRETURN(frame);
    }

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

RwMatrix           *
Build_PushTransform(void)
{
    RwMatrix           *matrix = (RwMatrix *)NULL;
    buildType           bt;

    RWFUNCTION(RWSTRING("Build_PushTransform"));

    bt = Build_GetType();

    if (bt == CLUMP)
    {
        RwFrame            *frame = RwFrameCreate();

        if (!frame)
        {
            RWRETURN(((RwMatrix *)NULL));
        }

        if (!Build_PushFrame(frame))
        {
            RwFrameDestroy(frame);

            RWRETURN((RwMatrix *)NULL);
        }

        matrix = RwFrameGetMatrix(frame);
    }
    else                       /* WORLD */
    {
        matrix = RwMatrixCreate();
        if (!matrix)
        {
            RWRETURN((RwMatrix *)NULL);
        }

        if (!Stack_Push(&matrixPtrStack, matrix))
        {
            RwMatrixDestroy(matrix);

            RWRETURN((RwMatrix *)NULL);
        }
    }

    RWRETURN(matrix);
}

void
Build_PopFrame(void)
{
    RwFrame            *childFrame;

    RWFUNCTION(RWSTRING("Build_PopFrame"));

    childFrame = (RwFrame *) Stack_Pop(&framePtrStack);

    if (Stack_IsEmpty(&framePtrStack))
    {
        RwFrameAddChild(rootFrame, childFrame);
    }
    else
    {
        RwFrame            *parentFrame =
            (RwFrame *) Stack_Top(&framePtrStack);

        RwFrameAddChild(parentFrame, childFrame);
    }
    currentFrameStackDepth--;

    RWRETURNVOID();
}

RwFrame            *
Build_GetCurrentFrame(void)
{
    RwFrame            *frame;

    RWFUNCTION(RWSTRING("Build_GetCurrentFrame"));

    frame = (RwFrame *) Stack_Top(&framePtrStack);

    RWRETURN(frame);
}

void
Build_PopTransform(void)
{
    buildType           bt;

    RWFUNCTION(RWSTRING("Build_PopTransform"));

    bt = Build_GetType();

    if (bt == CLUMP)
    {
        Build_PopFrame();
    }
    else
    {
        /* WORLD */
        RwMatrix           *matrix;

        if ((matrix = (RwMatrix *) Stack_Pop(&matrixPtrStack)))
        {
            RwMatrixDestroy(matrix);
        }
    }

    RWRETURNVOID();
}

RwMatrix           *
Build_GetCurrentLTM(void)
{
    LLinkList          *matrixList;
    RwMatrix           *matrix;

    RWFUNCTION(RWSTRING("Build_GetCurrentLTM"));

    matrixList = &matrixPtrStack.StackList;
    RwMatrixSetIdentity(currentLTM);
    LLinkList_IteratorReset(matrixList);
    while ((matrix = (RwMatrix *) LLinkList_IteratorNext(matrixList)))
    {
        RwMatrixMultiply(currentLTM, currentLTM, matrix);
    }

    RWRETURN(currentLTM);
}

RwBool
Build_AddAtomic(RpAtomic * atomic)
{
    RWFUNCTION(RWSTRING("Build_AddAtomic"));
    RWASSERT(atomic);

    if (atomic)
    {
        RwFrame            *frame;

        frame = (RwFrame *) Stack_Top(&framePtrStack);
        RpAtomicSetFrame(atomic, frame);
        RpClumpAddAtomic(clump, atomic);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
Build_AddGeometry(RpGeometry * geometry)
{
    RWFUNCTION(RWSTRING("Build_AddGeometry"));
    RWASSERT(geometry);

    if (geometry)
    {
        RpAtomic           *atomic;

        atomic = RpAtomicCreate();
        if (!atomic)
        {
            RWRETURN(FALSE);
        }

        /* add the geometry */
        RpAtomicSetGeometry(atomic, geometry, 0);
        /* destroy to decrease the refrence count */
        RpGeometryDestroy(geometry);
        Build_AddAtomic(atomic);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RpWorld            *
Build_WorldGet(void)
{
    RWFUNCTION(RWSTRING("Build_WorldGet"));

    RWRETURN(world);
}

RpClump            *
Build_ClumpGet(void)
{
    RWFUNCTION(RWSTRING("Build_ClumpGet"));

    RWASSERT(GbuildType == CLUMP);

    RWRETURN(clump);
}

void
Build_SetType(buildType bt)
{
    RWFUNCTION(RWSTRING("Build_SetType"));

    GbuildType = bt;

    RWRETURNVOID();
}

buildType
Build_GetType(void)
{
    RWFUNCTION(RWSTRING("Build_GetType"));

    switch (GbuildType)
    {
        case CLUMP:
            {
                RWRETURN(CLUMP);
            }
        case WORLD:
            {
                RWRETURN(WORLD);
            }
        case SCENE:
            {
                if (GgeomType == GEOM_DYNAMIC)
                {
                    RWRETURN(CLUMP);
                }
                else
                {
                    RWRETURN(WORLD);
                }
            }

        case BUILDTYPEFORCEENUMSIZEINT:
            break;
    }

    RWRETURN(WORLD);
}

RtWorldImportVertex *
Build_WorldAddMoreVertices(RwInt32 numVertices)
{
    RwInt32             vertexIndOffset;

    RWFUNCTION(RWSTRING("Build_WorldAddMoreVertices"));

    vertexIndOffset = RtWorldImportGetNumVertices(nhsWorld);
    GvertexIndOffset = vertexIndOffset;

    if (RtWorldImportAddNumVertices(nhsWorld, numVertices))
    {
        RtWorldImportVertex *vertices;

        vertices = RtWorldImportGetVertices(nhsWorld);
        vertices += vertexIndOffset;

        /* initialize the vertices */
        if (vertices)
        {
            RtWorldImportVertex *vert = vertices;
            RwRGBA              cColour;
            RwInt32             i;

            /* set defualt values for prelight */
            if (Material_GetMatNodeFlag())
            {
                RwRGBAReal          nColour;

                sfcolor            *sfc;

                sfc = Material_GetCurrentEmissiveColor();

                nColour.red = (RwReal) (sfc->r);
                nColour.green = (RwReal) (sfc->g);
                nColour.blue = (RwReal) (sfc->b);
                nColour.alpha = (RwReal) (1);
                RwRGBAFromRwRGBAReal(&cColour, &nColour);
            }
            else
            {
                cColour.red = cColour.green = cColour.blue = 255;
                cColour.alpha = 255;
            }

            for (i = 1; i < numVertices; i++)
            {
                vert->OC.x = (RwReal) (0);
                vert->OC.y = (RwReal) (0);
                vert->OC.z = (RwReal) (0);

                vert->texCoords[0].u = 0;
                vert->texCoords[0].v = 0;

                vert->preLitCol = cColour;

                vert++;
            }

        }

        RWRETURN(vertices);
    }

    RWRETURN((RtWorldImportVertex *)NULL);
}

RtWorldImportTriangle *
Build_WorldAddMoreTriangles(RwInt32 numTriFaces)
{
    RwInt32             triangleIndOffset;

    RWFUNCTION(RWSTRING("Build_WorldAddMoreTriangles"));

    triangleIndOffset = RtWorldImportGetNumTriangles(nhsWorld);
    GfaceIndOffset = triangleIndOffset;

    if (RtWorldImportAddNumTriangles(nhsWorld, numTriFaces))
    {
        RtWorldImportTriangle *triangles;

        triangles = RtWorldImportGetTriangles(nhsWorld);
        triangles += triangleIndOffset;

        RWRETURN(triangles);
    }

    RWRETURN((RtWorldImportTriangle *)NULL);
}

RwInt32
Build_WorldGetFaceIndOffset(void)
{
    RWFUNCTION(RWSTRING("Build_WorldGetFaceIndOffset"));

    RWRETURN(GfaceIndOffset);
}

RwInt32
Build_WorldGetVertexIndOffset(void)
{
    RWFUNCTION(RWSTRING("Build_WorldGetVertexIndOffset"));

    RWRETURN(GvertexIndOffset);
}

RwBool
Build_SetScaleFactor(RwReal scaleFactor)
{
    RWFUNCTION("Build_SetScaleFactor");
    RWASSERT(scaleFactor > (RwReal) (0));

    if (scaleFactor > (RwReal) (0))
    {
        GscaleFactor = scaleFactor;

        RWRETURN(TRUE);
    }

    RWERROR((E_RP_VRML_SCALEFACTOR, scaleFactor));
    RWRETURN(FALSE);
}

RwReal
Build_GetScaleFactor(void)
{
    RWFUNCTION("Build_GetScaleFactor");

    RWRETURN(GscaleFactor);
}

RwBool
Build_PushPivotOffset(sfvec3f * pivotOffet)
{
    RWFUNCTION("Build_PushPivotOffset");
    RWASSERT(pivotOffet);

    if (pivotOffet)
    {
        if (!Stack_Push(&pivotOffsetStack, pivotOffet))
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
Build_PopPivotOffset(void)
{
    RWFUNCTION("Build_PopPivotOffset");

    if (!Stack_Pop(&pivotOffsetStack))
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

sfvec3f            *
Build_GetPivotOffset(void)
{
    sfvec3f            *pivotOffset;

    RWFUNCTION("Build_GetPivotOffset");

    pivotOffset = (sfvec3f *) Stack_Top(&pivotOffsetStack);

    RWRETURN(pivotOffset);
}

/* 1 - dynamic, 0 - static */
geomType
Build_GetGeomType(void)
{
    RWFUNCTION("Build_GetGeomType");

    RWRETURN(GgeomType);
}

void
Build_SetGeomType(geomType isDynamic)
{
    RWFUNCTION("Build_SetGeomType");

    GgeomType = isDynamic;

    if (GbuildType == SCENE)
    {
        Build_NewBeginClump();
    }

    RWRETURNVOID();
}

RpClump            *
Build_NewBeginClump(void)
{
    RpClump            *newClump;
    RwMatrix           *matrix, *LTM;

    RWFUNCTION("Build_NewBeginClump");

    newClump = RpClumpCreate();
    if (!newClump)
    {
        RWRETURN((RpClump *)NULL);
    }

    rootFrame = RwFrameCreate();
    if (!rootFrame)
    {
        RpClumpDestroy(newClump);

        RWRETURN((RpClump *)NULL);
    }

    LTM = Build_GetCurrentLTM();
    matrix = RwFrameGetMatrix(rootFrame);
    *matrix = *LTM;

    if (!Stack_Push(&framePtrStack, rootFrame))
    {
        RpClumpDestroy(newClump);

        RWRETURN((RpClump *)NULL);
    }
    RpClumpSetFrame(newClump, rootFrame);

    if (!LLinkList_AddData(&clumpList, newClump))
    {
        RpClumpDestroy(newClump);

        RWRETURN((RpClump *)NULL);
    }

    clump = newClump;

    RWRETURN(clump);
}

RwBool
Build_SceneAddLight(RpLight * light)
{
    RWFUNCTION("Build_SceneAddLight");
    RWASSERT(light);

    if (light)
    {
        if ((GbuildType == WORLD) || (GbuildType == SCENE))
        {
            RwFrame            *frame;
            RwMatrix            matrix, *matrixLight, *LTM;

            frame = RpLightGetFrame(light);
            if (!frame)
            {
                RWRETURN(FALSE);
            }

            matrixLight = RwFrameGetMatrix(frame);

            /* move the light into world space */
            LTM = Build_GetCurrentLTM();
            RwMatrixMultiply(&matrix, LTM, matrixLight);
            *matrixLight = matrix;

            /* add to the worlds list of lights */
            if (!LLinkList_AddData(&lightList, light))
            {
                RWRETURN(FALSE);
            }

            RWRETURN(TRUE);
        }
    }

    RWRETURN(FALSE);
}
