/**
 * \ingroup rtimport
 * \page rtworldimportoverview RtWorldImport Overview
 *
 * The functions in this toolkit are used to create a static world within an
 * \ref RpWorld object and generate the associated compressed BSP data.
 *
 * The developer would normally create an \ref RtWorldImport object, initialise
 * it with all the static data needed for the final \ref RpWorld object, fill
 * the \ref RtWorldImportParameters structure with the required settings and
 * then call \ref RtWorldImportCreateWorld to generate the BSP tree.
 *
 * Callbacks are provided to increase flexibility.
 *
 * Copyright Criterion Software Limited
 */

/*
 * Converting no hs worlds to real binary worlds (with bsps).
 * No HS worlds are used in the generation process of worlds
 *
 * The NoHS format of the world is a simplified format where all of the
 * materials, polygons and clumps are seperated out such that it is possible
 * to and save and load a world quickly. This form is not suitable for
 * rendering - and must be converted into a RwWorld before rendering can
 * occur
 */

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

#include <stdlib.h>
#include <math.h>
#include <string.h>

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

#include "rpdbgerr.h"

#include "nhsstats.h"
#include "nhsutil.h"
#include "nhsworld.h"
#include "nhscond.h"
#include "nhswing.h"
#include "nhssplit.h"

#include "rtimport.h"

static const char   rcsid[] __RWUNUSED__ =
    "@@(#)$Id: rtimport.c,v 1.129 2001/08/28 10:30:52 mattt Exp $";

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

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

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

/****************************************************************************
 Globals (across program)
 */

RwInt32             _rtWorldImportTotalPolysInWorld = 0;
RwInt32             _rtWorldImportNumPolysInLeaves = 0;
RwInt32             _rtWorldImportNumPolysInCompressedLeaves = 0;

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

static RwBool       ExitRequested = FALSE;

static RtWorldImportProgressCallBack progressCallBack =
    (RtWorldImportProgressCallBack) NULL;

static RtWorldImportUserdataCallBacks UserDataCallBacks = {
    (RtWorldImportDestroyVertexUserdataCallBack) NULL,
    (RtWorldImportCloneVertexUserdataCallBack) NULL,
    (RtWorldImportInterpVertexUserdataCallBack) NULL,
    (RtWorldImportDestroyPolygonUserdataCallBack) NULL,
    (RtWorldImportSplitPolygonUserdataCallBack) NULL,
    (RtWorldImportSectorSetVertexUserdataCallBack) NULL,
    (RtWorldImportSectorSetPolygonUserdataCallBack) NULL
};

/****************************************************************************
 Functions
 */

/****************************************************************************
 Static Functions
 */

static              RwBool
ImportWorldFreeVertices(RtWorldImport * nohsworld)
{
    RWFUNCTION(RWSTRING("ImportWorldFreeVertices"));
    RWASSERT(nohsworld);

    if (nohsworld)
    {
        if (nohsworld->vertices)
        {
            RtWorldImportDestroyVertexUserdataCallBack
                destroyVertexUserdata =
                UserDataCallBacks.destroyVertexUserdata;

            if (destroyVertexUserdata)
            {
                RwInt32             i;
                RtWorldImportVertex *vertices = nohsworld->vertices;

                for (i = 0; i < nohsworld->numVertices; i++)
                {
                    if (vertices[i].pUserdata)
                    {
                        destroyVertexUserdata(&vertices[i].pUserdata);
                    }
                }
            }
            RwFree(nohsworld->vertices);
            nohsworld->numVertices = 0;
        }

        RWRETURN(TRUE);
    }

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

static              RwBool
ImportWorldFreeTriangles(RtWorldImport * nohsworld)
{
    RWFUNCTION(RWSTRING("ImportWorldFreeTriangles"));
    RWASSERT(nohsworld);

    if (nohsworld)
    {
        if (nohsworld->polygons)
        {
            RtWorldImportDestroyPolygonUserdataCallBack
                destroyPolygonUserdata =
                UserDataCallBacks.destroyPolygonUserdata;

            if (destroyPolygonUserdata)
            {
                RwInt32             i;
                RtWorldImportTriangle *polygons = nohsworld->polygons;

                for (i = 0; i < nohsworld->numPolygons; i++)
                {
                    if (polygons[i].pUserdata)
                    {
                        destroyPolygonUserdata(&polygons[i].pUserdata);
                    }
                }
            }
            RwFree(nohsworld->polygons);
            nohsworld->numPolygons = 0;
        }

        RWRETURN(TRUE);
    }

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

/****************************************************************************
 SPI Functions
 */

void
_rtImportWorldSendProgressMessage(RwInt32 msgtype, RwReal value)
{
    RWFUNCTION(RWSTRING("_rtImportWorldSendProgressMessage"));
    if (progressCallBack)
    {
        if (!progressCallBack(msgtype, value))
        {
            ExitRequested = TRUE;
        }
    }

    RWRETURNVOID();
}

/****************************************************************************
 API Functions
 */

/***************************************************************************
                           Userdata CallBacks
 ***************************************************************************
 ***************************************************************************/

/* *INDENT-OFF* */

/**
 * \ingroup rtimport
 * \ref RtWorldImportSetUserdataCallBacks is used to define a set of
 * call-back functions that are called from \ref RtWorldImportCreateWorld in
 * order to allow userdata to be passed from an import world to an RpWorld.
 *
 * No callback functions are defined until this function is called.
 *
 * The world plugin must be attached before using this function.
 * The include file rtimport.h and the library file rtimport.lib are
 * also required.
 *
 * \param Pointers to the user data call-back functions.
 *
 * \return None.
 *
 * \see RtWorldImportDestroyVertexUserdataCallBack
 * \see RtWorldImportCloneVertexUserdataCallBack
 * \see RtWorldImportInterpVertexUserdataCallBack
 * \see RtWorldImportDestroyPolygonUserdataCallBack
 * \see RtWorldImportSplitPolygonUserdataCallBack
 * \see RtWorldImportCreateWorld
 * \see RpWorldPluginAttach
 */
void 
RtWorldImportSetUserdataCallBacks(RtWorldImportDestroyVertexUserdataCallBack destroyVertexUserdataCB,
                                  RtWorldImportCloneVertexUserdataCallBack cloneVertexUserdataCB,
                                  RtWorldImportInterpVertexUserdataCallBack interpVertexUserdataCB,
                                  RtWorldImportSectorSetVertexUserdataCallBack sectorSetVertexUserdataCB,
                                  RtWorldImportDestroyPolygonUserdataCallBack destroyPolygonUserdataCB,
                                  RtWorldImportSplitPolygonUserdataCallBack splitPolygonUserdataCB,
                                  RtWorldImportSectorSetPolygonUserdataCallBack sectorSetPolygonUserdataCB)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportSetUserdataCallBacks"));

    UserDataCallBacks.destroyVertexUserdata =
        destroyVertexUserdataCB;
    UserDataCallBacks.cloneVertexUserdata =
        cloneVertexUserdataCB;
    UserDataCallBacks.interpVertexUserdata =
        interpVertexUserdataCB;
    UserDataCallBacks.sectorSetVertexUserdata =
        sectorSetVertexUserdataCB;
    UserDataCallBacks.destroyPolygonUserdata =
        destroyPolygonUserdataCB;
    UserDataCallBacks.splitPolygonUserdata = 
        splitPolygonUserdataCB;
    UserDataCallBacks.sectorSetPolygonUserdata =
        sectorSetPolygonUserdataCB;

    RWRETURNVOID();
}

/* *INDENT-ON* */

/***************************************************************************
 ***************************************************************************
                           Progress Info
 ***************************************************************************
 ***************************************************************************/

/**
 * \ingroup rtimport
 * \ref RtWorldImportSetProgressCallBack is used to define a call-back
 * function that is called from \ref RtWorldImportCreateWorld at strategic
 * points to indicate the progress made in constructing a BSP world from
 * an import world.
 *
 * The format of the callback function is:
   \verbatim
  void (*RtWorldImportProgressCallBack) (RwInt32 message, RwReal value);
  \endverbatim
 * where message is one of the following,
 *      \li rtWORLDIMPORTPROGRESSBSPBUILDSTART The BSP creation process
 *           is about to start.
 *           The argument value is equal to 0.0.
 *      \li rtWORLDIMPORTPROGRESSBSPBUILDUPDATE The BSP creation process
 *           has finished processing a subsection of the world.
 *           The argument value is equal to the percentage of the
 *           world processed up to this point.
 *      \li rtWORLDIMPORTPROGRESSBSPBUILDEND The BSP creation process
 *           has ended. The argument value is equal to 100.0.
 *      \li rtWORLDIMPORTPROGRESSBSPCOMPRESSSTART The BSP compression process
 *           is about to start. The argument value is equal to 0.0.
 *      \li rtWORLDIMPORTPROGRESSBSPCOMPRESSUPDATE The BSP compression
 *            has finished processing a subsection of the world.
 *            The argument value is equal to the percentage of the world
 *            processed up to this point.
 *      \li rtWORLDIMPORTPROGRESSBSPCOMPRESSEND The BSP compression process
 *            has ended. The argument value is equal to 100.0.
 *
 * No callback function is defined until this function is called.
 *
 * The world plugin must be attached before using this function.
 * The include file rtimport.h and the library file rtimport.lib are
 * also required.
 *
 * \param progressCB  Pointer to the progress call-back function of type.
 *
 * \return None.
 *
 * \see RtWorldImportProgressCallBack
 * \see RtWorldImportCreateWorld
 * \see RpWorldPluginAttach
 */
void
RtWorldImportSetProgressCallBack(RtWorldImportProgressCallBack CB)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportSetProgressCallBack"));
    progressCallBack = CB;
    RWRETURNVOID();
}

/***************************************************************************
 ***************************************************************************
                         Reading and Writing
 ***************************************************************************
 ***************************************************************************/

/**
 * \ingroup rtimport
 * \ref RtWorldImportWrite is used to save a binary representation of the
 * specified import world to the given disk file.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param world  Pointer to the import world.
 * \param filename  Pointer to a string containing the name of the input file.
 *
 * \return Returns a pointer to the import world if successful
 *          or NULL if there is an error.
 *
 * \see RtWorldImportWrite
 * \see RpWorldPluginAttach
 */
RtWorldImport      *
RtWorldImportWrite(RtWorldImport * world, RwChar * filename)
{
    RwStream           *worldStream;

    RWAPIFUNCTION(RWSTRING("RtWorldImportWrite"));

    worldStream = RwStreamOpen(rwSTREAMFILENAME,
                               rwSTREAMWRITE, filename);

    if (!worldStream)
    {
        world = (RtWorldImport *) NULL;
    }
    else
    {
        RwStreamWriteInt(worldStream, &world->numPolygons,
                         sizeof(RwInt32));
        RwStreamWriteInt(worldStream, &world->numVertices,
                         sizeof(RwInt32));
        RwStreamWrite(worldStream, world->polygons,
                      sizeof(RtWorldImportTriangle) *
                      world->numPolygons);
        RwStreamWrite(worldStream, world->vertices,
                      sizeof(RtWorldImportVertex) * world->numVertices);
        RwStreamWrite(worldStream, &world->surfaceProps,
                      sizeof(RwSurfaceProperties));
        rpMaterialListStreamWrite(&world->matList, worldStream);

        RwStreamClose(worldStream, NULL);
    }

    RWRETURN(world);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportRead is used to load a binary representation of the
 * an import world from the given disk file.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param filename  Pointer to a string containing the name of the input file.
 *
 * \return Returns a pointer to the import world if successful or NULL if there
 * is an error.
 *
 * \see RtWorldImportWrite
 * \see RpWorldPluginAttach
 */
RtWorldImport      *
RtWorldImportRead(RwChar * filename)
{
    RwStream           *worldStream;
    RtWorldImport      *world;
    RwInt32             i;

    RWAPIFUNCTION(RWSTRING("RtWorldImportRead"));

    worldStream =
        RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename);
    if (worldStream)
    {
        world = (RtWorldImport *) RwMalloc(sizeof(RtWorldImport));
        RwStreamReadInt(worldStream, &world->numPolygons,
                        sizeof(RwInt32));
        RwStreamReadInt(worldStream, &world->numVertices,
                        sizeof(RwInt32));
        world->polygons = (RtWorldImportTriangle *)
            RwMalloc(sizeof(RtWorldImportTriangle) *
                     world->numPolygons);
        if (!world->polygons)
        {
            RWRETURN((RtWorldImport *) NULL);
        }
        RwStreamRead(worldStream, world->polygons,
                     sizeof(RtWorldImportTriangle) *
                     world->numPolygons);
        world->vertices = (RtWorldImportVertex *)
            RwMalloc(sizeof(RtWorldImportVertex) * world->numVertices);
        if (!world->vertices)
        {
            RWRETURN((RtWorldImport *) NULL);
        }
        RwStreamRead(worldStream, world->vertices,
                     sizeof(RtWorldImportVertex) * world->numVertices);
        /* Make all the userdata pointers NULL since we don't support
         * streaming of userdata */
        for (i = 0; i < world->numPolygons; i++)
        {
            world->polygons[i].pUserdata = NULL;
        }
        for (i = 0; i < world->numVertices; i++)
        {
            world->vertices[i].pUserdata = NULL;
        }
        RwStreamRead(worldStream, &world->surfaceProps,
                     sizeof(RwSurfaceProperties));
        RwStreamFindChunk(worldStream, rwID_MATLIST,
                          (RwUInt32 *) NULL, (RwUInt32 *) NULL);
        rpMaterialListStreamRead(worldStream, &world->matList);
        RwStreamClose(worldStream, NULL);
        RWRETURN(world);
    }
    RWRETURN((RtWorldImport *) NULL);
}

/***************************************************************************
 ***************************************************************************
                                Initializing
 ***************************************************************************
 ***************************************************************************/

/**
 * \ingroup rtimport
 * \ref RtWorldImportSetSurfaceProperties is used to define the
 * surface reflection coefficients of the specified import world.
 *
 * The world plugin must be attached before using this function. The
 * include file rtimport.h and the library file rtimport.lib are also
 * required to use this function.
 *
 * \param world  Pointer to the import world
 * \param surface  Pointer to a RwSurfaceProperties value
 *                 containing the reflection coefficients.
 *
 * \return Returns a pointer to the import world if successful or NULL
 * if there is an error.
 *
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportCreateWorld
 * \see RtWorldImportCreate
 * \see RtWorldImportDestroy
 * \see RpWorldPluginAttach
 */
RtWorldImport      *
RtWorldImportSetSurfaceProperties(RtWorldImport * world,
                                  RwSurfaceProperties * surface)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportSetSurfaceProperties"));
    RWASSERT(world);
    RWASSERT(surface);

    if (world && surface)
    {
        world->surfaceProps = *surface;
        RWRETURN(world);
    }

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

/**
 * \ingroup rtimport
 * \ref RtWorldImportGetSurfaceProperties is used to retrieve the surface
 * properties of the specified import world.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param world  Pointer to the import world.
 *
 * \return Returns a pointer to a \ref RwSurfaceProperties value
 *         if successful or NULL if there is an error.
 *
 * \see RtWorldImportSetSurfaceProperties
 * \see RtWorldImportCreateWorld
 * \see RtWorldImportCreate
 * \see RtWorldImportDestroy
 * \see RpWorldPluginAttach
 */
RwSurfaceProperties *
RtWorldImportGetSurfaceProperties(RtWorldImport * world)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportGetSurfaceProperties"));
    RWASSERT(world);

    if (world)
    {
        RWRETURN(&world->surfaceProps);
    }

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

/**
 * \ingroup rtimport
 * \ref RtWorldImportParametersInitialize is used to initialize the
 * specified world convert structure \ref RtWorldImportParameters with default
 * values. The world convert
 * structure is used by \ref RtWorldImportCreateWorld to produce a world
 * containing static geometry in the form of a compressed BSP tree. It
 * controls the way the new world is generated.
 *
 * The following default values are set:
 *      \li worldSectorMaxSize = RwRealMAXVAL
 *      \li maxWorldSectorPolygons = 1024
 *      \li spaceFillingSectors = TRUE
 *      \li maxClosestCheck = 20
 *      \li calcNormals = TRUE
 *      \li weldThreshold = (RwReal)0.001
 *      \li angularThreshold = (RwReal)0.8660254
 *      \li maxOverlapPercent = (RwReal)0.25
 *      \li noAlphaInOverlap = FALSE
 *      \li conditionGeometry = TRUE
 *      \li userSpecifiedBBox = FALSE
 *      \li uvLimit = (RwReal)16.0
 *      \li retainCreases = FALSE
 *      \li fixTJunctions = TRUE
 *      \li weldPolygons = TRUE
 *      \li flags: rpWORLDTEXTURED rpWORLDPRELIT rpWORLDNORMALS rpWORLDLIGHT
 *      \ii mode = rwTEXTUREADDRESSWRAP
 *      \li sortPolygons = FALSE
 *      \li cullZeroAreaPolygons = FALSE
 *      \li numTexCoordSets = 0  (ie use flags to determine this)
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param conversionParams  Pointer to the world convert structure.
 *
 * \return Returns pointer to the world convert structure if successful or NULL
 * if there is an error.
 *
 * \see RtWorldImportParameters
 * \see RtWorldImportSetSurfaceProperties
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportCreate
 * \see RpWorldPluginAttach
 */
RtWorldImportParameters *
RtWorldImportParametersInitialize(RtWorldImportParameters *
                                  conversionParams)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportParametersInitialize"));
    RWASSERT(conversionParams);

    if (conversionParams)
    {
        conversionParams->worldSectorMaxSize = RwRealMAXVAL;
        conversionParams->maxWorldSectorPolygons = 1024;
        conversionParams->spaceFillingSectors = TRUE;
        conversionParams->maxClosestCheck = 20;
        conversionParams->calcNormals = TRUE;
        conversionParams->weldThreshold = WELDTHRESHOLDPOSITIONAL;
        conversionParams->angularThreshold = WELDTHRESHOLDANGULAR;
        conversionParams->maxOverlapPercent = 0.25f;
        conversionParams->noAlphaInOverlap = FALSE;
        conversionParams->conditionGeometry = TRUE;
        conversionParams->userSpecifiedBBox = FALSE;
        conversionParams->uvLimit = 16.0f;
        conversionParams->retainCreases = FALSE;
        conversionParams->fixTJunctions = TRUE;
        conversionParams->generateCollTrees = TRUE;
        conversionParams->weldPolygons = TRUE;
        /* Set the default flags */
        conversionParams->flags = (rpWORLDTEXTURED |
                                   rpWORLDPRELIT |
                                   rpWORLDNORMALS | rpWORLDLIGHT);
        conversionParams->mode = rwTEXTUREADDRESSWRAP;
        conversionParams->sortPolygons = FALSE;
        conversionParams->cullZeroAreaPolygons = FALSE;
        /* Setting numTexCoordSets param to zero means the flags are used
         * to determine this setting. */
        conversionParams->numTexCoordSets = 0;

#if (defined(_MSC_VER))
#if (_MSC_VER>=1000)
        {
            RwInt32             sortPolygons = 0;
            RwInt32             Congruence_Off = 0;

            RWGETWINREGDWORD(sortPolygons, _T("sortPolygons"));
            RWMONITOR(("%d == sortPolygons", sortPolygons));
            if (sortPolygons)
            {
                conversionParams->sortPolygons = TRUE;
            }

            RWGETWINREGDWORD(Congruence_Off, _T("Congruence_Off"));
            RWMONITOR(("%d == Congruence_Off", Congruence_Off));
            if (Congruence_Off)
            {
                conversionParams->mode =
                    rwTEXTUREADDRESSNATEXTUREADDRESS;
            }

        }
#endif /*  (_MSC_VER>=1000) */
#endif /* (defined(_MSC_VER)) */

        RWRETURN(conversionParams);
    }

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

/*  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 * General utilities */

/**
 * \ingroup rtimport
 * \ref RtWorldImportAddNumVertices is used to allocate memory in the
 * specified import world for the given number of additional vertices.
 *
 * Note that this function does not initialize the new vertex array elements
 * (of type \ref RtWorldImportVertex) and the effect of this function is
 * cumulative.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 * \param numNewVertices  The number of vertices to add.
 *
 * \return Returns a pointer to the import world if successful or NULL if
 * there is an error.

 * \see RtWorldImportAddNumTriangles
 * \see RtWorldImportAddMaterial
 * \see RtWorldImportGetTriangles
 * \see RtWorldImportGetVertices
 * \see RtWorldImportCreate
 * \see RtWorldImportSetSurfaceProperties
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportCreateWorld
 * \see RpWorldPluginAttach
 */

RtWorldImport      *
RtWorldImportAddNumVertices(RtWorldImport * nohsworld,
                            RwInt32 numNewVertices)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportAddNumVertices"));

    if (!nohsworld)
    {
        RWERROR((E_RW_NULLP));
    }
    else if (0 < numNewVertices)
    {
        const RwInt32       numVerts =
            nohsworld->numVertices + numNewVertices;
        const RwUInt32      bytes =
            sizeof(RtWorldImportVertex) * numVerts;
        RwInt32             i;
        RtWorldImportVertex *vertices = nohsworld->vertices;

        if (!vertices)
        {
            /* for initial memory allocation use RwMalloc() */
            vertices = (RtWorldImportVertex *) RwMalloc(bytes);
        }
        else
        {
            vertices =
                (RtWorldImportVertex *) RwRealloc(vertices, bytes);
        }

        if (!vertices)
        {
            nohsworld->numVertices = 0;
            nohsworld->vertices = (RtWorldImportVertex *) NULL;

            RWRETURN((RtWorldImport *) NULL);
        }

        /* Make the new vertex userdata pointers NULL */
        for (i = nohsworld->numVertices; i < numVerts; i++)
        {
            vertices[i].pUserdata = NULL;
        }

        nohsworld->numVertices = numVerts;
        nohsworld->vertices = vertices;

    }

    RWRETURN(nohsworld);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportAddNumTriangles is used to allocate memory in the
 * specified import world for the given number of additional triangles.
 *
 * Note that this function does not initialize the new triangle array elements
 * (of type \ref RtWorldImportTriangle) and the effect of this function is
 * cumulative.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 * \param numNewTriangles  Number of triangles to add.
 *
 * \return Returns a pointer to the import world if successful or NULL if
 * there is an error.
 *
 * \see RtWorldImportAddNumVertices
 * \see RtWorldImportAddMaterial
 * \see RtWorldImportGetTriangles
 * \see RtWorldImportGetVertices
 * \see RtWorldImportCreate
 * \see RtWorldImportSetSurfaceProperties
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportCreateWorld
 * \see RpWorldPluginAttach
 */
RtWorldImport      *
RtWorldImportAddNumTriangles(RtWorldImport * nohsworld,
                             RwInt32 numNewTriangles)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportAddNumTriangles"));
    RWASSERT(nohsworld);

    if (!nohsworld)
    {

        RWERROR((E_RW_NULLP));
    }
    else if (0 < numNewTriangles)
    {
        const RwInt32       numFaces =
            nohsworld->numPolygons + numNewTriangles;
        const RwUInt32      bytes =
            sizeof(RtWorldImportTriangle) * numFaces;
        RwInt32             i;
        RtWorldImportTriangle *triangles = nohsworld->polygons;

        if (!triangles)
        {
            /* for initial memory allocation use RwMalloc() */
            triangles = (RtWorldImportTriangle *) RwMalloc(bytes);
        }
        else
        {
            triangles =
                (RtWorldImportTriangle *) RwRealloc(triangles, bytes);
        }

        if (!triangles)
        {
            nohsworld->numPolygons = 0;
            nohsworld->polygons = (RtWorldImportTriangle *) NULL;

            RWRETURN((RtWorldImport *) NULL);
        }

        /* Make the new polygon userdata pointers NULL */
        for (i = nohsworld->numPolygons; i < numFaces; i++)
        {
            triangles[i].pUserdata = NULL;
        }

        nohsworld->numPolygons = numFaces;
        nohsworld->polygons = triangles;

    }

    RWRETURN(nohsworld);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportGetMaterial is used to retrieve a material from the
 * specified import world with the given index into the import world’s
 * material list.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 * \param matInd Material index.
 *
 * \return Returns a pointer to the material if successful or NULL if there is
 * an error.
 *
 * \see RtWorldImportGetMaterialIndex
 * \see RtWorldImportGetVertices
 * \see RtWorldImportGetTriangles
 * \see RtWorldImportGetNumVertices
 * \see RtWorldImportGetNumTriangles
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportSetSurfaceProperties
 * \see RpWorldPluginAttach
 */
RpMaterial         *
RtWorldImportGetMaterial(RtWorldImport * nohsworld, RwInt32 matInd)
{
    RpMaterial         *result;

    RWAPIFUNCTION(RWSTRING("RtWorldImportGetMaterial"));
    RWASSERT(nohsworld);

    result = RtWorldImportGetMaterialMacro(nohsworld, matInd);

    RWRETURN(result);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportGetNumVertices is used to retrieve the number of
 * vertices defining the specified import world.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 *
 * \return Returns a RwInt32 value equal to the number of vertices if
 * successful or -1 if there is an error.
 *
 * \see RtWorldImportAddNumTriangles
 * \see RtWorldImportAddNumVertices
 * \see RtWorldImportGetVertices
 * \see RtWorldImportGetTriangles
 * \see RtWorldImportGetMaterial
 * \see RtWorldImportGetNumTriangles
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportSetSurfaceProperties
 * \see RpWorldPluginAttach
 */

RwInt32
RtWorldImportGetNumVertices(RtWorldImport * nohsworld)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportGetNumVertices"));
    RWASSERT(nohsworld);

    if (nohsworld)
    {
        RWRETURN(nohsworld->numVertices);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(-1);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportGetVertices is used to retrieve the array of
 * vertices of type \ref RtWorldImportVertex from the specified import world.
 * Use this function for directly defining the properties of each vertex.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 *
 * \return Returns an array of \ref RtWorldImportVertex values if successful or
 * NULL if there is an error.
 *
 * \see RtWorldImportAddNumTriangles
 * \see RtWorldImportAddNumVertices
 * \see RtWorldImportGetMaterial
 * \see RtWorldImportGetTriangles
 * \see RtWorldImportGetNumVertices
 * \see RtWorldImportGetNumTriangles
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportSetSurfaceProperties
 * \see RpWorldPluginAttach
 */
RtWorldImportVertex *
RtWorldImportGetVertices(RtWorldImport * nohsworld)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportGetVertices"));
    RWASSERT(nohsworld);

    if (nohsworld)
    {
        RWRETURN(nohsworld->vertices);
    }

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

/**
 * \ingroup rtimport
 * \ref RtWorldImportGetNumTriangles is used to retrieve the number of
 * triangles defining the specified import world.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 *
 * \return Returns a RwInt32 value equal to the number of triangles if
 * successful or -1 if there is an error.
 *
 * \see RtWorldImportAddNumTriangles
 * \see RtWorldImportAddNumVertices
 * \see RtWorldImportGetVertices
 * \see RtWorldImportGetTriangles
 * \see RtWorldImportGetNumVertices
 * \see RtWorldImportGetMaterial
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportSetSurfaceProperties
 * \see RpWorldPluginAttach
 */
RwInt32
RtWorldImportGetNumTriangles(RtWorldImport * nohsworld)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportGetNumTriangles"));
    RWASSERT(nohsworld);

    if (nohsworld)
    {
        RWRETURN(nohsworld->numPolygons);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(-1);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportGetTriangles is used to retrieve the array of
 * triangles (of type \ref RtWorldImportTriangle) from the specified import
 * world. Use this function for directly defining the properties of each
 * triangle.
 *
 * Use this function for directly defining the properties of each triangle:
 * \verbatim
   typedef struct
   {
   RwInt32 matIndex;  Index into material list.
   RwInt32 vertIndex[3]; Indices into vertex list.
   } RtWorldImportTriangle;
   \endverbatim
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 *
 * \return Returns an array of \ref RtWorldImportTriangle values if successful
 * or NULL if there is an error.
 *
 * \see RtWorldImportAddNumTriangles
 * \see RtWorldImportAddNumVertices
 * \see RtWorldImportGetVertices
 * \see RtWorldImportGetMaterial
 * \see RtWorldImportGetNumVertices
 * \see RtWorldImportGetNumTriangles
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportSetSurfaceProperties
 * \see RpWorldPluginAttach
 */

RtWorldImportTriangle *
RtWorldImportGetTriangles(RtWorldImport * nohsworld)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportGetTriangles"));
    RWASSERT(nohsworld);

    if (nohsworld)
    {
        RWRETURN(nohsworld->polygons);
    }

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

/**
 * \ingroup rtimport
 * \ref RtWorldImportAddMaterial is used to register the given material
 * with the specified import world. Once registered, this material can be
 * associated with any triangle constituting the import world via the returned
 * material index. All triangles must have a material before the import world
 * is converted to a BSP world.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 * \param material  Pointer to the material.
 *
 * \return Returns the material’s index if successful or -1 if there is an
 * error.
 *
 * \see RtWorldImportAddNumTriangles
 * \see RtWorldImportAddNumVertices
 * \see RtWorldImportGetTriangles
 * \see RtWorldImportGetVertices
 * \see RtWorldImportCreate
 * \see RtWorldImportSetSurfaceProperties
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportCreateWorld
 * \see RpWorldPluginAttach
 */
RwInt32
RtWorldImportAddMaterial(RtWorldImport * nohsworld,
                         RpMaterial * material)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportAddMaterial"));
    RWASSERT(nohsworld);
    RWASSERT(material);

    if (nohsworld && material)
    {
        RwInt32             matIndex;

        matIndex =
            rpMaterialListAppendMaterial(&nohsworld->matList, material);
        RWRETURN(matIndex);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(-1);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportGetMaterialIndex is used to retrieve the index of
 * the given material from the specified import world’s material list.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 * \param material  Pointer to the material.
 *
 * \return Returns the material’s index if successful or -1 if there
 * is an error.
 *
 * \see RtWorldImportGetMaterial
 * \see RtWorldImportGetVertices
 * \see RtWorldImportGetTriangles
 * \see RtWorldImportGetNumVertices
 * \see RtWorldImportGetNumTriangles
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportSetSurfaceProperties
 * \see RpWorldPluginAttach
 */
RwInt32
RtWorldImportGetMaterialIndex(RtWorldImport * nohsworld,
                              RpMaterial * material)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportGetMaterialIndex"));
    RWASSERT(nohsworld);
    RWASSERT(material);

    if (nohsworld && material)
    {
        RwInt32             i;

        /* If this material is in the list return the index else return -1 */
        for (i = 0; i < nohsworld->matList.numMaterials; i++)
        {
            if (material == nohsworld->matList.materials[i])
            {
                /* Found our material */
                RWRETURN(i);
            }
        }

        /* Not in the list */
        RWRETURN(-1);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(-1);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportForAllMaterials is used to apply the given callback
 * function to all materials registered with the specified import world. The
 * format of the callback function is:
 * \verbatim
   RpMaterial *(*RpMaterialCallBack)(RpMaterial *material, void *data);
   \endverbatim
 * where (p data) is a user-supplied data pointer to pass to the callback
 * function.
 *
 * Note that if any invocation of the callback function returns a failure
 * status the iteration is terminated. However,
 * \ref RtWorldImportForAllMaterials will still return successfully.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 * \param fpCallBack  Pointer to the callback function
 *                    to apply to each material.
 * \param pData  Pointer to user-supplied data to pass to callback function.
 *
 * \return Returns pointer to the import world if successful or NULL if there
 * is an error.
 *
 * \see RtWorldImportGetMaterial
 * \see RtWorldImportGetMaterialIndex
 * \see RtWorldImportGetVertices
 * \see RtWorldImportGetTriangles
 * \see RtWorldImportGetNumVertices
 * \see RtWorldImportGetNumTriangles
 * \see RpWorldPluginAttach
 */
RtWorldImport      *
RtWorldImportForAllMaterials(RtWorldImport * nohsworld,
                             RpMaterialCallBack fpCallBack, void *pData)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportForAllMaterials"));
    RWASSERT(nohsworld);
    RWASSERT(fpCallBack);

    if (nohsworld && fpCallBack)
    {
        RwInt32             numMaterials =
            nohsworld->matList.numMaterials;
        RwInt32             i;

        for (i = 0; i < numMaterials; i++)
        {
            RpMaterial         *material;

            material = RtWorldImportGetMaterial(nohsworld, i);

            RWASSERT(material);

            if (!fpCallBack(material, pData))
            {
                /* Early out */
                RWRETURN(nohsworld);
            }
        }

        /* All ok */
        RWRETURN(nohsworld);
    }

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

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

   Dividing up the world

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

#define _DOT_EPSILON (((RwReal)1)/((RwReal)(1<<10)))

/**
 * \ingroup rtimport
 * \ref RtWorldImportCreateWorld is used to create a BSP world from the
 * specified import world according to the given conversion parameters.
 *
 * A world uses a BSP mechanism to make collision detection and rendering
 * faster. An import world has no such mechanism and is a large collection of
 * vertices and polygons that describe the world.
 *
 * Note that all triangles must have an associated material before the import
 * world is converted to a BSP world.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 * \param conversionParams  Pointer to the world convert structure.
 *
 * \return Returns a pointer to the new world if successful or NULL if there
 * is an error.
 *
 * \see RtWorldImportParametersInitialize
 * \see RtWorldImportSetSurfaceProperties
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportSetProgressCallBack
 * \see RtWorldImportDestroy
 * \see RpWorldPluginAttach
 *
 * The BSP world structure is a modified Kd-tree;, see
 * \li "Multidimensional Binary Search Trees Used for Associative Searching" -
 *      ACM Sept. 1975 Vol. 18. No. 9
 * \li "An Algorithm for Finding Best Matches in Logarithmic Expected Time" -
 *      ACM Transactions of Mathematical Software
 *      Vol 3 No. 3 Sept. 1977 pp.  209-226
 * \li "Refinements to Nearest-Neighbor Searching in k-Dimensional Trees"
 *      J. Algorithmica 1990. pp. 579-589
 *  \li http://www.cs.sunysb.edu/~algorith/files/kd-trees.shtml
 */

RpWorld            *
RtWorldImportCreateWorld(RtWorldImport *nohsworld,
                         RtWorldImportParameters *params)
{
    RpWorld            *result = (RpWorld *) NULL;

    RWAPIFUNCTION(RWSTRING("RtWorldImportCreateWorld"));
    RWASSERT(nohsworld);
    RWASSERT(params);
    RWASSERT(params->numTexCoordSets <= rwMAXTEXTURECOORDS);

    if (nohsworld && params)
    {
        /* Multitexture specification consistency */
        if (params->numTexCoordSets == 0)
        {
            /* Set num tex coords from backward compatibility flags */
            params->numTexCoordSets =
                (params->flags & rpWORLDTEXTURED2) ? 2 :
                    ((params->flags & rpWORLDTEXTURED) ? 1 : 0);                    
        }
        else
        {
            /* Make sure backward compatibility flags set appropriately */
            params->flags &= ~(rpWORLDTEXTURED|rpWORLDTEXTURED2);
            params->flags |= 
                (params->numTexCoordSets == 1) ? rpWORLDTEXTURED :
                    ((params->numTexCoordSets > 1) ? rpWORLDTEXTURED2 : 0);

        }

        result = _rtImportWorldCreateWorld(nohsworld,
                                           params,
                                           &ExitRequested,
                                           &UserDataCallBacks);

    }
    else
    {
        RWERROR((E_RW_NULLP));
    }

    RWRETURN(result);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportCreateGeometry is used to create a geometry from
 * the specified import world according to the given conversion parameters.
 * The geometry is created with one morph target and with the following flags:
 * rpGEOMETRYLIGHT, rpGEOMETRYNORMALS, rpGEOMETRYTEXTURED and
 * rpGEOMETRYPRELIGHT.
 *
 * Note that all triangles must have an associated material before the import
 * world is converted to a geometry.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 * \param conversionParams  Pointer to the world convert
 *                          structure \ref RtWorldImportParameters
 *
 * \return Returns a pointer to the new geometry if successful or NULL if
 * there is an error.
 *
 * \see RtWorldImportCreateWorld
 * \see RtWorldImportParametersInitialize
 * \see RtWorldImportSetSurfaceProperties
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportSetProgressCallBack
 * \see RtWorldImportDestroy
 * \see RpWorldPluginAttach
 */
RpGeometry         *
RtWorldImportCreateGeometry(RtWorldImport * nohsworld,
                            RtWorldImportParameters * conversionParams)
{
    RtWorldImportBuildSector *buildSector;
    RpGeometry         *gpGeom;
    RpMorphTarget      *MorphTarget;
    RpTriangle         *tpTriangle;
    RwV3d              *vpPoints, *vpNormals;
    RwTexCoords        *Texcoords;
    RwSphere           *sphere;
    RwRGBA             *prelit;
    RwInt32             i, *vertMap, nI, nJ;

    RWAPIFUNCTION(RWSTRING("RtWorldImportCreateGeometry"));
    RWASSERT(nohsworld);
    RWASSERT(conversionParams);
    RWASSERT(nohsworld->numVertices < 65535);

    /* verify RtWorldImport */
    {
        RwInt32             nI;

        for (nI = 0; nI < nohsworld->numPolygons; nI++)
        {
            RwInt32             nK;

            const RwInt32       matIndex =
                nohsworld->polygons[nI].matIndex;
            RwInt32            *const vertIndex =
                nohsworld->polygons[nI].vertIndex;

            if (matIndex < 0)
            {
                RWRETURN((RpGeometry *) NULL);
            }
            if (matIndex >=
                rpMaterialListGetNumMaterials(&nohsworld->matList))
            {
                RWRETURN((RpGeometry *) NULL);
            }

            for (nK = 0; nK < 3; nK++)
            {
                if (vertIndex[nK] < 0)
                {
                    RWRETURN((RpGeometry *) NULL);
                }
                if (vertIndex[nK] >= nohsworld->numVertices)
                {
                    RWRETURN((RpGeometry *) NULL);
                }
            }
        }
    }

    _rtImportSetWeldThresholdPositional
        (conversionParams->weldThreshold);
    _rtImportSetWeldThresholdAngular
        (conversionParams->angularThreshold);

    /* Create a sector ready for use (and condition geometry) */
    buildSector =
        _rtImportBuildSectorCreateFromNoHSWorld(nohsworld,
                                                conversionParams,
                                                &nohsworld->matList,
                                                &UserDataCallBacks);
    if (!buildSector)
    {
        RWRETURN((RpGeometry *) NULL);
    }

    vertMap = ((RwInt32 *)
               RwMalloc(sizeof(RwInt32) * buildSector->numVertices));

    if (vertMap)
    {
        RwInt32             currentIndex = 0;

        /* clear the map */
        for (nJ = 0; nJ < buildSector->numVertices; nJ++)
        {
            vertMap[nJ] = -1;
        }

        /* first build a vertex map */
        for (nJ = 0;
             nJ < rpMaterialListGetNumMaterials(&nohsworld->matList);
             nJ++)
        {
            RtWorldImportBuildVertex *boundaries =
                buildSector->boundaries;

            for (nI = 0; nI < buildSector->numPolygons; nI++)
            {

                RwInt16             matIndex =
                    boundaries[3].pinfo.matIndex;

                if (matIndex == nJ)
                {
                    RtWorldImportBuildVertexMode *mode;
                    RwInt32             index;

                    /* the indices */

                    mode = &boundaries[0].mode;
                    index = mode->vpVert - buildSector->vertices;
                    if (-1 == vertMap[index])
                    {
                        vertMap[index] = currentIndex++;
                    }

                    mode = &boundaries[1].mode;
                    index = mode->vpVert - buildSector->vertices;
                    if (-1 == vertMap[index])
                    {
                        vertMap[index] = currentIndex++;
                    }

                    mode = &boundaries[2].mode;
                    index = mode->vpVert - buildSector->vertices;
                    if (-1 == vertMap[index])
                    {
                        vertMap[index] = currentIndex++;
                    }
                }

                boundaries += 4;
            }

        }
    }
    else
    {
        RWERROR((E_RW_NOMEM,
                 sizeof(RwInt32) * buildSector->numVertices));
        RWRETURN((RpGeometry *) NULL);
    }

    gpGeom = (RpGeometryCreate(buildSector->numVertices,
                               buildSector->numPolygons,
                               rpGEOMETRYLIGHT | rpGEOMETRYNORMALS |
                               rpGEOMETRYTEXTURED | rpGEOMETRYPRELIT));

    RpGeometryLock(gpGeom, rpGEOMETRYLOCKALL);
    MorphTarget = RpGeometryGetMorphTarget(gpGeom, 0);
    tpTriangle = RpGeometryGetTriangles(gpGeom);
    Texcoords = RpGeometryGetVertexTexCoords(gpGeom,
                                             rwTEXTURECOORDINATEINDEX0);
    prelit = RpGeometryGetPreLightColors(gpGeom);
    vpPoints = RpMorphTargetGetVertices(MorphTarget);
    vpNormals = RpMorphTargetGetVertexNormals(MorphTarget);

    rpMaterialListCopy(&gpGeom->matList, &nohsworld->matList);

    /* add geometry */
    for (i = 0; i < buildSector->numVertices; i++)
    {
        vpPoints[vertMap[i]] = buildSector->vertices[i].OC;
        vpNormals[vertMap[i]] = buildSector->vertices[i].normal;
        Texcoords[vertMap[i]] = buildSector->vertices[i].texCoords[0];
        prelit[vertMap[i]] = buildSector->vertices[i].preLitCol;
    }

    for (i = 0; i < buildSector->numPolygons; i++)
    {
        RwInt32             matid;
        RwUInt16            tv[3];
        RtWorldImportBuildVertexMode *mode;
        RwInt32             index;

        matid = buildSector->boundaries[i * 4 + 3].pinfo.matIndex;
        RpGeometryTriangleSetMaterial(gpGeom, &tpTriangle[i],
                                      gpGeom->matList.materials[matid]);

        mode = &buildSector->boundaries[i * 4 + 0].mode;
        index = mode->vpVert - buildSector->vertices;
        tv[0] = (RwUInt16) vertMap[index];

        mode = &buildSector->boundaries[i * 4 + 1].mode;
        index = mode->vpVert - buildSector->vertices;
        tv[1] = (RwUInt16) vertMap[index];

        mode = &buildSector->boundaries[i * 4 + 2].mode;
        index = mode->vpVert - buildSector->vertices;
        tv[2] = (RwUInt16) vertMap[index];

        RpGeometryTriangleSetVertexIndices(gpGeom, &tpTriangle[i],
                                           tv[0], tv[1], tv[2]);
    }

    /* init bounding volume */
    sphere = RpMorphTargetGetBoundingSphere(MorphTarget);
    RpMorphTargetCalcBoundingSphere(MorphTarget, sphere);
    RpMorphTargetSetBoundingSphere(MorphTarget, sphere);

    /* all done */
    RpGeometryUnlock(gpGeom);

    RwFree(vertMap);

    _rtImportBuildSectorDestroy(buildSector, &UserDataCallBacks);

    RWRETURN(gpGeom);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportCreate is used to create a new import world that
 * has no vertices or triangles.
 *
 * Vertices and triangles must be allocated and initialized before attempting
 * to create a world using \ref RtWorldImportCreateWorld. Use the function
 * \ref RtWorldImportDestroy to free this memory.
 *
 * Also, an \ref RtWorldImportParameters structure must be defined, and
 * initialized with \ref RtWorldImportParametersInitialize, for use in creating
 * a BSP world from the import world.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \return Returns a pointer to a new import world if successful or NULL if
 * there is an error.
 *
 * \see RtWorldImportDestroy
 * \see RtWorldImportAddNumVertices
 * \see RtWorldImportAddNumTriangles
 * \see RtWorldImportAddMaterial
 * \see RtWorldImportSetSurfaceProperties
 * \see RtWorldImportGetSurfaceProperties
 * \see RtWorldImportCreateWorld
 * \see RtWorldImportParametersInitialize
 * \see RtWorldImportRead
 * \see RtWorldImportWrite
 * \see RpWorldPluginAttach
 */
RtWorldImport      *
RtWorldImportCreate(void)
{
    RtWorldImport      *nohsworld;

    RWAPIFUNCTION(RWSTRING("RtWorldImportCreate"));

    nohsworld = (RtWorldImport *) RwMalloc(sizeof(RtWorldImport));

    if (!nohsworld)
    {
        RWRETURN((RtWorldImport *) NULL);
    }

    rpMaterialListInitialize(&nohsworld->matList);
    /* this world has no vertices or polygons */
    nohsworld->vertices = (RtWorldImportVertex *) NULL;
    nohsworld->numVertices = 0;
    nohsworld->polygons = (RtWorldImportTriangle *) NULL;
    nohsworld->numPolygons = 0;

    /* hard code some defaults in */
    nohsworld->surfaceProps.ambient = 1.0f;
    nohsworld->surfaceProps.diffuse = 1.0f;
    nohsworld->surfaceProps.specular = 0.0f;

    RWRETURN(nohsworld);
}

/**
 * \ingroup rtimport
 * \ref RtWorldImportDestroy is used to destroy the specified import
 * world. This function should be used to free the memory allocated to
 * triangles, vertices and materials created via \ref RtWorldImportCreate or
 * \ref RtWorldImportRead.
 *
 * The world plugin must be attached before using this function. The include
 * file rtimport.h and the library file rtimport.lib are also required.
 *
 * \param nohsworld  Pointer to the import world.
 *
 * \return Returns TRUE if successful or FALSE if there is an error.
 *
 * \see RtWorldImportCreate
 * \see RtWorldImportRead
 * \see RpWorldPluginAttach
 */
RwBool
RtWorldImportDestroy(RtWorldImport * nohsworld)
{
    RWAPIFUNCTION(RWSTRING("RtWorldImportDestroy"));
    RWASSERT(nohsworld);

    if (nohsworld)
    {
        /* Destroy all of the materials being referenced */
        rpMaterialListDeinitialize(&nohsworld->matList);

        /* Free the polygons */
        ImportWorldFreeTriangles(nohsworld);

        /* Free the vertices */
        ImportWorldFreeVertices(nohsworld);

        /* Free the world!! */
        RwFree(nohsworld);

        RWRETURN(TRUE);
    }

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