/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright © 2000-2002 The OGRE Team
Also see acknowledgements in Readme.html

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.
-----------------------------------------------------------------------------
*/
#include "OgreBspLevel.h"
#include "OgreBspResourceManager.h"
#include "OgreException.h"
#include "OgreMaterial.h"
#include "OgreMaterialManager.h"
#include "OgreMovableObject.h"
#include "OgreSceneManager.h"
#include "OgrePatchSurface.h"
#include "OgreQuake3ShaderManager.h"
#include "OgreQuake3Shader.h"
#include "OgreMath.h"
#include "OgreStringVector.h"
#include "OgreStringConverter.h"
#include "OgreLogManager.h"
#include "OgreSceneManagerEnumerator.h"
#include "OgreTechnique.h"
#include "OgrePass.h"
#include "OgreTextureUnitState.h"


namespace Ogre {


    //-----------------------------------------------------------------------
    BspLevel::BspLevel(const String& name)
    {
        mName = name;
        mRootNode = 0;
        mBrushes = 0;
        mVertexData = 0;
        mFaceGroups = 0;
        mLeafFaceGroups = 0;
        mVisData.tableData = 0;
    }

    //-----------------------------------------------------------------------
    BspLevel::~BspLevel()
    {
        if (mIsLoaded)
        {
            unload();
            mIsLoaded = false;
        }

    }

    //-----------------------------------------------------------------------
    void BspLevel::load()
    {
        // Use Quake3 file loader
        Quake3Level q3;
        DataChunk chunk;
        BspResourceManager::getSingleton()._findResourceData(mName, chunk);

        q3.loadFromChunk(chunk);

        loadQuake3Level(q3);

        chunk.clear();
        mIsLoaded = true;

    }

    //-----------------------------------------------------------------------
    void BspLevel::unload()
    {
        if (mVertexData)
            delete mVertexData;
        if (mFaceGroups)
            delete [] mFaceGroups;
        if (mLeafFaceGroups)
            delete [] mLeafFaceGroups;
        if (mRootNode)
            delete [] mRootNode;
        if (mVisData.tableData)
            delete [] mVisData.tableData;
        if (mBrushes)
            delete [] mBrushes;

        mVertexData = 0;
        mRootNode = 0;
        mFaceGroups = 0;
        mLeafFaceGroups = 0;
        mBrushes = 0;
        mVisData.tableData = 0;
    }

    //-----------------------------------------------------------------------
    void BspLevel::loadQuake3Level(const Quake3Level& q3lvl)
    {
        SceneManager* sm = SceneManagerEnumerator::getSingleton().getSceneManager(ST_INTERIOR);

        loadEntities(q3lvl);

        // Parse shaders
        Quake3ShaderManager::getSingleton().parseAllSources(".shader");
        // Extract lightmaps into textures
        q3lvl.extractLightmaps();

        //-----------------------------------------------------------------------
        // Vertices
        //-----------------------------------------------------------------------
        // Allocate memory for vertices & copy
        mVertexData = new VertexData();

        /// Create vertex declaration
        VertexDeclaration* decl = mVertexData->vertexDeclaration;
        size_t offset = 0;
        decl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
        offset += VertexElement::getTypeSize(VET_FLOAT3);
        decl->addElement(0, offset, VET_FLOAT3, VES_NORMAL);
        offset += VertexElement::getTypeSize(VET_FLOAT3);
        decl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
        offset += VertexElement::getTypeSize(VET_COLOUR);
        decl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
        offset += VertexElement::getTypeSize(VET_FLOAT2);
        decl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 1);

        // Build initial patches - we need to know how big the vertex buffer needs to be
        // to accommodate the subdivision
        initQuake3Patches(q3lvl, decl);

        /// Create the vertex buffer, allow space for patches
        HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton()
            .createVertexBuffer(
                sizeof(BspVertex), 
                q3lvl.mNumVertices + mPatchVertexCount, 
                HardwareBuffer::HBU_STATIC_WRITE_ONLY);
        //COPY static vertex data - Note that we can't just block-copy the vertex data because we have to reorder
        //    our vertex elements; this is to ensure compatibility with older cards when using
        //    hardware vertex buffers - Direct3D requires that the buffer format maps onto a
        //    FVF in those older drivers. 
        // Lock just the non-patch area for now
        BspVertex* pVert = static_cast<BspVertex*>(
            vbuf->lock(0, q3lvl.mNumVertices * sizeof(BspVertex), HardwareBuffer::HBL_DISCARD) );
        // Keep another base pointer for use later in patch building
        for (int v = 0; v < q3lvl.mNumVertices; ++v)
        {
            quakeVertexToBspVertex(&q3lvl.mVertices[v], pVert++);
        }
        vbuf->unlock();
        // Setup binding
        mVertexData->vertexBufferBinding->setBinding(0, vbuf);
        // Set other data
        mVertexData->vertexStart = 0;
        mVertexData->vertexCount = q3lvl.mNumVertices + mPatchVertexCount;

        //-----------------------------------------------------------------------
        // Faces
        // --------
        mNumLeafFaceGroups = q3lvl.mNumLeafFaces;
        mLeafFaceGroups = new int[mNumLeafFaceGroups];
        memcpy(mLeafFaceGroups, q3lvl.mLeafFaces, sizeof(int)*mNumLeafFaceGroups);
        mNumFaceGroups = q3lvl.mNumFaces;
        mFaceGroups = new StaticFaceGroup[mNumFaceGroups];
        // Set up index buffer
        // NB Quake3 indexes are 32-bit
        // Copy the indexes into a software area for staging
        mNumIndexes = q3lvl.mNumElements + mPatchIndexCount;
        // Create an index buffer manually in system memory, allow space for patches
        mIndexes = new DefaultHardwareIndexBuffer(
            HardwareIndexBuffer::IT_32BIT, 
            mNumIndexes, 
            HardwareBuffer::HBU_DYNAMIC);
        // Write main indexes
        mIndexes->writeData(0, sizeof(unsigned int) * q3lvl.mNumElements, q3lvl.mElements, true);

        // now build patch information
        buildQuake3Patches(q3lvl.mNumVertices, q3lvl.mNumElements);

        //-----------------------------------------------------------------------
        // Create materials for shaders
        //-----------------------------------------------------------------------
        // NB this only works for the 'default' shaders for now
        //  i.e. those that don't have a .shader script and thus default
        //  to just texture + lightmap
        // TODO: pre-parse all .shader files and create lookup for next stage (use ROGL shader_file_t)

        // Material names are shadername#lightmapnumber
        // This is because I like to define materials up front completely
        //  rather than combine lightmap and shader dynamically (it's
        //  more generic). It results in more materials, but they're small
        //  beer anyway. Texture duplication is prevented by infrastructure.
        // To do this I actually need to parse the faces since they have the
        //  shader/lightmap combo (lightmap number is not in the shader since
        //  it can be used with multiple lightmaps)
        char shaderName[256];
        int face;
        face = q3lvl.mNumFaces;
        int matHandle;
        String meshName;

        while(face--)
        {

            // Check to see if existing material
            // Format shader#lightmap
            int shadIdx = q3lvl.mFaces[face].shader;
            sprintf(shaderName, "%s#%d",
                q3lvl.mShaders[shadIdx].name,
                q3lvl.mFaces[face].lm_texture);

            Material *shadMat = sm->getMaterial(shaderName);
            if (shadMat == 0)
            {
                // Build new material

                // Colour layer
                // NB no extension in Q3A(doh), have to try shader, .jpg, .tga
                String tryName = q3lvl.mShaders[shadIdx].name;
                // Try shader first
                Quake3Shader* pShad = (Quake3Shader*)Quake3ShaderManager::getSingleton().getByName(tryName);
                if (pShad)
                {
                    shadMat = pShad->createAsMaterial(sm, q3lvl.mFaces[face].lm_texture);
                }
                else
                {
                    // No shader script, try default type texture
                    shadMat = sm->createMaterial(shaderName);
                    Pass *shadPass = shadMat->getTechnique(0)->getPass(0);
                    // Try jpg
                    TextureUnitState* tex = shadPass->createTextureUnitState(tryName + ".jpg");
                    tex->_load();
                    if (tex->isBlank())
                    {
                        // Try tga
                        tex->setTextureName(tryName + ".tga");
                    }
                    // Set replace on all first layer textures for now
                    tex->setColourOperation(LBO_REPLACE);
                    tex->setTextureAddressingMode(TextureUnitState::TAM_WRAP);

                    if (q3lvl.mFaces[face].lm_texture != -1)
                    {
                        // Add lightmap, additive blending
                        char lightmapName[16];
                        sprintf(lightmapName, "@lightmap%d",q3lvl.mFaces[face].lm_texture);
                        tex = shadPass->createTextureUnitState(lightmapName);
                        // Blend
                        tex->setColourOperation(LBO_MODULATE);
                        // Use 2nd texture co-ordinate set
                        tex->setTextureCoordSet(1);
                        // Clamp
                        tex->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);

                    }
                    // Set culling mode to none
                    shadMat->setCullingMode(CULL_NONE);
                    // No dynamic lighting
                    shadMat->setLightingEnabled(false);

                }
            }
            matHandle = shadMat->getHandle();
            shadMat->load();

            // Copy face data
            StaticFaceGroup* dest = &mFaceGroups[face];
            bsp_face_t* src = &q3lvl.mFaces[face];

            if (q3lvl.mShaders[src->shader].surface_flags & SURF_SKY)
            {
                dest->isSky = true;
            }
            else
            {
                dest->isSky = false;
            }


            dest->materialHandle = matHandle;
            dest->elementStart = src->elem_start;
            dest->numElements = src->elem_count;
            dest->numVertices = src->vert_count;
            dest->vertexStart = src->vert_start;
            if (src->type == BSP_FACETYPE_NORMAL)
            {
                dest->fType = FGT_FACE_LIST;
                // Assign plane
                dest->plane.normal = Vector3(src->normal);
                dest->plane.d = -dest->plane.normal.dotProduct(Vector3(src->org));

                // Don't rebase indexes here - Quake3 re-uses some indexes for multiple vertex
                // groups eg repeating small details have the same relative vertex data but
                // use the same index data.

            }
            else if (src->type == BSP_FACETYPE_PATCH)
            {
                // Seems to be some crap in the Q3 level where vertex count = 0 or num control points = 0?
                if (dest->numVertices == 0 || src->mesh_cp[0] == 0)
                {
                    dest->fType = FGT_UNKNOWN;
                }
                else
                {

                    // Set up patch surface
                    dest->fType = FGT_PATCH;
                    
                    // Locate the patch we already built
                    PatchMap::iterator p = mPatches.find(face);
                    if (p == mPatches.end())
                    {
                        Except(Exception::ERR_INTERNAL_ERROR, "Patch not found from previous built state",
                        "BspLevel::loadQuake3Level");
                    }

                    dest->patchSurf = p->second;
                    
                }


            }
			else if (src->type == BSP_FACETYPE_MESH)
			{
				dest->fType = FGT_FACE_LIST;
				// Assign plane
				dest->plane.normal = Vector3(src->normal);
				dest->plane.d = -dest->plane.normal.dotProduct(Vector3(src->org));                
			}
			else
			{
				LogManager::getSingleton().logMessage("!!! Unknown Face Type !!!", LML_CRITICAL);
			}


        }

        //-----------------------------------------------------------------------
        // Nodes
        //-----------------------------------------------------------------------
        // Allocate memory for all nodes (leaves and splitters)
        mNumNodes = q3lvl.mNumNodes + q3lvl.mNumLeaves;
        mNumLeaves = q3lvl.mNumLeaves;
        mLeafStart = q3lvl.mNumNodes;
        mRootNode = new BspNode[mNumNodes];
        int i;
        // Convert nodes
        // In our array, first q3lvl.mNumNodes are non-leaf, others are leaves
        for (i = 0; i < q3lvl.mNumNodes; ++i)
        {
            BspNode* node = &mRootNode[i];
            bsp_node_t* q3node = &q3lvl.mNodes[i];

            // Set non-leaf
            node->mIsLeaf = false;
            // Set owner
            node->mOwner = this;
            // Set plane
            node->mSplitPlane.normal = Vector3(q3lvl.mPlanes[q3node->plane].normal);
            node->mSplitPlane.d = -q3lvl.mPlanes[q3node->plane].dist;
            // Set bounding box
            node->mBounds.setMinimum(Vector3(&q3node->bbox[0]));
            node->mBounds.setMaximum(Vector3(&q3node->bbox[3]));
            // Set back pointer
            // Negative indexes in Quake3 mean leaves
            if (q3node->back < 0)
            {
                // Points to leaf, offset to leaf start and negate index
                node->mBack = &mRootNode[mLeafStart + (~(q3node->back))];

            }
            else
            {
                // Points to node
                node->mBack = &mRootNode[q3node->back];
            }
            // Set front pointer
            // Negative indexes in Quake3 mean leaves
            if (q3node->front < 0)
            {
                // Points to leaf, offset to leaf start and negate index
                node->mFront = &mRootNode[mLeafStart + (~(q3node->front))];

            }
            else
            {
                // Points to node
                node->mFront = &mRootNode[q3node->front];
            }


        }
        //-----------------------------------------------------------------------
        // Brushes
        //-----------------------------------------------------------------------
        // Reserve enough memory for all brushes, solid or not (need to maintain indexes)
        mBrushes = new BspNode::Brush[q3lvl.mNumBrushes];
        for (i = 0; i < q3lvl.mNumBrushes; ++i)
        {
            bsp_brush_t* q3brush = &q3lvl.mBrushes[i];

            // Create a new OGRE brush
            BspNode::Brush *pBrush = &(mBrushes[i]);
            int brushSideIdx, numBrushSides;
            numBrushSides = q3brush->numsides;
            brushSideIdx = q3brush->firstside;
            // Iterate over the sides and create plane for each
            while (numBrushSides--)
            {
                bsp_brushside_t* q3brushside = &q3lvl.mBrushSides[brushSideIdx];
                bsp_plane_t* q3brushplane = &q3lvl.mPlanes[q3brushside->planenum];
                // Notice how we normally invert Q3A plane distances, but here we do not
                // Because we want plane normals pointing out of solid brushes, not in
                Plane brushSide(Vector3(q3brushplane->normal), q3brushplane->dist);
                pBrush->planes.push_back(brushSide);
                ++brushSideIdx;
            }
            // Build world fragment
            pBrush->fragment.fragmentType = SceneQuery::WFT_PLANE_BOUNDED_REGION;
            pBrush->fragment.planes = &(pBrush->planes);

        }



        //-----------------------------------------------------------------------
        // Leaves
        //-----------------------------------------------------------------------
        for (i = 0; i < q3lvl.mNumLeaves; ++i)
        {
            BspNode* node = &mRootNode[i + mLeafStart];
            bsp_leaf_t* q3leaf = &q3lvl.mLeaves[i];

            // Set leaf
            node->mIsLeaf = true;
            // Set owner
            node->mOwner = this;
            // Set bounding box
            node->mBounds.setMinimum(Vector3(&q3leaf->bbox[0]));
            node->mBounds.setMaximum(Vector3(&q3leaf->bbox[3]));
            // Set faces
            node->mFaceGroupStart = q3leaf->face_start;
            node->mNumFaceGroups = q3leaf->face_count;

            node->mVisCluster = q3leaf->cluster;

            // Load Brushes for this leaf
            int brushIdx, brushCount, realBrushIdx;
            brushCount = q3leaf->brush_count;
            brushIdx = q3leaf->brush_start;

            while(brushCount--)
            {
                realBrushIdx = q3lvl.mLeafBrushes[brushIdx];
                bsp_brush_t* q3brush = &q3lvl.mBrushes[realBrushIdx];
                // Only load solid ones, we don't care about any other types
                // Shader determines this
                bsp_shader_t* brushShader = &q3lvl.mShaders[q3brush->shaderIndex];
                if (brushShader->content_flags & CONTENTS_SOLID)
                {
                    // Get brush 
                    BspNode::Brush *pBrush = &(mBrushes[realBrushIdx]);
                    assert(pBrush->fragment.fragmentType == SceneQuery::WFT_PLANE_BOUNDED_REGION);
                    // Assign node pointer
                    node->mSolidBrushes.push_back(pBrush);
                }
                ++brushIdx;
            }

        }



        // Vis - just copy
        mVisData.numClusters = q3lvl.mVis->cluster_count;
        mVisData.rowLength = q3lvl.mVis->row_size;
        mVisData.tableData = new unsigned char[q3lvl.mVis->row_size * q3lvl.mVis->cluster_count];
        memcpy(mVisData.tableData, q3lvl.mVis->data, q3lvl.mVis->row_size * q3lvl.mVis->cluster_count);



    }

    //-----------------------------------------------------------------------
    void BspLevel::initQuake3Patches(const Quake3Level & q3lvl, VertexDeclaration* decl)
    {
        int face;

        mPatchVertexCount = 0;
        mPatchIndexCount = 0;

        // We're just building the patch here to get a hold on the size of the mesh
        // although we'll reuse this information later
        face = q3lvl.mNumFaces;
        while (face--)
        {

            bsp_face_t* src = &q3lvl.mFaces[face];

            if (src->type == BSP_FACETYPE_PATCH)
            {
                // Seems to be some crap in the Q3 level where vertex count = 0 or num control points = 0?
                if (src->vert_count == 0 || src->mesh_cp[0] == 0)
                {
                    continue;
                }
                PatchSurface* ps = new PatchSurface();
                // Set up control points & format
                // Reuse the vertex declaration 
                // Copy control points into a buffer so we can convert their format
                BspVertex* pControlPoints = new BspVertex[src->vert_count];
                bsp_vertex_t* pSrc = q3lvl.mVertices + src->vert_start;
                for (int v = 0; v < src->vert_count; ++v)
                {
                    quakeVertexToBspVertex(pSrc++, &pControlPoints[v]);
                }
                // Define the surface, but don't build it yet (no vertex / index buffer)
                ps->defineSurface(
                    pControlPoints,
                    decl, 
                    src->mesh_cp[0],
                    src->mesh_cp[1],
                    PatchSurface::PST_BEZIER);
                // Get stats
                mPatchVertexCount += ps->getRequiredVertexCount();
                mPatchIndexCount += ps->getRequiredIndexCount();

                // Save the surface for later
                mPatches[face] = ps;
            }


        }

    }
    //-----------------------------------------------------------------------
    void BspLevel::buildQuake3Patches(size_t vertOffset, size_t indexOffset)
    {
        // Loop through the patches
        PatchMap::iterator i, iend;
        iend = mPatches.end();

        size_t currVertOffset = vertOffset;
        size_t currIndexOffset = indexOffset;

        HardwareVertexBufferSharedPtr vbuf = mVertexData->vertexBufferBinding->getBuffer(0);

        for (i = mPatches.begin(); i != iend; ++i)
        {
            PatchSurface* ps = i->second;
            
            ps->build(vbuf, currVertOffset, mIndexes, currIndexOffset);

            // No need for control points anymore
            BspVertex* pCP = static_cast<BspVertex*>(ps->getControlPointBuffer());
            delete [] pCP;
            ps->notifyControlPointBufferDeallocated();

            currVertOffset += ps->getRequiredVertexCount();
            currIndexOffset += ps->getRequiredIndexCount();
        
        }
    }
    //-----------------------------------------------------------------------
    bool BspLevel::isLeafVisible(const BspNode* from, const BspNode* to) const
    {
        if (to->mVisCluster == -1)
            return false;
        if (from->mVisCluster == -1)
            // Camera outside world?
            return true;


        if (!from->isLeaf() || !to->isLeaf())
            throw Exception(Exception::ERR_INVALIDPARAMS,
                "Both nodes must be leaf nodes for visibility testing.",
                "BspLevel::isLeafVisible");

        // Use PVS to determine visibility

        /*
        // In wordier terms, the fairly cryptic (but fast) version is doing this:
        //   Could make it a macro for even more speed?

        // Row offset = from cluster number * row size
        int offset = from->mVisCluster*mVisData.rowLength;

        // Column offset (in bytes) = to cluster number divided by 8 (since 8 bits per bytes)
        offset += to->mVisCluster >> 3;

        // Get the right bit within the byte, i.e. bitwise 'and' with bit at remainder position
        int result = *(mVisData.tableData + offset) & (1 << (to->mVisCluster & 7));

        return (result != 0);
        */

        //return ((*(mVisData.tableData + from->mVisCluster * mVisData.rowLength +
        //           ((to->mVisCluster)>>3)) & (1 << ((to->mVisCluster) & 7))) != 0);

        return (*(mVisData.tableData + from->mVisCluster*mVisData.rowLength +
                   ((to->mVisCluster)>>3)) & (1 << ((to->mVisCluster) & 7))) != 0;

    }
    //-----------------------------------------------------------------------
    const BspNode* BspLevel::getRootNode(void)
    {
        return mRootNode;
    }
    //-----------------------------------------------------------------------
    BspNode* BspLevel::findLeaf(const Vector3& point) const
    {
        BspNode* node = mRootNode;

        while (!node->isLeaf())
        {
            node = node->getNextNode(point);
        }

        return node;

    }
    //-----------------------------------------------------------------------
    void BspLevel::loadEntities(const Quake3Level& q3lvl)
    {
        char* strEnt;
        String line;
        StringVector vecparams;
        Vector3 origin;
        Radian angle ( 0 );
        size_t pos;
        char* lineend;
        bool isPlayerStart;

        isPlayerStart = false;
        strEnt = (char*)q3lvl.mEntities;

        lineend = strchr(strEnt, '\n');
        while (lineend != 0)
        {
            *lineend = '\0';
            line = strEnt;
            strEnt = lineend+1;
            StringUtil::trim(line);
            if (line.length() > 0)
            {
                StringUtil::toLowerCase(line);
                // Remove quotes
				while( ( pos = line.find("\"",0) ) != String::npos )
                {
                    line = line.substr(0,pos) + line.substr(pos+1,line.length()-(pos+1));
                }
                vecparams = StringUtil::split(line);
                StringVector::iterator params = vecparams.begin();

                if (params[0] == "origin")
                {
                    origin.x = atof(params[1].c_str());
                    origin.y = atof(params[2].c_str());
                    origin.z = atof(params[3].c_str());
                }
                if (params[0] == "angle")
                {
                    angle = Degree(atof(params[1].c_str()));
                }
                if (params[0] == "classname" && params[1] == "info_player_deathmatch")
                {
                    isPlayerStart = true;
                }
                if (params[0] == "}")
                {
                    if (isPlayerStart)
                    {
                        // Save player start
                        ViewPoint vp;
                        vp.position = origin;
                        vp.orientation.FromAngleAxis(angle, Vector3::UNIT_Z);
                        mPlayerStarts.push_back(vp);
                    }
                    isPlayerStart = false;
                }
            }

            lineend = strchr(strEnt, '\n');
        }


    }
    //-----------------------------------------------------------------------
    void BspLevel::_notifyObjectMoved(const MovableObject* mov, 
            const Vector3& pos)
    {

        // Locate any current nodes the object is supposed to be attached to
        MovableToNodeMap::iterator i = mMovableToNodeMap.find(mov);
        if (i != mMovableToNodeMap.end())
        {
            std::list<BspNode*>::iterator nodeit, nodeitend;
            nodeitend = i->second.end();
            for (nodeit = i->second.begin(); nodeit != nodeitend; ++nodeit)
            {
                // Tell each node
                (*nodeit)->_removeMovable(mov);
            }
            // Clear the existing list of nodes because we'll reevaluate it
            i->second.clear();
        }

        tagNodesWithMovable(mRootNode, mov, pos);
    }
    //-----------------------------------------------------------------------
    void BspLevel::tagNodesWithMovable(BspNode* node, const MovableObject* mov,
        const Vector3& pos)
    {
        if (node->isLeaf())
        {
            // Add to movable->node map
            // Insert all the time, will get current if already there
            std::pair<MovableToNodeMap::iterator, bool> p = 
                mMovableToNodeMap.insert(
                MovableToNodeMap::value_type(mov, std::list<BspNode*>()));

            p.first->second.push_back(node);

            // Add movable to node
            node->_addMovable(mov);

        }
        else
        {
            // Find distance to dividing plane
            Real dist = node->getDistance(pos);
            if (Math::Abs(dist) < mov->getBoundingRadius())
            {
                // Bounding sphere crosses the plane, do both
                tagNodesWithMovable(node->getBack(), mov, pos);
                tagNodesWithMovable(node->getFront(), mov, pos);
            }
            else if (dist < 0)
            {    //-----------------------------------------------------------------------

                // Do back
                tagNodesWithMovable(node->getBack(), mov, pos);
            }
            else
            {
                // Do front
                tagNodesWithMovable(node->getFront(), mov, pos);
            }
        }
    }
    //-----------------------------------------------------------------------
	void BspLevel::_notifyObjectDetached(const MovableObject* mov)	
	{
        // Locate any current nodes the object is supposed to be attached to
        MovableToNodeMap::iterator i = mMovableToNodeMap.find(mov);
        if (i != mMovableToNodeMap.end())
        {
            std::list<BspNode*>::iterator nodeit, nodeitend;
            nodeitend = i->second.end();
            for (nodeit = i->second.begin(); nodeit != nodeitend; ++nodeit)
            {
                // Tell each node
                (*nodeit)->_removeMovable(mov);
            }
            // delete the entry for this MovableObject
            mMovableToNodeMap.erase(i);
        }
	}
    //-----------------------------------------------------------------------
    void BspLevel::quakeVertexToBspVertex(const bsp_vertex_t* src, BspVertex* dest)
    {
        memcpy(dest->position, src->point, sizeof(Real) * 3);
        memcpy(dest->normal, src->normal,  sizeof(Real) * 3);
        dest->colour = src->color;
        dest->texcoords[0]  = src->texture[0];
        dest->texcoords[1]  = src->texture[1];
        dest->lightmap[0]  = src->lightmap[0];
        dest->lightmap[1]  = src->lightmap[1];
    }
}
