//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#ifndef _SCENEOBJECT_H_
#define _SCENEOBJECT_H_

#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _NETOBJECT_H_
#include "sim/netObject.h"
#endif
#ifndef _COLLISION_H_
#include "collision/collision.h"
#endif
#ifndef _POLYHEDRON_H_
#include "collision/polyhedron.h"
#endif
#ifndef _ABSTRACTPOLYLIST_H_
#include "collision/abstractPolyList.h"
#endif
#ifndef _OBJECTTYPES_H_
#include "game/objectTypes.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _LIGHTMANAGER_H_
#include "sceneGraph/lightManager.h"
#endif

//-------------------------------------- Forward declarations...
class SceneObject;
class SceneGraph;
class SceneState;
class SceneRenderImage;
class Box3F;
class Point3F;
class LightManager;
class Convex;

//----------------------------------------------------------------------------
/// Extension of the collision structore to allow use with raycasting.
///
/// @see Collision
struct RayInfo: public Collision 
{
   // The collision struct has object, point, normal & material.
   F32 t;
};

//--------------------------------------------------------------------------

/// Reference to a scene object.
///
/// @note There are two indiscretions here.  First is the name, which refers rather
///       blatantly to the container bin system.  A hygiene issue.  Next is the
///       user defined U32, which is added solely for the zoning system.  This should
///       properly be split up into two structures, for the disparate purposes, especially
///       since it's not nice to force the container bin to use 20 bytes structures when
///       it could get away with a 16 byte version.
class SceneObjectRef
{
  public:
   SceneObject*    object;
   SceneObjectRef* nextInBin;
   SceneObjectRef* prevInBin;
   SceneObjectRef* nextInObj;

   U32             zone;
};

/// A scope frustum describes a pyramid to clip new portals against.  It is
///  rooted at the root position of the scoping query, which is not stored
///  here.
class ScopeFrustum
{
  public:
   enum Constants {
      TopLeft     = 0,
      TopRight    = 1,
      BottomLeft  = 2,
      BottomRight = 3
   };

   Point3F frustumPoints[4];
};


//----------------------------------------------------------------------------
class Container
{
public:
   struct Link
   {
      Link* next;
      Link* prev;
      Link();
      void unlink();
      void linkAfter(Link* ptr);
   };

   struct CallbackInfo 
   {
      AbstractPolyList* polyList;
      Box3F boundingBox;
      SphereF boundingSphere;
      void *key;
   };

   static const U32 csmNumBins;
   static const F32 csmBinSize;
   static const F32 csmTotalBinSize;
   static const U32 csmRefPoolBlockSize;
   static U32    smCurrSeqKey;

private:
   Link mStart,mEnd;

   SceneObjectRef*         mFreeRefPool;
   Vector<SceneObjectRef*> mRefPoolBlocks;

   SceneObjectRef* mBinArray;
   SceneObjectRef  mOverflowBin;

public:
   Container();
   ~Container();

   /// @name Basic database operations
   /// @{

   ///
   typedef void (*FindCallback)(SceneObject*,void *key);
   void findObjects(U32 mask, FindCallback, void *key = NULL);
   void findObjects(const Box3F& box, U32 mask, FindCallback, void *key = NULL);
   void polyhedronFindObjects(const Polyhedron& polyhedron, U32 mask,
                              FindCallback, void *key = NULL);
   /// @}

   /// @name Line intersection
   /// @{

   ///
   bool castRay(const Point3F &start, const Point3F &end, U32 mask, RayInfo* info);
   bool collideBox(const Point3F &start, const Point3F &end, U32 mask, RayInfo* info);
   /// @}

   /// @name Poly list
   /// @{

   ///
   bool buildPolyList(const Box3F& box, U32 mask, AbstractPolyList*,FindCallback=0,void *key = NULL);
   bool buildCollisionList(const Box3F& box, const Point3F& start, const Point3F& end, const VectorF& velocity,
      U32 mask,CollisionList* collisionList,FindCallback = 0,void *key = NULL,const Box3F *queryExpansion = 0);
   bool buildCollisionList(const Polyhedron& polyhedron,
                           const Point3F& start, const Point3F& end,
                           const VectorF& velocity,
                           U32 mask, CollisionList* collisionList,
                           FindCallback callback = 0, void *key = NULL);
   /// @}

   ///
   bool addObject(SceneObject*);
   bool removeObject(SceneObject*);

   void addRefPoolBlock();
   SceneObjectRef* allocateObjectRef();
   void freeObjectRef(SceneObjectRef*);
   void insertIntoBins(SceneObject*);
   void removeFromBins(SceneObject*);

   /// Checkbins makes sure that we're not just sticking the object right back
   /// where it came from.  The overloaded insertInto is so we don't calculate
   /// the ranges twice.
   void checkBins(SceneObject*);
   void insertIntoBins(SceneObject*, U32, U32, U32, U32);


private:
   Vector<SimObjectPtr<SceneObject>*>  mSearchList;///< Object searches to support console querying of the database.  ONLY WORKS ON SERVER
   S32                                 mCurrSearchPos;
   Point3F                             mSearchReferencePoint;
   void cleanupSearchVectors();

public:
   void initRadiusSearch(const Point3F& searchPoint,
                         const F32      searchRadius,
                         const U32      searchMask);
   U32  containerSearchNext();
   F32  containerSearchCurrDist();
   F32  containerSearchCurrRadiusDist();
};


//----------------------------------------------------------------------------
/// For simple queries.  Simply creates a vector of the objects
class SimpleQueryList
{
  public:
   Vector<SceneObject*> mList;

  public:
   SimpleQueryList() 
   {
      VECTOR_SET_ASSOCIATION(mList);
   }

   void insertObject(SceneObject* obj) { mList.push_back(obj); }
   static void insertionCallback(SceneObject* obj, void *key);
};


//----------------------------------------------------------------------------

/// A 3D object.
///
/// @section SceneObject_intro Introduction
///
/// SceneObject exists as a foundation for 3D objects in Torque. It provides the
/// basic functionality for:
///      - A scene graph (in the Zones and Portals sections), allowing efficient
///        and robust rendering of the game scene.
///      - Various helper functions, including functions to get bounding information
///        and momentum/velocity.
///      - Collision detection, as well as ray casting.
///      - Lighting. SceneObjects can register lights both at lightmap generation time,
///        and dynamic lights at runtime (for special effects, such as from flame or
///        a projectile, or from an explosion).
///      - Manipulating scene objects, for instance varying scale.
///
/// @section SceneObject_example An Example
///
/// Melv May has written a most marvelous example object deriving from SceneObject.
/// Unfortunately this page is too small to contain it.
///
/// @see http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=3217
///      for a copy of Melv's example.
class SceneObject : public NetObject, public Container::Link
{
   typedef NetObject Parent;
   friend class Container;
   friend class SceneGraph;
   friend class SceneState;

   //-------------------------------------- Public constants
public:
   enum 
   {
      MaxObjectZones = 128
   };
   
   enum TraversalState 
   {
      Pending = 0,
      Working = 1,
      Done = 2
   };
   
   enum SceneObjectMasks 
   {
      ScaleMask    = BIT(0),
      NextFreeMask = BIT(1)
   };

   //-------------------------------------- Public interfaces
   // C'tors and D'tors
private:
   SceneObject(const SceneObject&); ///< @deprecated disallowed

public:
   SceneObject();
   virtual ~SceneObject();

   /// Returns a value representing this object which can be passed to script functions.
   const char* scriptThis();

public:
   /// @name Collision and transform related interface
   ///
   /// The Render Transform is the interpolated transform with respect to the
   /// frame rate. The Render Transform will differ from the object transform
   /// because the simulation is updated in fixed intervals, which controls the
   /// object transform. The framerate is, most likely, higher than this rate,
   /// so that is why the render transform is interpolated and will differ slightly
   /// from the object transform.
   ///
   /// @{

   /// Disables collisions for this object including raycasts
   virtual void disableCollision();

   /// Enables collisions for this object
   virtual void enableCollision();

   /// Returns true if collisions are enabled
   bool         isCollisionEnabled() const { return mCollisionCount == 0; }

   /// Returns true if this object allows itself to be displaced
   /// @see displaceObject
   virtual bool    isDisplacable() const;

   /// Returns the momentum of this object
   virtual Point3F getMomentum() const;

   /// Sets the momentum of this object
   /// @param   momentum   Momentum
   virtual void    setMomentum(const Point3F &momentum);

   /// Returns the mass of this object
   virtual F32     getMass() const;

   /// Displaces this object by a vector
   /// @param   displaceVector   Displacement vector
   virtual bool    displaceObject(const Point3F& displaceVector);

   /// Returns the transform which can be used to convert object space
   /// to world space
   const MatrixF& getTransform() const      { return mObjToWorld; }

   /// Returns the transform which can be used to convert world space
   /// into object space
   const MatrixF& getWorldTransform() const { return mWorldToObj; }

   /// Returns the scale of the object
   const VectorF& getScale() const          { return mObjScale;   }

   /// Returns the bounding box for this object in local coordinates
   const Box3F&   getObjBox() const        { return mObjBox;      }

   /// Returns the bounding box for this object in world coordinates
   const Box3F&   getWorldBox() const      { return mWorldBox;    }

   /// Returns the bounding sphere for this object in world coordinates
   const SphereF& getWorldSphere() const   { return mWorldSphere; }

   /// Returns the center of the bounding box in world coordinates
   Point3F        getBoxCenter() const     { return (mWorldBox.min + mWorldBox.max) * 0.5f; }

   /// Sets the Object -> World transform
   ///
   /// @param   mat   New transform matrix
   virtual void setTransform(const MatrixF & mat);

   /// Sets the scale for the object
   /// @param   scale   Scaling values
   virtual void setScale(const VectorF & scale);

   /// This sets the render transform for this object
   /// @param   mat   New render transform
   virtual void   setRenderTransform(const MatrixF &mat);

   /// Returns the render transform
   const MatrixF& getRenderTransform() const      { return mRenderObjToWorld; }

   /// Returns the render transform to convert world to local coordinates
   const MatrixF& getRenderWorldTransform() const { return mRenderWorldToObj; }

   /// Returns the render world box
   const Box3F&   getRenderWorldBox()  const      { return mRenderWorldBox;   }

   /// Builds a convex hull for this object.
   ///
   /// Think of a convex hull as a low-res mesh which covers, as tightly as
   /// possible, the object mesh, and is used as a collision mesh.
   /// @param   box
   /// @param   convex   Convex mesh generated (out)
   virtual void     buildConvex(const Box3F& box,Convex* convex);

   /// Builds a list of polygons which intersect a bounding volume.
   ///
   /// This will use either the sphere or the box, not both, the
   /// SceneObject implimentation ignores sphere.
   ///
   /// @see AbstractPolyList
   /// @param   polyList   Poly list build (out)
   /// @param   box        Box bounding volume
   /// @param   sphere     Sphere bounding volume
   virtual bool     buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);

   /// Builds a collision tree of all the polygons which collide with a bounding volume.
   ///
   /// @note Not implemented in SceneObject. @see TerrainBlock::buildCollisionBSP
   /// @param   tree     BSP tree built (out)
   /// @param   box      Box bounding volume
   /// @param   sphere   Sphere bounding volume
   virtual BSPNode* buildCollisionBSP(BSPTree *tree, const Box3F &box, const SphereF &sphere);

   /// Casts a ray and obtain collision information, returns true if RayInfo is modified.
   ///
   /// @param   start   Start point of ray
   /// @param   end   End point of ray
   /// @param   info   Collision information obtained (out)
   virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);

   virtual bool collideBox(const Point3F &start, const Point3F &end, RayInfo* info);

   /// Returns the position of the object.
   Point3F  getPosition() const;

   /// Returns the render-position of the object.
   ///
   /// @see getRenderTransform
   Point3F  getRenderPosition() const;

   /// Sets the position of the object
   void     setPosition(const Point3F &pos);

   /// Gets the velocity of the object
   virtual Point3F getVelocity() const;

   /// Sets the velocity of the object
   /// @param  v  Velocity
   virtual void setVelocity(const Point3F &v);

   /// @}

public:
  /// @name Zones
  ///
  /// A zone is a portalized section of an InteriorInstance, and an InteriorInstance can manage more than one zone.
  /// There is always at least one zone in the world, zone 0, which represens the whole world. Any
  /// other zone will be in an InteriorInstance. Torque keeps track of the zones containing an object
  /// as it moves throughout the world. An object can exists in multiple zones at once.
  /// @{

   /// Returns true if this object is managing zones.
   ///
   /// This is only true in the case of InteriorInstances which have zones in them.
   bool isManagingZones() const;

   /// Gets the index of the first zone this object manages in the collection of zones.
   U32  getZoneRangeStart() const                         { return mZoneRangeStart;   }

   /// Gets the number of zones containing this object.
   U32  getNumCurrZones() const                           { return mNumCurrZones;     }

   /// Returns the nth zone containing this object.
   U32  getCurrZone(const U32 index) const;

   /// If an object exists in multiple zones, this method will give you the
   /// number and indices of these zones (storing them in the provided variables).
   ///
   /// @param   obj      Object in question.
   /// @param   zones    Indices of zones containing the object. (out)
   /// @param   numZones Number of elements in the returned array.  (out)
   virtual bool getOverlappingZones(SceneObject* obj, U32* zones, U32* numZones);

   /// Returns the zone containing p.
   ///
   /// @param   p   Point to test.
   virtual U32  getPointZone(const Point3F& p);

   /// This is called on a zone managing object to scope all the zones managed.
   ///
   /// @param   rootPosition   Camera position
   /// @param   rootDistance   Camera visible distance
   /// @param   zoneScopeState Array of booleans which line up with the collection of zones, marked true if that zone is scoped (out)
   virtual bool scopeObject(const Point3F&        rootPosition,
                            const F32             rootDistance,
                            bool*                 zoneScopeState);
   /// @}

   /// Called when the object is supposed to render itself.
   ///
   /// @param   state   Current rendering state.
   ///                  @see SceneState
   /// @param   image   Image associated with this object to render.
   ///                  @see SceneRenderImage
   virtual void renderObject(SceneState *state, SceneRenderImage *image);

   /// Called when the SceneGraph is ready for the registration of RenderImages.
   ///
   /// @see SceneState
   ///
   /// @param   state               SceneState
   /// @param   stateKey            State key of the current SceneState
   /// @param   startZone           Base zone index
   /// @param   modifyBaseZoneState If true, the object needs to modify the zone state.
   virtual bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone,
                                const bool modifyBaseZoneState = false);

   /// Adds object to the client or server container depending on the object
   void addToScene();

   /// Removes the object from the client/server container
   void removeFromScene();

   //-------------------------------------- Derived class interface
   // Overrides
protected:
   bool onAdd();
   void onRemove();

   // Overrideables
protected:
   /// Called when this is added to the SceneGraph.
   ///
   /// @param   graph   SceneGraph this is getting added to
   virtual bool onSceneAdd(SceneGraph *graph);

   /// Called when this is removed from the SceneGraph
   virtual void onSceneRemove();

   /// Called when the size of the object changes
   virtual void onScaleChanged();

   /// @name Portals
   /// @{

   /// This is used by a portal controling object to transform the base-modelview
   /// used by the scenegraph for rendering to the modelview it needs to render correctly.
   ///
   /// @see MirrorSubObject
   ///
   /// @param   portalIndex   Index of portal in the list of portals controlled by the object.
   /// @param   oldMV         Current modelview matrix used by the SceneGraph (in)
   /// @param   newMV         New modelview to be used by the SceneGraph (out)
   virtual void transformModelview(const U32 portalIndex, const MatrixF& oldMV, MatrixF* newMV);

   /// Used to tranform the position of a point based on a portal.
   ///
   /// @param   portalIndex   Index of a portal to transform by.
   /// @param   point         Point to transform.
   virtual void transformPosition(const U32 portalIndex, Point3F& point);

   /// Returns a new view frustum for the portal.
   ///
   /// @param   portalIndex   Which portal in the list of portals the object controls
   /// @param   oldFrustum    Current frustum.
   /// @param   nearPlane     Near clipping plane.
   /// @param   farPlane      Far clipping plane.
   /// @param   oldViewport   Current viewport.
   /// @param   newFrustum    New view frustum to use. (out)
   /// @param   newViewport   New viewport to use. (out)
   /// @param   flippedMatrix Should the object should use a flipped matrix to calculate viewport and frustum?
   virtual bool computeNewFrustum(const U32      portalIndex,
                                  const F64*     oldFrustum,
                                  const F64      nearPlane,
                                  const F64      farPlane,
                                  const RectI&   oldViewport,
                                  F64*           newFrustum,
                                  RectI&         newViewport,
                                  const bool     flippedMatrix);

   /// Called before things are to be rendered from the portals point of view, to set up
   /// everything the portal needs to render correctly.
   ///
   /// @param   portalIndex   Index of portal to use.
   /// @param   pCurrState    Current SceneState
   /// @param   pParentState  SceneState used before this portal was activated
   virtual void openPortal(const U32   portalIndex,
                           SceneState* pCurrState,
                           SceneState* pParentState);

   /// Called after rendering of a portal is complete, this resets the states
   /// the previous call to openPortal() changed.
   ///
   /// @param   portalIndex    Index of portal to use.
   /// @param   pCurrState     Current SceneState
   /// @param   pParentState   SceneState used before this portal was activated
   virtual void closePortal(const U32   portalIndex,
                            SceneState* pCurrState,
                            SceneState* pParentState);
public:

   /// Returns the plane of the portal in world space.
   ///
   /// @param   portalIndex   Index of portal to use.
   /// @param   plane         Plane of the portal in world space (out)
   virtual void getWSPortalPlane(const U32 portalIndex, PlaneF *plane);

   /// @}

protected:
   /// Sets the mLastState and mLastStateKey.
   ///
   /// @param   state   SceneState to set as the last state
   /// @param   key     Key to set as the last state key
   void          setLastState(SceneState *state, U32 key);

   /// Returns true if the provided SceneState and key are set as this object's
   /// last state and key.
   ///
   /// @param   state    SceneState in question
   /// @param   key      State key in question
   bool          isLastState(SceneState *state, U32 key) const;


   /// @name Traversal State
   ///
   /// The SceneGraph traversal is recursive and the traversal state of an object
   /// can be one of three things:
   ///           - Pending - The object has not yet been examined for zone traversal.
   ///           - Working - The object is currently having its zones traversed.
   ///           - Done    - The object has had all of its zones traversed or doesn't manage zones.
   ///
   /// @note These states were formerly referred to as TraverseColor, with White, Black, and
   ///       Gray; this was changed in Torque 1.2 by Pat "KillerBunny" Wilson. This code is
   ///       only used internal to this class
   /// @{

   // These two replaced by TraversalState because that makes more sense -KB
   //void          setTraverseColor(TraverseColor);
   //TraverseColor getTraverseColor() const;
   // ph34r teh muskrat! - Travis Colure

   /// This sets the traversal state of the object.
   ///
   /// @note This is used internally; you should not normally need to call it.
   /// @param   s   Traversal state to assign
   void           setTraversalState( TraversalState s );

   /// Returns the traversal state of this object
   TraversalState getTraversalState() const;

   /// @}

   /// @name Lighting
   /// @{

   struct LightingInfo
   {
      LightingInfo();

      bool                       mUseInfo;
      bool                       mDirty;
      ColorF                     mDefaultColor;
      ColorF                     mAlarmColor;

      SimObjectPtr<SceneObject>  mInterior;

      bool                       mHasLastColor;
      ColorF                     mLastColor;
      U32                        mLastTime;

      static LightInfo           smAmbientLight;

      enum 
      {
         Interior = 0,
         Terrain,
      };
      U32                        mLightSource;
   };

   /// Sets up lighting for the rendering of this object
   virtual void installLights();

   /// Removes lighting for the rendering of this object
   virtual void uninstallLights();

   /// Gets the color of the ambient light in the area of the object and
   /// stores it in the provided ColorF.
   ///
   /// @param   col   Ambient color (out)
   virtual bool getLightingAmbientColor(ColorF * col);

   LightingInfo      mLightingInfo; ///< Lighting info for this object

   /// @}

protected:

   /// @name Transform and Collision Members
   /// @{

   ///
   Container* mContainer;

   MatrixF mObjToWorld;   ///< Transform from object space to world space
   MatrixF mWorldToObj;   ///< Transform from world space to object space (inverse)
   Point3F mObjScale;     ///< Object scale

   Box3F   mObjBox;       ///< Bounding box in object space
   Box3F   mWorldBox;     ///< Bounding box in world space
   SphereF mWorldSphere;  ///< Bounding sphere in world space

   MatrixF mRenderObjToWorld;    ///< Render matrix to transform object space to world space
   MatrixF mRenderWorldToObj;    ///< Render matrix to transform world space to object space
   Box3F   mRenderWorldBox;      ///< Render bounding box in world space
   SphereF mRenderWorldSphere;   ///< Render bounxing sphere in world space

   /// Regenerates the world-space bounding box and bounding sphere
   void    resetWorldBox();

   /// Regenerates the render-world-space bounding box and sphere
   void    resetRenderWorldBox();

   SceneObjectRef* mZoneRefHead;
   SceneObjectRef* mBinRefHead;

   U32 mBinMinX;
   U32 mBinMaxX;
   U32 mBinMinY;
   U32 mBinMaxY;

   /// @}

   /// @name Container Interface
   ///
   /// When objects are searched, we go through all the zones and ask them for
   /// all of their objects. Because an object can exist in multiple zones, the
   /// container sequence key is set to the id of the current search. Then, while
   /// searching, we check to see if an object's sequence key is the same as the
   /// current search key. If it is, it will NOT be added to the list of returns
   /// since it has already been processed.
   ///
   /// @{

   U32  mContainerSeqKey;  ///< Container sequence key

   /// Returns the container sequence key
   U32  getContainerSeqKey() const        { return mContainerSeqKey; }

   /// Sets the container sequence key
   void setContainerSeqKey(const U32 key) { mContainerSeqKey = key;  }
   /// @}

public:

   /// Returns a pointer to the container that contains this object
   Container* getContainer()         { return mContainer;       }

protected:
   S32     mCollisionCount;

   bool    mGlobalBounds;

public:
   /// Returns the type mask for this object
   U32 getTypeMask() { return(mTypeMask); }

   const bool isGlobalBounds() const
   {
      return mGlobalBounds;
   }

   /// If global bounds are set to be true, then the object is assumed to
   /// have an infinitely large bounding box for collision and rendering
   /// purposes.
   ///
   /// They can't be toggled currently.
   void setGlobalBounds()
   { 
      if(mContainer)
         mContainer->removeFromBins(this);

      mGlobalBounds = true;
      mObjBox.min.set(-1e10, -1e10, -1e10);
      mObjBox.max.set( 1e10,  1e10,  1e10);

      if(mContainer)
         mContainer->insertIntoBins(this);
   }


   /// @name Rendering Members
   /// @{
protected:
   SceneGraph* mSceneManager;      ///< SceneGraph that controls this object
   U32         mZoneRangeStart;    ///< Start of range of zones this object controls, 0xFFFFFFFF == no zones

   U32         mNumCurrZones;     ///< Number of zones this object exists in

private:
   TraversalState mTraversalState;  ///< State of this object in the SceneGraph traversal - DON'T MESS WITH THIS
   SceneState*    mLastState;       ///< Last SceneState that was used to render this object.
   U32            mLastStateKey;    ///< Last state key that was used to render this object.

   /// @}

   /// @name Persist and console
   /// @{
public:
   static void initPersistFields();
   void inspectPostApply();
   DECLARE_CONOBJECT(SceneObject);

   /// @}
};


//--------------------------------------------------------------------------
extern Container gServerContainer;
extern Container gClientContainer;

//--------------------------------------------------------------------------
//-------------------------------------- Inlines
//
inline bool SceneObject::isManagingZones() const
{
   return mZoneRangeStart != 0xFFFFFFFF;
}

inline void SceneObject::setLastState(SceneState* state, U32 key)
{
   mLastState    = state;
   mLastStateKey = key;
}

inline bool SceneObject::isLastState(SceneState* state, U32 key) const
{
   return (mLastState == state && mLastStateKey == key);
}

inline void SceneObject::setTraversalState( TraversalState s ) {
   mTraversalState = s;
}

inline SceneObject::TraversalState SceneObject::getTraversalState() const {
   return mTraversalState;
}

inline U32 SceneObject::getCurrZone(const U32 index) const
{
   // Not the most efficient way to do this, walking the list,
   //  but it's an uncommon call...
   SceneObjectRef* walk = mZoneRefHead;
   for (U32 i = 0; i < index; i++) 
   {
      walk = walk->nextInObj;
      AssertFatal(walk!=NULL, "Error, too few object refs!");
   }
   AssertFatal(walk!=NULL, "Error, too few object refs!");

   return walk->zone;
}

//--------------------------------------------------------------------------
inline SceneObjectRef* Container::allocateObjectRef()
{
   if (mFreeRefPool == NULL) 
   {
      addRefPoolBlock();
   }
   AssertFatal(mFreeRefPool!=NULL, "Error, should always have a free reference here!");

   SceneObjectRef* ret = mFreeRefPool;
   mFreeRefPool = mFreeRefPool->nextInObj;

   ret->nextInObj = NULL;
   return ret;
}

inline void Container::freeObjectRef(SceneObjectRef* trash)
{
   trash->object = NULL;
   trash->nextInBin = NULL;
   trash->prevInBin = NULL;
   trash->nextInObj = mFreeRefPool;
   mFreeRefPool     = trash;
}

inline void Container::findObjects(U32 mask, FindCallback callback, void *key)
{
   for (Link* itr = mStart.next; itr != &mEnd; itr = itr->next) {
      SceneObject* ptr = static_cast<SceneObject*>(itr);
      if ((ptr->getType() & mask) != 0 && !ptr->mCollisionCount)
         (*callback)(ptr,key);
   }
}

#endif  // _H_SCENEOBJECT_

