/*
 *
 * VRML to RW converter plug-in
 */

/** 
 * \ingroup rpvrml
 * \page rpvrmloverview RpVRML Plugin Overview
 *
 * The RpVrml plugin extends core RenderWare Graphics functionality by adding some 
 * Virtual Reality Mark-up Language (‘VRML’)  support for importing and 
 * exporting of VRML 2.0 scene description files.  
 *
 * RpVrml only supports ‘shape’ and ‘interpolator’ nodes. Other node types 
 * are ignored if encountered. Also, external prototypes are not supported.
 */

/****************************************************************************
 *                                                                         
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1997 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : Raniml.c
 * Module  : RpVrml.c
 *                                                                         
 * Purpose : RW3.0 interface to the vrml library
 *                         
 ****************************************************************************/

/****************************************************************************
 Includes
 */
#include <stdio.h>
#include "rpplugin.h"
#include "_rpvrml.h"
#include "parser.h"
#include "abstractnode.h"
#include "vrmlnodetype.h"
#include "fieldrec.h"
#include "llinklist.h"
#include "rpvrml.h"
#include "rpvrmlanim.h"
#include "animfuncttab.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: rpvrml.c,v 1.46 2001/03/15 16:53:00 katherinet Exp $";

/****************************************************************************
 Public Globals
 */

#if (defined(RWDEBUG))
long                rpVRMLStackDepth = 0;
#endif /* (defined(RWDEBUG)) */

RpVRMLAnimFunctions *GanimFuncts = (RpVRMLAnimFunctions *)NULL;

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

static RwModuleInfo vrmlModule;

#if (0)
static RwInt32      clumpVrmlSceneOffset = 0;
#endif /* (0) */

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

/****************************************************************************
 Local Defines
 */

typedef struct ViewPoint ViewPoint;
struct ViewPoint
{
    RwReal              fov;
    RwFrame            *frame;
}
                   ;

typedef struct VrmlSceneInfo VrmlSceneInfo;
struct VrmlSceneInfo
{
    LLinkList           viewPoints;
    LLinkList           lights;
    RwRGBAReal          bg;
}
                   ;

#define RWCLUMPGETVRMLSCENEINFO(object, offset) \
    ((VrmlSceneInfo *)(((RwUInt8 *)object) + offset))

#if (0)

/*
 * Retired Code
 */
static void        *
_VrmlSceneInfo_Dtor(void *object,
                    RwInt32 offsetInObject,
                    RwInt32 __RWUNUSED__ sizeInObject)
{
    VrmlSceneInfo      *vsi;

    RWFUNCTION(RWSTRING("_VrmlSceneInfo_Dtor"));

    vsi = RWCLUMPGETVRMLSCENEINFO(object, offsetInObject);

    RWRETURN(object);
}

static void        *
_VrmlSceneInfo_Ctor(void *object,
                    RwInt32 offsetInObject,
                    RwInt32 __RWUNUSED__ sizeInObject)
{
    VrmlSceneInfo      *vsi;

    RWFUNCTION(RWSTRING("_VrmlSceneInfo_Ctor"));

    vsi = RWCLUMPGETVRMLSCENEINFO(object, offsetInObject);

    LLinkList_Init(&vsi->viewPoints);
    LLinkList_Init(&vsi->lights);

    vsi->bg.red = (RwReal) (0);
    vsi->bg.green = (RwReal) (0);
    vsi->bg.blue = (RwReal) (0);
    vsi->bg.alpha = (RwReal) (0);

    RWRETURN(object);
}
#endif /* (0) */

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

   Vrml methods

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

static RwFrame     *
_moveFrameAnimOn(RwFrame * frame, void *pData)
{
    RwReal              delta = *(RwReal *) pData;

    RWFUNCTION(RWSTRING("_moveFrameAnimOn"));

    _rpVrmlFrameAnimAddTime(frame, delta);
    RwFrameForAllChildren(frame, _moveFrameAnimOn, pData);

    RWRETURN(frame);
}

void               *
_rpVrmlClose(void *instance,
             RwInt32 __RWUNUSED__ offset, RwInt32 __RWUNUSED__ size)
{
    RWFUNCTION("_rpVrmlClose");
    RWASSERT(instance);

    ParserEnd();
    FieldRecClose();
    AbstractNodeClose();
    VrmlNodeTypeClose();
    LLinkListClose();

    /* one less module instance */
    vrmlModule.numInstances--;

    RWRETURN(instance);
}

void               *
_rpVrmlOpen(void *instance, RwInt32 offset, RwInt32 __RWUNUSED__ size)
{
    RWFUNCTION("_rpVrmlOpen");
    RWASSERT(instance);

    if (instance)
    {
        if (!LLinkListOpen())
        {
            RWRETURN(NULL);
        }

        if (!VrmlNodeTypeOpen())
        {
            LLinkListClose();

            RWRETURN(NULL);
        }

        if (!AbstractNodeOpen())
        {
            LLinkListClose();
            VrmlNodeTypeClose();

            RWRETURN(NULL);
        }

        if (!FieldRecOpen())
        {
            LLinkListClose();
            VrmlNodeTypeClose();
            AbstractNodeClose();

            RWRETURN(NULL);
        }

        if (!ParserBegin())
        {
            LLinkListClose();
            VrmlNodeTypeClose();
            AbstractNodeClose();
            FieldRecClose();

            RWRETURN(NULL);
        }

#if 0
        /* VrmlSceneInfo */
        clumpVrmlSceneOffset = RpClumpRegisterPlugin(sizeof
                                                     (VrmlSceneInfo),
                                                     rwID_VRMLPLUGIN,
                                                     _VrmlSceneInfo_Ctor,
                                                     _VrmlSceneInfo_Dtor,
                                                     NULL);
#endif /* 0 */

        /* grab the global data offset (same for all instances) */
        vrmlModule.globalsOffset = offset;

        /* one more module instance */
        vrmlModule.numInstances++;

        RWRETURN(instance);
    }

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

#if 0
/**
 * \ingroup rpvrml
 * \ref RpVrmlSceneRead ToDo
 */
RpWorld            *
RpVrmlSceneRead(const char *filename, RwReal scale)
{
    RWAPIFUNCTION(RWSTRING("RpVrmlSceneRead"));
    RWASSERT(vrmlModule.numInstances);
    RWASSERT(filename);

    if (vrmlModule.numInstances)
    {
        if (filename)
        {
            RpWorld            *world = NULL;

            Build_SetType(SCENE);
            if (!Build_SetScaleFactor(scale))
            {
                RWRETURN(NULL);
            }

            if (ParseFile(filename))
            {
                world = Build_WorldGet();
            }

            RWRETURN(world);
        }

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

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN(NULL);
}
#endif /* 0 */

/**
 * \ingroup rpvrml
 * \ref RpVrmlWorldRead
 * is used to read the specified file containing a VRML
 * 2.0 scene description and convert the data to a RenderWare world. The
 * geometry created by this function resides in the static component of
 * the world and is scaled uniformly by the given scale factor.
 * 
 * The file name may either be absolute or relative: if the latter,
 * RpVrmlWorldRead expects to find the specified file in the same
 * directory the application is running from.
 * 
 * If the VRML file contains references to textures, RpVrmlWorldRead will
 * only search the directory set by RwImageSetPath for the texture
 * files. Reading of textures indicated to reside at external sites is
 * not supported but RpVrmlWorldRead will attempt to locate the texture
 * file in the current search directory.
 * 
 * The VRML plug-in must be attached before using this function.
 * \param filename  Pointer to a string containing the name of the VRML file.
 * \param scale  A RwReal value equal to a factor used to uniformly scale the
 * VRML model. Must be greater than zero.
 * \return
 * a pointer to the new world if successful or NULL if there is
 * an error.
 * \see RpVrmlClumpRead
 * \see RpVrmlPluginAttach
 * \see RwImageSetPath
 */
RpWorld            *
RpVrmlWorldRead(const char *filename, RwReal scale)
{
    RWAPIFUNCTION(RWSTRING("RpVrmlWorldRead"));
    RWASSERT(vrmlModule.numInstances);
    RWASSERT(filename);

    if (vrmlModule.numInstances)
    {
        if (filename)
        {
            RpWorld            *world = (RpWorld *)NULL;

            Build_SetType(WORLD);
            if (!Build_SetScaleFactor(scale))
            {
                RWRETURN((RpWorld *)NULL);
            }

            if (ParseFile(filename))
            {
                world = Build_WorldGet();
            }

            RWRETURN(world);
        }

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

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpWorld *)NULL);
}

/**
 * \ingroup rpvrml
 * \ref RpVrmlClumpRead
 * is used to read the specified file containing a VRML
 * 2.0 scene description and convert the data to a RenderWare clump. The
 * geometry in the clump is scaled uniformly by the given scale factor.
 * 
 * The file name may either be absolute or relative: if the latter,
 * RpVrmlClumpRead expects to find the specified file in the same
 * directory the application is running from.
 * 
 * If the VRML file contains references to textures, RpVrmlClumpRead will
 * only search the directory set by RwImageSetPath for the texture
 * files. Reading of textures indicated to reside at external sites is
 * not supported but RpVrmlClumpRead will attempt to locate the texture
 * file in the current search directory.
 * 
 * The VRML plug-in must be attached before using this function.
 * \param filename  Pointer to a string containing the name of the VRML file.
 * \param scale  A RwReal value equal to a factor used to uniformly scale the
 * VRML model. Must be greater than zero.
 * \return
 * pointer to the new clump if successful or NULL if there is an
 * error.
 * \see RpVrmlClumpAnimAddTime
 * \see RpVrmlWorldRead
 * \see RpVrmlPluginAttach
 * \see RwImageSetPath
 */
RpClump            *
RpVrmlClumpRead(const char *filename, RwReal scale)
{
    RWAPIFUNCTION(RWSTRING("RpVrmlClumpRead"));
    RWASSERT(vrmlModule.numInstances);
    RWASSERT(filename);

    if (vrmlModule.numInstances)
    {
        if (filename)
        {
            RpClump            *clump = (RpClump *)NULL;

            Build_SetType(CLUMP);
            if (!Build_SetScaleFactor(scale))
            {
                RWRETURN((RpClump *)NULL);
            }

            if (ParseFile(filename))
            {
                clump = Build_ClumpGet();
            }

            RWRETURN(clump);
        }

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

    RWERROR((E_RW_PLUGINNOTINIT));
    RWRETURN((RpClump *)NULL);
}

/**
 * \ingroup rpvrml
 * \ref RpVrmlClumpAnimAddTime
 * is used to advance the animation contained in
 * the specified clump (derived from a VRML model) by the given amount of
 * time. The time is measured in seconds and would normally represent the
 * time elapsed between two successive rendering frames.
 * 
 * The VRML plug-in must be attached before using this function.
 * \param clump  to the clump.
 * \param time  A RwReal value equal to the time increment.
 * \return
 * pointer to the clump if successful or NULL if there is an
 * error.
 * \see RpVrmlClumpRead
 * \see RpVrmlPluginAttach
 */
RpClump            *
RpVrmlClumpAnimAddTime(RpClump * clump, RwReal time)
{
    RWAPIFUNCTION(RWSTRING("RpVrmlClumpAnimAddTime"));
    RWASSERT(vrmlModule.numInstances);
    RWASSERT(clump);

    if (vrmlModule.numInstances)
    {
        if (clump)
        {
            RwFrame            *frame;

            if (!GanimFuncts)
            {
                GanimFuncts = _rpVrmAnimFunctionsGet();
            }

            frame = RpClumpGetFrame(clump);

            _moveFrameAnimOn(frame, &time);

            RWRETURN(clump);
        }

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

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

#if 0
/**
 * \ingroup rpvrml
 * \ref RpVrmlClump_GetNumViews ToDo
 */
RwInt32
RpVrmlClump_GetNumViews(RpClump * clump)
{
    VrmlSceneInfo      *vsi;
    RwInt32             numViews;

    RWAPIFUNCTION(RWSTRING("RpVrmlClump_GetNumViews"));

    vsi = RWCLUMPGETVRMLSCENEINFO(clump, clumpVrmlSceneOffset);

    numViews = LLinkList_NumItems(&vsi->viewPoints);

    RWRETURN((numViews));
}
#endif /* 0 */

/**
 * \ingroup rpvrml
 * \ref RpVrmlPluginAttach
 * is used to attach the VRML plug-in to the
 * RenderWare system to enable the loading of VRML 2.0 scene description
 * files. The VRML plug-in must be attached between initializing the
 * system with RwEngineInit and opening it with RwEngineOpen.
 * 
 * The VRML plug-in only supports shape and interpolator nodes. All other
 * node types are ignored if present in the input file. Also, external
 * prototypes are not supported: a warning is issued on the debug stream
 * (for debug versions of the plug-in library) and the global error
 * status is set if references to external prototypes are encountered in
 * a VRML file.
 * 
 * Note that the VRML plug-in requires the world plug-in to be
 * attached. The include file rpvrml.h is also required and must be
 * included by an application wishing to read and write VRML files.
 * 
 * Note also that when linking the VRML plug-in library, rpvrml.lib, into
 * an application, make sure the following RenderWare libraries are made
 * available to the linker: rtimport.lib and rtworld.lib.
 * \return
 * TRUE if successful or FALSE if there is an error.
 * \see RpWorldPluginAttach
 * \see RwEngineInit
 * \see RwEngineOpen
 * \see RwEngineStart
 */
RwBool
RpVrmlPluginAttach(void)
{
    RWAPIFUNCTION(RWSTRING("RpVrmlPluginAttach"));

    if (!RwEngineRegisterPlugin
        (0, rwID_VRMLPLUGIN, _rpVrmlAnimOpen, _rpVrmlClose))
    {
        RWRETURN(FALSE);
    }

    if (!_rpVrmlAnimPluginAttach())
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}
