Torque3D Documentation / _generateds / sceneContainer.cpp

sceneContainer.cpp

Engine/source/scene/sceneContainer.cpp

More...

Public Functions

ConsoleFunctionGroupBegin(Containers , "Functions <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> ray casting and spatial <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">queries.\n\n</a>" )
DefineEngineFunction(containerBoxEmpty , bool , (U32 mask, Point3F center, F32 xRadius, F32 yRadius, F32 zRadius, bool useClientContainer) , (-1, -1, false) , "@brief See <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> any objects of the given types are present in box of given <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">extent.\n\n</a>" "@note Extent parameter is last since only one radius is often needed. If " "one radius is provided, the yRadius and zRadius are assumed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be the same. Unfortunately, " "<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> you need <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use the client container, you 'll need <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set all of the radius parameters. " " Fortunately, this function is mostly used on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">server.\n</a>" " @param mask Indicates the type of objects we are checking <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">against.\n</a>" " @param center Center of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">box.\n</a>" " @param xRadius Search radius in the x-axis. See note <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">above.\n</a>" " @param yRadius Search radius in the y-axis. See note <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">above.\n</a>" " @param zRadius Search radius in the z-axis. See note <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">above.\n</a>" " @param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" " @return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the box is empty, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> any object is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">found.\n</a>" " @ingroup Game" )
DefineEngineFunction(containerRayCast , const char * , (Point3F start, Point3F end, U32 mask, SceneObject *pExempt, bool useClientContainer) , (nullAsType< SceneObject * >(), false) , "@brief Cast <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> ray from start <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> end, checking <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> collision against items matching <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mask.\n\n</a>" "If pExempt is specified, then it is temporarily excluded from collision checks(For " "instance, you might want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> exclude the player <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> said player was firing <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> weapon.)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param start An XYZ vector containing the tail position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ray.\n</a>" " @param end An XYZ vector containing the head position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ray\n</a>" " @param mask A bitmask corresponding <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the type of objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> check <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">for\n</a>" " @param pExempt An optional ID <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single object that ignored <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">raycast\n</a>" " @param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" " @returns A string containing either null, <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> nothing was struck, or these <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">fields:\n</a>" "< ul >< li >The ID of the object that was struck.</li >" "< li >The x, y , z position that it was struck.</li >" "< li >The x, y , z of the normal of the face that was struck.</li >" "< li >The distance between the start point and the position we hit.</li ></ul >" " @ingroup Game" )
DefineEngineFunction(containerSearchCurrDist , F32 , (bool useClientContainer) , (false) , "@brief Get distance of the center of the current item from the center of the " "current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">initContainerRadiusSearch.\n\n</a>" "@param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" "@return distance from the center of the current object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the center of " "the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">search\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">containerSearchNext\n</a>" "@ingroup Game" )
DefineEngineFunction(containerSearchCurrRadiusDist , F32 , (bool useClientContainer) , (false) , "@brief Get the distance of the closest point of the current item from the center " "of the current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">initContainerRadiusSearch.\n\n</a>" "@param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" "@return distance from the closest point of the current object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the " "center of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">search\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">containerSearchNext\n</a>" "@ingroup Game" )
DefineEngineFunction(containerSearchNext , SceneObject * , (bool useClientContainer) , (false) , "@brief Get next item from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> search started with initContainerRadiusSearch() or " "initContainerTypeSearch().\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" "@return the next object found in the search)
DefineEngineFunction(initContainerRadiusSearch , void , (Point3F pos, F32 radius, U32 mask, bool useClientContainer) , (false) , "@brief Start <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> search <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> items at the given position and within the given radius, filtering by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mask.\n\n</a>" " @param pos Center position <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">search\n</a>" " @param radius Search <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">radius\n</a>" " @param mask Bitmask of object types <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> include in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">search\n</a>" " @param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">containerSearchNext\n</a>" " @ingroup Game" )
DefineEngineFunction(initContainerTypeSearch , void , (U32 mask, bool useClientContainer) , (false) , "@brief Start <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> search <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> all items of the types specified by the bitset <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mask.\n\n</a>" "@param mask Bitmask of object types <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> include in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">search\n</a>" "@param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">containerSearchNext\n</a>" "@ingroup Game" )
DefineEngineFunction(materialRayCast , const char * , (Point3F start, Point3F end, U32 mask, SceneObject *pExempt, bool useClientContainer) , (nullAsType< SceneObject * >(), false) , "@brief Cast <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> ray from start <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> end, checking <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> collision against items matching <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mask.\n\n</a>" "If pExempt is specified, then it is temporarily excluded from collision checks(For " "instance, you might want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> exclude the player <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> said player was firing <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> weapon.)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param start An XYZ vector containing the tail position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ray.\n</a>" " @param end An XYZ vector containing the head position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ray\n</a>" " @param mask A bitmask corresponding <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the type of objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> check <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">for\n</a>" " @param pExempt An optional ID <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single object that ignored <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">raycast\n</a>" " @param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" " @returns A string containing either null, <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> nothing was struck, or these <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">fields:\n</a>" "< ul >< li >The ID of the object that was struck.</li >" "< li >The x, y , z position that it was struck.</li >" "< li >The x, y , z of the normal of the face that was struck.</li >" "< li >The distance between the start point and the position we hit.</li ></ul >" " @ingroup Game" )

Detailed Description

Public Variables

SceneContainer gClientContainer 
SceneContainer gServerContainer 
Box3F sBoundingBox 
SphereF sBoundingSphere 
Point3F sgSortReferencePoint 
AbstractPolyList * sPolyList 

Public Functions

buildCallback(SceneObject * object, void * key)

cmpSearchPointers(const void * inP1, const void * inP2)

ConsoleFunctionGroupBegin(Containers , "Functions <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> ray casting and spatial <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">queries.\n\n</a>" )

ConsoleFunctionGroupEnd(Containers )

DefineEngineFunction(containerBoxEmpty , bool , (U32 mask, Point3F center, F32 xRadius, F32 yRadius, F32 zRadius, bool useClientContainer) , (-1, -1, false) , "@brief See <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> any objects of the given types are present in box of given <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">extent.\n\n</a>" "@note Extent parameter is last since only one radius is often needed. If " "one radius is provided, the yRadius and zRadius are assumed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be the same. Unfortunately, " "<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> you need <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use the client container, you 'll need <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set all of the radius parameters. " " Fortunately, this function is mostly used on the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">server.\n</a>" " @param mask Indicates the type of objects we are checking <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">against.\n</a>" " @param center Center of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">box.\n</a>" " @param xRadius Search radius in the x-axis. See note <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">above.\n</a>" " @param yRadius Search radius in the y-axis. See note <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">above.\n</a>" " @param zRadius Search radius in the z-axis. See note <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">above.\n</a>" " @param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" " @return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the box is empty, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> any object is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">found.\n</a>" " @ingroup Game" )

DefineEngineFunction(containerRayCast , const char * , (Point3F start, Point3F end, U32 mask, SceneObject *pExempt, bool useClientContainer) , (nullAsType< SceneObject * >(), false) , "@brief Cast <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> ray from start <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> end, checking <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> collision against items matching <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mask.\n\n</a>" "If pExempt is specified, then it is temporarily excluded from collision checks(For " "instance, you might want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> exclude the player <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> said player was firing <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> weapon.)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param start An XYZ vector containing the tail position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ray.\n</a>" " @param end An XYZ vector containing the head position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ray\n</a>" " @param mask A bitmask corresponding <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the type of objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> check <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">for\n</a>" " @param pExempt An optional ID <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single object that ignored <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">raycast\n</a>" " @param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" " @returns A string containing either null, <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> nothing was struck, or these <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">fields:\n</a>" "< ul >< li >The ID of the object that was struck.</li >" "< li >The x, y , z position that it was struck.</li >" "< li >The x, y , z of the normal of the face that was struck.</li >" "< li >The distance between the start point and the position we hit.</li ></ul >" " @ingroup Game" )

DefineEngineFunction(containerSearchCurrDist , F32 , (bool useClientContainer) , (false) , "@brief Get distance of the center of the current item from the center of the " "current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">initContainerRadiusSearch.\n\n</a>" "@param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" "@return distance from the center of the current object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the center of " "the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">search\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">containerSearchNext\n</a>" "@ingroup Game" )

DefineEngineFunction(containerSearchCurrRadiusDist , F32 , (bool useClientContainer) , (false) , "@brief Get the distance of the closest point of the current item from the center " "of the current <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">initContainerRadiusSearch.\n\n</a>" "@param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" "@return distance from the closest point of the current object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the " "center of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">search\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">containerSearchNext\n</a>" "@ingroup Game" )

DefineEngineFunction(containerSearchNext , SceneObject * , (bool useClientContainer) , (false) , "@brief Get next item from <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> search started with initContainerRadiusSearch() or " "initContainerTypeSearch().\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" "@return the next object found in the search)

DefineEngineFunction(initContainerRadiusSearch , void , (Point3F pos, F32 radius, U32 mask, bool useClientContainer) , (false) , "@brief Start <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> search <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> items at the given position and within the given radius, filtering by <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mask.\n\n</a>" " @param pos Center position <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">search\n</a>" " @param radius Search <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">radius\n</a>" " @param mask Bitmask of object types <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> include in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">search\n</a>" " @param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">containerSearchNext\n</a>" " @ingroup Game" )

DefineEngineFunction(initContainerTypeSearch , void , (U32 mask, bool useClientContainer) , (false) , "@brief Start <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> search <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> all items of the types specified by the bitset <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mask.\n\n</a>" "@param mask Bitmask of object types <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> include in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">search\n</a>" "@param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" "@see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">containerSearchNext\n</a>" "@ingroup Game" )

DefineEngineFunction(materialRayCast , const char * , (Point3F start, Point3F end, U32 mask, SceneObject *pExempt, bool useClientContainer) , (nullAsType< SceneObject * >(), false) , "@brief Cast <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> ray from start <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> end, checking <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> collision against items matching <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mask.\n\n</a>" "If pExempt is specified, then it is temporarily excluded from collision checks(For " "instance, you might want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> exclude the player <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> said player was firing <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> weapon.)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " @param start An XYZ vector containing the tail position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ray.\n</a>" " @param end An XYZ vector containing the head position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ray\n</a>" " @param mask A bitmask corresponding <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the type of objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> check <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">for\n</a>" " @param pExempt An optional ID <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single object that ignored <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">raycast\n</a>" " @param useClientContainer Optionally indicates the search should be within the " "client <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">container.\n</a>" " @returns A string containing either null, <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> nothing was struck, or these <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">fields:\n</a>" "< ul >< li >The ID of the object that was struck.</li >" "< li >The x, y , z position that it was struck.</li >" "< li >The x, y , z of the normal of the face that was struck.</li >" "< li >The distance between the start point and the position we hit.</li ></ul >" " @ingroup Game" )

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "platform/platform.h"
  25#include "scene/sceneContainer.h"
  26
  27#include "collision/extrudedPolyList.h"
  28#include "collision/earlyOutPolyList.h"
  29#include "scene/sceneObject.h"
  30#include "platform/profiler.h"
  31#include "console/engineAPI.h"
  32#include "math/util/frustum.h"
  33
  34
  35// [rene, 02-Mar-11]
  36//  - *Loads* of copy&paste sin in this file (among its many other sins); all the findObjectXXX methods
  37//    are trivial permutations of the same snippet of copy&pasted code
  38//  - FindCallback should return a bool so it's possible to use the findObjectXXX methods to look
  39//    for the first object matching a certain criteria
  40
  41
  42SceneContainer gServerContainer;
  43SceneContainer gClientContainer;
  44
  45const U32 SceneContainer::csmNumBins = 16;
  46const F32 SceneContainer::csmBinSize = 64;
  47const F32 SceneContainer::csmTotalBinSize = SceneContainer::csmBinSize * SceneContainer::csmNumBins;
  48const U32 SceneContainer::csmRefPoolBlockSize = 4096;
  49
  50// Statics used by buildPolyList methods
  51static AbstractPolyList* sPolyList;
  52static SphereF sBoundingSphere;
  53static Box3F sBoundingBox;
  54
  55
  56//=============================================================================
  57//    SceneContainer::Link.
  58//=============================================================================
  59
  60//-----------------------------------------------------------------------------
  61
  62SceneContainer::Link::Link()
  63{
  64   mNext = mPrev = this;
  65}
  66
  67//-----------------------------------------------------------------------------
  68
  69void SceneContainer::Link::unlink()
  70{
  71   mNext->mPrev = mPrev;
  72   mPrev->mNext = mNext;
  73   mNext = mPrev = this;
  74}
  75
  76//-----------------------------------------------------------------------------
  77
  78void SceneContainer::Link::linkAfter(SceneContainer::Link* ptr)
  79{
  80   mNext = ptr->mNext;
  81   mNext->mPrev = this;
  82   mPrev = ptr;
  83   mPrev->mNext = this;
  84}
  85
  86//=============================================================================
  87//    SceneContainer.
  88//=============================================================================
  89
  90//-----------------------------------------------------------------------------
  91
  92SceneContainer::SceneContainer()
  93{
  94   mSearchInProgress = false;
  95   mCurrSeqKey = 0;
  96
  97   mEnd.mNext = mEnd.mPrev = &mStart;
  98   mStart.mNext = mStart.mPrev = &mEnd;
  99
 100   mBinArray = new SceneObjectRef[csmNumBins * csmNumBins];
 101   for (U32 i = 0; i < csmNumBins; i++) 
 102   {
 103      U32 base = i * csmNumBins;
 104      for (U32 j = 0; j < csmNumBins; j++) 
 105      {
 106         mBinArray[base + j].object    = NULL;
 107         mBinArray[base + j].nextInBin = NULL;
 108         mBinArray[base + j].prevInBin = NULL;
 109         mBinArray[base + j].nextInObj = NULL;
 110      }
 111   }
 112   mOverflowBin.object    = NULL;
 113   mOverflowBin.nextInBin = NULL;
 114   mOverflowBin.prevInBin = NULL;
 115   mOverflowBin.nextInObj = NULL;
 116
 117   VECTOR_SET_ASSOCIATION( mRefPoolBlocks );
 118   VECTOR_SET_ASSOCIATION( mSearchList );
 119   VECTOR_SET_ASSOCIATION( mWaterAndZones );
 120   VECTOR_SET_ASSOCIATION( mTerrains );
 121
 122   mFreeRefPool = NULL;
 123   addRefPoolBlock();
 124
 125   cleanupSearchVectors();
 126}
 127
 128//-----------------------------------------------------------------------------
 129
 130SceneContainer::~SceneContainer()
 131{
 132   delete[] mBinArray;
 133
 134   for (U32 i = 0; i < mRefPoolBlocks.size(); i++)
 135   {
 136      SceneObjectRef* pool = mRefPoolBlocks[i];
 137      for (U32 j = 0; j < csmRefPoolBlockSize; j++)
 138      {
 139         // Depressingly, this can give weird results if its pointing at bad memory...
 140         if(pool[j].object != NULL)
 141            Con::warnf("Error, a %s (%x) isn't properly out of the bins!", pool[j].object->getClassName(), pool[j].object);
 142
 143         // If you're getting this it means that an object created didn't
 144         // remove itself from its container before we destroyed the
 145         // container. Typically you get this behavior from particle
 146         // emitters, as they try to hang around until all their particles
 147         // die. In general it's benign, though if you get it for things
 148         // that aren't particle emitters it can be a bad sign!
 149      }
 150
 151      delete [] pool;
 152   }
 153   mFreeRefPool = NULL;
 154
 155   cleanupSearchVectors();
 156}
 157
 158//-----------------------------------------------------------------------------
 159
 160bool SceneContainer::addObject(SceneObject* obj)
 161{
 162   AssertFatal(obj->mContainer == NULL, "Adding already added object.");
 163   obj->mContainer = this;
 164   obj->linkAfter(&mStart);
 165
 166   insertIntoBins(obj);
 167
 168   // Also insert water and physical zone types into the special vector.
 169   if ( obj->getTypeMask() & ( WaterObjectType | PhysicalZoneObjectType ) )
 170      mWaterAndZones.push_back(obj);
 171   if( obj->getTypeMask() & TerrainObjectType )
 172      mTerrains.push_back( obj );
 173
 174   return true;
 175}
 176
 177//-----------------------------------------------------------------------------
 178
 179bool SceneContainer::removeObject(SceneObject* obj)
 180{
 181   AssertFatal(obj->mContainer == this, "Trying to remove from wrong container.");
 182   removeFromBins(obj);
 183
 184   // Remove water and physical zone types from the special vector.
 185   if ( obj->getTypeMask() & ( WaterObjectType | PhysicalZoneObjectType ) )
 186   {
 187      Vector<SceneObject*>::iterator iter = T3D::find( mWaterAndZones.begin(), mWaterAndZones.end(), obj );
 188      if( iter != mTerrains.end() )
 189         mWaterAndZones.erase_fast(iter);
 190   }
 191
 192   // Remove terrain objects from special vector.
 193   if( obj->getTypeMask() & TerrainObjectType )
 194   {
 195      Vector< SceneObject* >::iterator iter = T3D::find( mTerrains.begin(), mTerrains.end(), obj );
 196      if( iter != mTerrains.end() )
 197         mTerrains.erase_fast(iter);
 198   }
 199
 200   obj->mContainer = 0;
 201   obj->unlink();
 202   return true;
 203}
 204
 205//-----------------------------------------------------------------------------
 206
 207void SceneContainer::addRefPoolBlock()
 208{
 209   mRefPoolBlocks.push_back(new SceneObjectRef[csmRefPoolBlockSize]);
 210   for (U32 i = 0; i < csmRefPoolBlockSize-1; i++)
 211   {
 212      mRefPoolBlocks.last()[i].object    = NULL;
 213      mRefPoolBlocks.last()[i].prevInBin = NULL;
 214      mRefPoolBlocks.last()[i].nextInBin = NULL;
 215      mRefPoolBlocks.last()[i].nextInObj = &(mRefPoolBlocks.last()[i+1]);
 216   }
 217   mRefPoolBlocks.last()[csmRefPoolBlockSize-1].object    = NULL;
 218   mRefPoolBlocks.last()[csmRefPoolBlockSize-1].prevInBin = NULL;
 219   mRefPoolBlocks.last()[csmRefPoolBlockSize-1].nextInBin = NULL;
 220   mRefPoolBlocks.last()[csmRefPoolBlockSize-1].nextInObj = mFreeRefPool;
 221
 222   mFreeRefPool = &(mRefPoolBlocks.last()[0]);
 223}
 224
 225//-----------------------------------------------------------------------------
 226
 227void SceneContainer::insertIntoBins(SceneObject* obj)
 228{
 229   AssertFatal(obj != NULL, "No object?");
 230   AssertFatal(obj->mBinRefHead == NULL, "Error, already have a bin chain!");
 231
 232   // The first thing we do is find which bins are covered in x and y...
 233   const Box3F* pWBox = &obj->getWorldBox();
 234
 235   U32 minX, maxX, minY, maxY;
 236   getBinRange(pWBox->minExtents.x, pWBox->maxExtents.x, minX, maxX);
 237   getBinRange(pWBox->minExtents.y, pWBox->maxExtents.y, minY, maxY);
 238
 239   // Store the current regions for later queries
 240   obj->mBinMinX = minX;
 241   obj->mBinMaxX = maxX;
 242   obj->mBinMinY = minY;
 243   obj->mBinMaxY = maxY;
 244
 245   // For huge objects, dump them into the overflow bin.  Otherwise, everything
 246   //  goes into the grid...
 247   if (!obj->isGlobalBounds() && ((maxX - minX + 1) < csmNumBins || (maxY - minY + 1) < csmNumBins))
 248   {
 249      SceneObjectRef** pCurrInsert = &obj->mBinRefHead;
 250
 251      for (U32 i = minY; i <= maxY; i++)
 252      {
 253         U32 insertY = i % csmNumBins;
 254         U32 base    = insertY * csmNumBins;
 255         for (U32 j = minX; j <= maxX; j++)
 256         {
 257            U32 insertX = j % csmNumBins;
 258
 259            SceneObjectRef* ref = allocateObjectRef();
 260
 261            ref->object    = obj;
 262            ref->nextInBin = mBinArray[base + insertX].nextInBin;
 263            ref->prevInBin = &mBinArray[base + insertX];
 264            ref->nextInObj = NULL;
 265
 266            if (mBinArray[base + insertX].nextInBin)
 267               mBinArray[base + insertX].nextInBin->prevInBin = ref;
 268            mBinArray[base + insertX].nextInBin = ref;
 269
 270            *pCurrInsert = ref;
 271            pCurrInsert  = &ref->nextInObj;
 272         }
 273      }
 274   }
 275   else
 276   {
 277      SceneObjectRef* ref = allocateObjectRef();
 278
 279      ref->object    = obj;
 280      ref->nextInBin = mOverflowBin.nextInBin;
 281      ref->prevInBin = &mOverflowBin;
 282      ref->nextInObj = NULL;
 283
 284      if (mOverflowBin.nextInBin)
 285         mOverflowBin.nextInBin->prevInBin = ref;
 286      mOverflowBin.nextInBin = ref;
 287
 288      obj->mBinRefHead = ref;
 289   }
 290}
 291
 292//-----------------------------------------------------------------------------
 293
 294void SceneContainer::insertIntoBins(SceneObject* obj,
 295                               U32 minX, U32 maxX,
 296                               U32 minY, U32 maxY)
 297{
 298   PROFILE_START(InsertBins);
 299   AssertFatal(obj != NULL, "No object?");
 300
 301   AssertFatal(obj->mBinRefHead == NULL, "Error, already have a bin chain!");
 302   // Store the current regions for later queries
 303   obj->mBinMinX = minX;
 304   obj->mBinMaxX = maxX;
 305   obj->mBinMinY = minY;
 306   obj->mBinMaxY = maxY;
 307
 308   // For huge objects, dump them into the overflow bin.  Otherwise, everything
 309   //  goes into the grid...
 310   //
 311   if ((maxX - minX + 1) < csmNumBins || ((maxY - minY + 1) < csmNumBins && !obj->isGlobalBounds()))
 312   {
 313      SceneObjectRef** pCurrInsert = &obj->mBinRefHead;
 314
 315      for (U32 i = minY; i <= maxY; i++)
 316      {
 317         U32 insertY = i % csmNumBins;
 318         U32 base    = insertY * csmNumBins;
 319         for (U32 j = minX; j <= maxX; j++)
 320         {
 321            U32 insertX = j % csmNumBins;
 322
 323            SceneObjectRef* ref = allocateObjectRef();
 324
 325            ref->object    = obj;
 326            ref->nextInBin = mBinArray[base + insertX].nextInBin;
 327            ref->prevInBin = &mBinArray[base + insertX];
 328            ref->nextInObj = NULL;
 329
 330            if (mBinArray[base + insertX].nextInBin)
 331               mBinArray[base + insertX].nextInBin->prevInBin = ref;
 332            mBinArray[base + insertX].nextInBin = ref;
 333
 334            *pCurrInsert = ref;
 335            pCurrInsert  = &ref->nextInObj;
 336         }
 337      }
 338   }
 339   else
 340   {
 341      SceneObjectRef* ref = allocateObjectRef();
 342
 343      ref->object    = obj;
 344      ref->nextInBin = mOverflowBin.nextInBin;
 345      ref->prevInBin = &mOverflowBin;
 346      ref->nextInObj = NULL;
 347
 348      if (mOverflowBin.nextInBin)
 349         mOverflowBin.nextInBin->prevInBin = ref;
 350      mOverflowBin.nextInBin = ref;
 351      obj->mBinRefHead = ref;
 352   }
 353   PROFILE_END();
 354}
 355
 356//-----------------------------------------------------------------------------
 357
 358void SceneContainer::removeFromBins(SceneObject* obj)
 359{
 360   PROFILE_START(RemoveFromBins);
 361   AssertFatal(obj != NULL, "No object?");
 362
 363   SceneObjectRef* chain = obj->mBinRefHead;
 364   obj->mBinRefHead = NULL;
 365
 366   while (chain)
 367   {
 368      SceneObjectRef* trash = chain;
 369      chain = chain->nextInObj;
 370
 371      AssertFatal(trash->prevInBin != NULL, "Error, must have a previous entry in the bin!");
 372      if (trash->nextInBin)
 373         trash->nextInBin->prevInBin = trash->prevInBin;
 374      trash->prevInBin->nextInBin = trash->nextInBin;
 375
 376      freeObjectRef(trash);
 377   }
 378   PROFILE_END();
 379}
 380
 381//-----------------------------------------------------------------------------
 382
 383void SceneContainer::checkBins(SceneObject* obj)
 384{
 385   AssertFatal(obj != NULL, "No object?");
 386
 387   PROFILE_START(CheckBins);
 388   if (obj->mBinRefHead == NULL)
 389   {
 390      insertIntoBins(obj);
 391      PROFILE_END();
 392      return;
 393   }
 394
 395   // Otherwise, the object is already in the bins.  Let's see if it has strayed out of
 396   //  the bins that it's currently in...
 397   const Box3F* pWBox = &obj->getWorldBox();
 398
 399   U32 minX, maxX, minY, maxY;
 400   getBinRange(pWBox->minExtents.x, pWBox->maxExtents.x, minX, maxX);
 401   getBinRange(pWBox->minExtents.y, pWBox->maxExtents.y, minY, maxY);
 402
 403   if (obj->mBinMinX != minX || obj->mBinMaxX != maxX ||
 404       obj->mBinMinY != minY || obj->mBinMaxY != maxY)
 405   {
 406      // We have to rebin the object
 407      removeFromBins(obj);
 408      insertIntoBins(obj, minX, maxX, minY, maxY);
 409   }
 410   PROFILE_END();
 411}
 412
 413//-----------------------------------------------------------------------------
 414
 415void SceneContainer::findObjects(const Box3F& box, U32 mask, FindCallback callback, void *key)
 416{
 417   PROFILE_SCOPE(ContainerFindObjects_Box);
 418
 419   // If we're searching for just water, just physical zones, or
 420   // just water and physical zones then use the optimized path.
 421   if ( mask == WaterObjectType || 
 422        mask == PhysicalZoneObjectType ||
 423        mask == (WaterObjectType</a>|<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084a1af121084d5760a7eaef4bb8828ce1cf">PhysicalZoneObjectType) )
 424   {
 425      _findSpecialObjects( mWaterAndZones, box, mask, callback, key );
 426      return;
 427   }
 428   else if( mask == TerrainObjectType )
 429   {
 430      _findSpecialObjects( mTerrains, box, mask, callback, key );
 431      return;
 432   }
 433
 434   AssertFatal( !mSearchInProgress, "SceneContainer::findObjects - Container queries are not re-entrant" );
 435   mSearchInProgress = true;
 436
 437   U32 minX, maxX, minY, maxY;
 438   getBinRange(box.minExtents.x, box.maxExtents.x, minX, maxX);
 439   getBinRange(box.minExtents.y, box.maxExtents.y, minY, maxY);
 440   mCurrSeqKey++;
 441   for (U32 i = minY; i <= maxY; i++)
 442   {
 443      U32 insertY = i % csmNumBins;
 444      U32 base    = insertY * csmNumBins;
 445      for (U32 j = minX; j <= maxX; j++)
 446      {
 447         U32 insertX = j % csmNumBins;
 448
 449         SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
 450         while (chain)
 451         {
 452            if (chain->object->getContainerSeqKey() != mCurrSeqKey)
 453            {
 454               chain->object->setContainerSeqKey(mCurrSeqKey);
 455
 456               if ((chain->object->getTypeMask() & mask) != 0 &&
 457                   chain->object->isCollisionEnabled())
 458               {
 459                  if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
 460                  {
 461                     (*callback)(chain->object,key);
 462                  }
 463               }
 464            }
 465            chain = chain->nextInBin;
 466         }
 467      }
 468   }
 469   SceneObjectRef* chain = mOverflowBin.nextInBin;
 470   while (chain)
 471   {
 472      if (chain->object->getContainerSeqKey() != mCurrSeqKey)
 473      {
 474         chain->object->setContainerSeqKey(mCurrSeqKey);
 475
 476         if ((chain->object->getTypeMask() & mask) != 0 &&
 477             chain->object->isCollisionEnabled())
 478         {
 479            if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
 480            {
 481               (*callback)(chain->object,key);
 482            }
 483         }
 484      }
 485      chain = chain->nextInBin;
 486   }
 487
 488   mSearchInProgress = false;
 489}
 490
 491//-----------------------------------------------------------------------------
 492
 493void SceneContainer::findObjects( const Frustum &frustum, U32 mask, FindCallback callback, void *key )
 494{
 495   PROFILE_SCOPE(ContainerFindObjects_Frustum);
 496
 497   Box3F searchBox = frustum.getBounds();
 498
 499   if (  mask == WaterObjectType || 
 500         mask == PhysicalZoneObjectType ||
 501         mask == (WaterObjectType</a>|<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084a1af121084d5760a7eaef4bb8828ce1cf">PhysicalZoneObjectType) )
 502   {
 503      _findSpecialObjects( mWaterAndZones, searchBox, mask, callback, key );
 504      return;
 505   }
 506   else if( mask == TerrainObjectType )
 507   {
 508      _findSpecialObjects( mTerrains, searchBox, mask, callback, key );
 509      return;
 510   }
 511
 512   AssertFatal( !mSearchInProgress, "SceneContainer::findObjects - Container queries are not re-entrant" );
 513   mSearchInProgress = true;
 514
 515   U32 minX, maxX, minY, maxY;
 516   getBinRange(searchBox.minExtents.x, searchBox.maxExtents.x, minX, maxX);
 517   getBinRange(searchBox.minExtents.y, searchBox.maxExtents.y, minY, maxY);
 518   mCurrSeqKey++;
 519
 520   for (U32 i = minY; i <= maxY; i++)
 521   {
 522      U32 insertY = i % csmNumBins;
 523      U32 base    = insertY * csmNumBins;
 524      for (U32 j = minX; j <= maxX; j++)
 525      {
 526         U32 insertX = j % csmNumBins;
 527
 528         SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
 529         while (chain)
 530         {
 531            SceneObject *object = chain->object;
 532
 533            if (object->getContainerSeqKey() != mCurrSeqKey)
 534            {
 535               object->setContainerSeqKey(mCurrSeqKey);
 536
 537               if ((object->getTypeMask() & mask) != 0 &&
 538                  object->isCollisionEnabled())
 539               {
 540                  const Box3F &worldBox = object->getWorldBox();
 541                  if ( object->isGlobalBounds() || worldBox.isOverlapped(searchBox) )
 542                  {
 543                     if ( !frustum.isCulled( worldBox ) )
 544                        (*callback)(chain->object,key);
 545                  }
 546               }
 547            }
 548            chain = chain->nextInBin;
 549         }
 550      }
 551   }
 552
 553   SceneObjectRef* chain = mOverflowBin.nextInBin;
 554   while (chain)
 555   {
 556      SceneObject *object = chain->object;
 557
 558      if (object->getContainerSeqKey() != mCurrSeqKey)
 559      {
 560         object->setContainerSeqKey(mCurrSeqKey);
 561
 562         if ((object->getTypeMask() & mask) != 0 &&
 563            object->isCollisionEnabled())
 564         {
 565            const Box3F &worldBox = object->getWorldBox();
 566
 567            if ( object->isGlobalBounds() || worldBox.isOverlapped(searchBox) )
 568            {
 569               if ( !frustum.isCulled( worldBox ) )
 570                  (*callback)(object,key);
 571            }
 572         }
 573      }
 574      chain = chain->nextInBin;
 575   }
 576
 577   mSearchInProgress = false;
 578}
 579
 580//-----------------------------------------------------------------------------
 581
 582void SceneContainer::polyhedronFindObjects(const Polyhedron& polyhedron, U32 mask, FindCallback callback, void *key)
 583{
 584   PROFILE_SCOPE(ContainerFindObjects_polyhedron);
 585
 586   U32 i;
 587   Box3F box;
 588   box.minExtents.set(1e9, 1e9, 1e9);
 589   box.maxExtents.set(-1e9, -1e9, -1e9);
 590   for (i = 0; i < polyhedron.mPointList.size(); i++)
 591   {
 592      box.minExtents.setMin(polyhedron.mPointList[i]);
 593      box.maxExtents.setMax(polyhedron.mPointList[i]);
 594   }
 595
 596   if (  mask == WaterObjectType || 
 597         mask == PhysicalZoneObjectType ||
 598         mask == (WaterObjectType</a>|<a href="/coding/file/objecttypes_8h/#objecttypes_8h_1ac18d4a11e9446825e48fd474d7529084a1af121084d5760a7eaef4bb8828ce1cf">PhysicalZoneObjectType) )
 599   {
 600      _findSpecialObjects( mWaterAndZones, box, mask, callback, key );
 601      return;
 602   }
 603   else if( mask == TerrainObjectType )
 604   {
 605      _findSpecialObjects( mTerrains, mask, callback, key );
 606      return;
 607   }
 608
 609   AssertFatal( !mSearchInProgress, "SceneContainer::polyhedronFindObjects - Container queries are not re-entrant" );
 610   mSearchInProgress = true;
 611
 612   U32 minX, maxX, minY, maxY;
 613   getBinRange(box.minExtents.x, box.maxExtents.x, minX, maxX);
 614   getBinRange(box.minExtents.y, box.maxExtents.y, minY, maxY);
 615   mCurrSeqKey++;
 616   for (i = minY; i <= maxY; i++)
 617   {
 618      U32 insertY = i % csmNumBins;
 619      U32 base    = insertY * csmNumBins;
 620      for (U32 j = minX; j <= maxX; j++)
 621      {
 622         U32 insertX = j % csmNumBins;
 623
 624         SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
 625         while (chain)
 626         {
 627            if (chain->object->getContainerSeqKey() != mCurrSeqKey)
 628            {
 629               chain->object->setContainerSeqKey(mCurrSeqKey);
 630
 631               if ((chain->object->getTypeMask() & mask) != 0 &&
 632                   chain->object->isCollisionEnabled())
 633               {
 634                  if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
 635                  {
 636                     (*callback)(chain->object,key);
 637                  }
 638               }
 639            }
 640            chain = chain->nextInBin;
 641         }
 642      }
 643   }
 644   SceneObjectRef* chain = mOverflowBin.nextInBin;
 645   while (chain)
 646   {
 647      if (chain->object->getContainerSeqKey() != mCurrSeqKey)
 648      {
 649         chain->object->setContainerSeqKey(mCurrSeqKey);
 650
 651         if ((chain->object->getTypeMask() & mask) != 0 &&
 652             chain->object->isCollisionEnabled())
 653         {
 654            if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds())
 655            {
 656               (*callback)(chain->object,key);
 657            }
 658         }
 659      }
 660      chain = chain->nextInBin;
 661   }
 662
 663   mSearchInProgress = false;
 664}
 665
 666//-----------------------------------------------------------------------------
 667
 668void SceneContainer::findObjectList( const Box3F& searchBox, U32 mask, Vector<SceneObject*> *outFound )
 669{
 670   PROFILE_SCOPE( Container_FindObjectList_Box );
 671
 672   AssertFatal( !mSearchInProgress, "SceneContainer::findObjectList - Container queries are not re-entrant" );
 673   mSearchInProgress = true;
 674
 675   // TODO: Optimize for water and zones?
 676
 677   U32 minX, maxX, minY, maxY;
 678   getBinRange(searchBox.minExtents.x, searchBox.maxExtents.x, minX, maxX);
 679   getBinRange(searchBox.minExtents.y, searchBox.maxExtents.y, minY, maxY);
 680   mCurrSeqKey++;
 681
 682   for (U32 i = minY; i <= maxY; i++)
 683   {
 684      U32 insertY = i % csmNumBins;
 685      U32 base    = insertY * csmNumBins;
 686      for (U32 j = minX; j <= maxX; j++)
 687      {
 688         U32 insertX = j % csmNumBins;
 689
 690         SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
 691         while (chain)
 692         {
 693            SceneObject *object = chain->object;
 694
 695            if (object->getContainerSeqKey() != mCurrSeqKey)
 696            {
 697               object->setContainerSeqKey(mCurrSeqKey);
 698
 699               if ((object->getTypeMask() & mask) != 0 &&
 700                  object->isCollisionEnabled())
 701               {
 702                  const Box3F &worldBox = object->getWorldBox();
 703                  if ( object->isGlobalBounds() || worldBox.isOverlapped( searchBox ) )
 704                  {
 705                     outFound->push_back( object );
 706                  }
 707               }
 708            }
 709            chain = chain->nextInBin;
 710         }
 711      }
 712   }
 713
 714   SceneObjectRef* chain = mOverflowBin.nextInBin;
 715   while (chain)
 716   {
 717      SceneObject *object = chain->object;
 718
 719      if (object->getContainerSeqKey() != mCurrSeqKey)
 720      {
 721         object->setContainerSeqKey(mCurrSeqKey);
 722
 723         if ((object->getTypeMask() & mask) != 0 &&
 724            object->isCollisionEnabled())
 725         {
 726            const Box3F &worldBox = object->getWorldBox();
 727
 728            if ( object->isGlobalBounds() || worldBox.isOverlapped( searchBox ) )
 729            {
 730               outFound->push_back( object );
 731            }
 732         }
 733      }
 734      chain = chain->nextInBin;
 735   }
 736
 737   mSearchInProgress = false;
 738}
 739
 740//-----------------------------------------------------------------------------
 741
 742void SceneContainer::findObjectList( const Frustum &frustum, U32 mask, Vector<SceneObject*> *outFound )
 743{
 744   PROFILE_SCOPE( Container_FindObjectList_Frustum );
 745
 746   // Do a box find first.
 747   findObjectList( frustum.getBounds(), mask, outFound );
 748
 749   // Now do the frustum testing.
 750   for ( U32 i=0; i < outFound->size(); )
 751   {
 752      const Box3F &worldBox = (*outFound)[i]->getWorldBox();
 753      if ( frustum.isCulled( worldBox ) )
 754         outFound->erase_fast( i );
 755      else
 756         i++;
 757   }
 758}
 759
 760//-----------------------------------------------------------------------------
 761
 762void SceneContainer::findObjectList( U32 mask, Vector<SceneObject*> *outFound )
 763{
 764   for ( Link* itr = mStart.mNext; itr != &mEnd; itr = itr->mNext)
 765   {
 766      SceneObject* ptr = static_cast<SceneObject*>( itr );
 767      if ( ( ptr->getTypeMask() & mask ) != 0 )
 768         outFound->push_back( ptr );
 769   }
 770}
 771
 772//-----------------------------------------------------------------------------
 773
 774void SceneContainer::findObjects( U32 mask, FindCallback callback, void *key )
 775{
 776   for (Link* itr = mStart.mNext; itr != &mEnd; itr = itr->mNext) {
 777      SceneObject* ptr = static_cast<SceneObject*>(itr);
 778      if ((ptr->getTypeMask() & mask) != 0 && !ptr->mCollisionCount)
 779         (*callback)(ptr,key);
 780   }
 781}
 782
 783//-----------------------------------------------------------------------------
 784
 785void SceneContainer::_findSpecialObjects( const Vector< SceneObject*>& vector, U32 mask, FindCallback callback, void *key )
 786{
 787   PROFILE_SCOPE( Container_findSpecialObjects );
 788
 789   Vector<SceneObject*>::const_iterator iter = vector.begin();
 790   for ( ; iter != vector.end(); iter++ )
 791   {
 792      if ( (*iter)->getTypeMask() & mask )
 793         callback( *iter, key );
 794   }   
 795}
 796
 797//-----------------------------------------------------------------------------
 798
 799void SceneContainer::_findSpecialObjects( const Vector< SceneObject*>& vector, const Box3F &box, U32 mask, FindCallback callback, void *key )
 800{
 801   PROFILE_SCOPE( Container_findSpecialObjects_Box );
 802
 803   Vector<SceneObject*>::const_iterator iter = vector.begin();
 804
 805   for ( ; iter != vector.end(); iter++ )
 806   {
 807      SceneObject *pObj = *iter;
 808      
 809      if ( pObj->getTypeMask() & mask &&
 810           ( pObj->isGlobalBounds() || pObj->getWorldBox().isOverlapped(box) ) )
 811      {
 812         callback( pObj, key );
 813      }
 814   }  
 815}
 816
 817//-----------------------------------------------------------------------------
 818
 819bool SceneContainer::castRay( const Point3F& start, const Point3F& end, U32 mask, RayInfo* info, CastRayCallback callback )
 820{
 821   AssertFatal( info->userData == NULL, "SceneContainer::castRay - RayInfo->userData cannot be used here!" );
 822
 823   PROFILE_START( SceneContainer_CastRay );
 824   bool result = _castRay( CollisionGeometry, start, end, mask, info, callback );
 825   PROFILE_END();
 826   return result;
 827}
 828
 829//-----------------------------------------------------------------------------
 830
 831bool SceneContainer::castRayRendered( const Point3F& start, const Point3F& end, U32 mask, RayInfo* info, CastRayCallback callback )
 832{
 833   AssertFatal( info->userData == NULL, "SceneContainer::castRayRendered - RayInfo->userData cannot be used here!" );
 834
 835   PROFILE_START( SceneContainer_CastRayRendered );
 836   bool result = _castRay( RenderedGeometry, start, end, mask, info, callback );
 837   PROFILE_END();
 838   return result;
 839}
 840
 841//-----------------------------------------------------------------------------
 842
 843// DMMNOTE: There are still some optimizations to be done here.  In particular:
 844//           - After checking the overflow bin, we can potentially shorten the line
 845//             that we rasterize against the grid if there is a collision with say,
 846//             the terrain.
 847//           - The optimal grid size isn't necessarily what we have set here. possibly
 848//             a resolution of 16 meters would give better results
 849//           - The line rasterizer is pretty lame.  Unfortunately we can't use a
 850//             simple bres. here, since we need to check every grid element that the line
 851//             passes through, which bres does _not_ do for us.  Possibly there's a
 852//             rasterizer for anti-aliased lines that will serve better than what
 853//             we have below.
 854
 855bool SceneContainer::_castRay( U32 type, const Point3F& start, const Point3F& end, U32 mask, RayInfo* info, CastRayCallback callback )
 856{
 857   AssertFatal( !mSearchInProgress, "SceneContainer::_castRay - Container queries are not re-entrant" );
 858   mSearchInProgress = true;
 859
 860   F32 currentT = 2.0;
 861   mCurrSeqKey++;
 862
 863   SceneObjectRef* overflowChain = mOverflowBin.nextInBin;
 864   while (overflowChain)
 865   {
 866      SceneObject* ptr = overflowChain->object;
 867      if (ptr->getContainerSeqKey() != mCurrSeqKey)
 868      {
 869         ptr->setContainerSeqKey(mCurrSeqKey);
 870
 871         // In the overflow bin, the world box is always going to intersect the line,
 872         //  so we can omit that test...
 873         if ((ptr->getTypeMask() & mask) != 0 &&
 874             ptr->isCollisionEnabled() == true)
 875         {
 876            Point3F xformedStart, xformedEnd;
 877            ptr->mWorldToObj.mulP(start, &xformedStart);
 878            ptr->mWorldToObj.mulP(end,   &xformedEnd);
 879            xformedStart.convolveInverse(ptr->mObjScale);
 880            xformedEnd.convolveInverse(ptr->mObjScale);
 881
 882            RayInfo ri;
 883            ri.generateTexCoord  = info->generateTexCoord;
 884            bool result = false;
 885            if (type == CollisionGeometry)
 886               result = ptr->castRay(xformedStart, xformedEnd, &ri);
 887            else if (type == RenderedGeometry)
 888               result = ptr->castRayRendered(xformedStart, xformedEnd, &ri);
 889            if (result)
 890            {
 891               if( ri.t < currentT && ( !callback || callback( &ri ) ) )
 892               {
 893                  *info = ri;
 894                  info->point.interpolate(start, end, info->t);
 895                  currentT = ri.t;
 896                  info->distance = (start - info->point).len();
 897               }
 898            }
 899         }
 900      }
 901     overflowChain = overflowChain->nextInBin;
 902   }
 903
 904   // These are just for rasterizing the line against the grid.  We want the x coord
 905   //  of the start to be <= the x coord of the end
 906   Point3F normalStart, normalEnd;
 907   if (start.x <= end.x)
 908   {
 909      normalStart = start;
 910      normalEnd   = end;
 911   }
 912   else
 913   {
 914      normalStart = end;
 915      normalEnd   = start;
 916   }
 917
 918   // Ok, let's scan the grids.  The simplest way to do this will be to scan across in
 919   //  x, finding the y range for each affected bin...
 920   U32 minX, maxX;
 921   U32 minY, maxY;
 922//if (normalStart.x == normalEnd.x)
 923//   Con::printf("X start = %g, end = %g", normalStart.x, normalEnd.x);
 924
 925   getBinRange(normalStart.x, normalEnd.x, minX, maxX);
 926   getBinRange(getMin(normalStart.y, normalEnd.y),
 927               getMax(normalStart.y, normalEnd.y), minY, maxY);
 928
 929//if (normalStart.x == normalEnd.x && minX != maxX)
 930//   Con::printf("X min = %d, max = %d", minX, maxX);
 931//if (normalStart.y == normalEnd.y && minY != maxY)
 932//   Con::printf("Y min = %d, max = %d", minY, maxY);
 933
 934   // We'll optimize the case that the line is contained in one bin row or column, which
 935   //  will be quite a few lines.  No sense doing more work than we have to...
 936   //
 937   if ((mFabs(normalStart.x - normalEnd.x) < csmTotalBinSize && minX == maxX) ||
 938       (mFabs(normalStart.y - normalEnd.y) < csmTotalBinSize && minY == maxY))
 939   {
 940      U32 count;
 941      U32 incX, incY;
 942      if (minX == maxX)
 943      {
 944         count = maxY - minY + 1;
 945         incX  = 0;
 946         incY  = 1;
 947      }
 948      else
 949      {
 950         count = maxX - minX + 1;
 951         incX  = 1;
 952         incY  = 0;
 953      }
 954
 955      U32 x = minX;
 956      U32 y = minY;
 957      for (U32 i = 0; i < count; i++)
 958      {
 959         U32 checkX = x % csmNumBins;
 960         U32 checkY = y % csmNumBins;
 961
 962         SceneObjectRef* chain = mBinArray[(checkY * csmNumBins) + checkX].nextInBin;
 963         while (chain)
 964         {
 965            SceneObject* ptr = chain->object;
 966            if (ptr->getContainerSeqKey() != mCurrSeqKey)
 967            {
 968               ptr->setContainerSeqKey(mCurrSeqKey);
 969
 970               if ((ptr->getTypeMask() & mask) != 0      &&
 971                   ptr->isCollisionEnabled() == true)
 972               {
 973                  if (ptr->getWorldBox().collideLine(start, end) || chain->object->isGlobalBounds())
 974                  {
 975                     Point3F xformedStart, xformedEnd;
 976                     ptr->mWorldToObj.mulP(start, &xformedStart);
 977                     ptr->mWorldToObj.mulP(end,   &xformedEnd);
 978                     xformedStart.convolveInverse(ptr->mObjScale);
 979                     xformedEnd.convolveInverse(ptr->mObjScale);
 980
 981                     RayInfo ri;
 982                     ri.generateTexCoord  = info->generateTexCoord;
 983                     bool result = false;
 984                     if (type == CollisionGeometry)
 985                        result = ptr->castRay(xformedStart, xformedEnd, &ri);
 986                     else if (type == RenderedGeometry)
 987                        result = ptr->castRayRendered(xformedStart, xformedEnd, &ri);
 988                     if (result)
 989                     {
 990                        if( ri.t < currentT && ( !callback || callback( &ri ) ) )
 991                        {
 992                           *info = ri;
 993                           info->point.interpolate(start, end, info->t);
 994                           currentT = ri.t;
 995                           info->distance = (start - info->point).len();
 996                        }
 997                     }
 998                  }
 999               }
1000            }
1001            chain = chain->nextInBin;
1002         }
1003
1004         x += incX;
1005         y += incY;
1006      }
1007   }
1008   else
1009   {
1010      // Oh well, let's earn our keep.  We know that after the above conditional, we're
1011      //  going to cross at least one boundary, so that simplifies our job...
1012
1013      F32 currStartX = normalStart.x;
1014
1015      AssertFatal(currStartX != normalEnd.x, "This is going to cause problems in SceneContainer::castRay");
1016      if(mIsNaN_F(currStartX))
1017      {
1018         PROFILE_END();
1019         return false;
1020      }
1021      while (currStartX != normalEnd.x)
1022      {
1023         F32 currEndX   = getMin(currStartX + csmTotalBinSize, normalEnd.x);
1024
1025         F32 currStartT = (currStartX - normalStart.x) / (normalEnd.x - normalStart.x);
1026         F32 currEndT   = (currEndX   - normalStart.x) / (normalEnd.x - normalStart.x);
1027
1028         F32 y1 = normalStart.y + (normalEnd.y - normalStart.y) * currStartT;
1029         F32 y2 = normalStart.y + (normalEnd.y - normalStart.y) * currEndT;
1030
1031         U32 subMinX, subMaxX;
1032         getBinRange(currStartX, currEndX, subMinX, subMaxX);
1033
1034         F32 subStartX = currStartX;
1035         F32 subEndX   = currStartX;
1036
1037         if (currStartX < 0.0f)
1038            subEndX -= mFmod(subEndX, csmBinSize);
1039         else
1040            subEndX += (csmBinSize - mFmod(subEndX, csmBinSize));
1041
1042         for (U32 currXBin = subMinX; currXBin <= subMaxX; currXBin++)
1043         {
1044            U32 checkX = currXBin % csmNumBins;
1045
1046            F32 subStartT = (subStartX - currStartX) / (currEndX - currStartX);
1047            F32 subEndT   = getMin(F32((subEndX   - currStartX) / (currEndX - currStartX)), 1.f);
1048
1049            F32 subY1 = y1 + (y2 - y1) * subStartT;
1050            F32 subY2 = y1 + (y2 - y1) * subEndT;
1051
1052            U32 newMinY, newMaxY;
1053            getBinRange(getMin(subY1, subY2), getMax(subY1, subY2), newMinY, newMaxY);
1054
1055            for (U32 i = newMinY; i <= newMaxY; i++)
1056            {
1057               U32 checkY = i % csmNumBins;
1058
1059               SceneObjectRef* chain = mBinArray[(checkY * csmNumBins) + checkX].nextInBin;
1060               while (chain)
1061               {
1062                  SceneObject* ptr = chain->object;
1063                  if (ptr->getContainerSeqKey() != mCurrSeqKey)
1064                  {
1065                     ptr->setContainerSeqKey(mCurrSeqKey);
1066
1067                     if ((ptr->getTypeMask() & mask) != 0      &&
1068                         ptr->isCollisionEnabled() == true)
1069                     {
1070                        if (ptr->getWorldBox().collideLine(start, end))
1071                        {
1072                           Point3F xformedStart, xformedEnd;
1073                           ptr->mWorldToObj.mulP(start, &xformedStart);
1074                           ptr->mWorldToObj.mulP(end,   &xformedEnd);
1075                           xformedStart.convolveInverse(ptr->mObjScale);
1076                           xformedEnd.convolveInverse(ptr->mObjScale);
1077
1078                           RayInfo ri;
1079                           ri.generateTexCoord  = info->generateTexCoord;
1080                           bool result = false;
1081                           if (type == CollisionGeometry)
1082                              result = ptr->castRay(xformedStart, xformedEnd, &ri);
1083                           else if (type == RenderedGeometry)
1084                              result = ptr->castRayRendered(xformedStart, xformedEnd, &ri);
1085                           if (result)
1086                           {
1087                              if( ri.t < currentT && ( !callback || callback( &ri ) ) )
1088                              {
1089                                 *info = ri;
1090                                 info->point.interpolate(start, end, info->t);
1091                                 currentT = ri.t;
1092                                 info->distance = (start - info->point).len();
1093                              }
1094                           }
1095                        }
1096                     }
1097                  }
1098                  chain = chain->nextInBin;
1099               }
1100            }
1101
1102            subStartX = subEndX;
1103            subEndX   = getMin(subEndX + csmBinSize, currEndX);
1104         }
1105
1106         currStartX = currEndX;
1107      }
1108   }
1109
1110   mSearchInProgress = false;
1111
1112   // Bump the normal into worldspace if appropriate.
1113   if(currentT != 2)
1114   {
1115      PlaneF fakePlane;
1116      fakePlane.x = info->normal.x;
1117      fakePlane.y = info->normal.y;
1118      fakePlane.z = info->normal.z;
1119      fakePlane.d = 0;
1120
1121      PlaneF result;
1122      mTransformPlane(info->object->getTransform(), info->object->getScale(), fakePlane, &result);
1123      info->normal = result;
1124
1125      return true;
1126   }
1127   else
1128   {
1129      // Do nothing and exit...
1130      return false;
1131   }
1132}
1133
1134//-----------------------------------------------------------------------------
1135
1136// collide with the objects projected object box
1137bool SceneContainer::collideBox(const Point3F &start, const Point3F &end, U32 mask, RayInfo * info)
1138{
1139   AssertFatal( info->userData == NULL, "SceneContainer::collideBox - RayInfo->userData cannot be used here!" );
1140
1141   F32 currentT = 2;
1142   for (Link* itr = mStart.mNext; itr != &mEnd; itr = itr->mNext)
1143   {
1144      SceneObject* ptr = static_cast<SceneObject*>(itr);
1145      if (ptr->getTypeMask() & mask && !ptr->mCollisionCount)
1146      {
1147         Point3F xformedStart, xformedEnd;
1148         ptr->mWorldToObj.mulP(start, &xformedStart);
1149         ptr->mWorldToObj.mulP(end,   &xformedEnd);
1150         xformedStart.convolveInverse(ptr->mObjScale);
1151         xformedEnd.convolveInverse(ptr->mObjScale);
1152
1153         RayInfo ri;
1154         if(ptr->collideBox(xformedStart, xformedEnd, &ri))
1155         {
1156            if(ri.t < currentT)
1157            {
1158               *info = ri;
1159               info->point.interpolate(start, end, info->t);
1160               currentT = ri.t;
1161            }
1162         }
1163      }
1164   }
1165   return currentT != 2;
1166}
1167
1168//-----------------------------------------------------------------------------
1169
1170static void buildCallback(SceneObject* object,void *key)
1171{
1172   SceneContainer::CallbackInfo* info = reinterpret_cast<SceneContainer::CallbackInfo*>(key);
1173   object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere);
1174}
1175
1176bool SceneContainer::buildPolyList(PolyListContext context, const Box3F &box, U32 mask, AbstractPolyList *polyList)
1177{
1178   CallbackInfo info;
1179   info.context = context;
1180   info.boundingBox = box;
1181   info.polyList = polyList;
1182
1183   // Build bounding sphere
1184   info.boundingSphere.center = (info.boundingBox.minExtents + info.boundingBox.maxExtents) * 0.5;
1185   VectorF bv = box.maxExtents - info.boundingSphere.center;
1186   info.boundingSphere.radius = bv.len();
1187
1188   sPolyList = polyList;
1189   findObjects(box,mask,buildCallback,&info);
1190   return !polyList->isEmpty();
1191}
1192
1193//-----------------------------------------------------------------------------
1194
1195void SceneContainer::cleanupSearchVectors()
1196{
1197   for (U32 i = 0; i < mSearchList.size(); i++)
1198      delete mSearchList[i];
1199   mSearchList.clear();
1200   mCurrSearchPos = -1;
1201}
1202
1203//-----------------------------------------------------------------------------
1204
1205static Point3F sgSortReferencePoint;
1206static S32 QSORT_CALLBACK cmpSearchPointers(const void* inP1, const void* inP2)
1207{
1208   SimObjectPtr<SceneObject>** p1 = (SimObjectPtr<SceneObject>**)inP1;
1209   SimObjectPtr<SceneObject>** p2 = (SimObjectPtr<SceneObject>**)inP2;
1210
1211   Point3F temp;
1212   F32 d1, d2;
1213
1214   if (bool(**p1))
1215   {
1216      (**p1)->getWorldBox().getCenter(&temp);
1217      d1 = (temp - sgSortReferencePoint).len();
1218   }
1219   else
1220   {
1221      d1 = 0;
1222   }
1223   if (bool(**p2))
1224   {
1225      (**p2)->getWorldBox().getCenter(&temp);
1226      d2 = (temp - sgSortReferencePoint).len();
1227   }
1228   else
1229   {
1230      d2 = 0;
1231   }
1232
1233   if (d1 > d2)
1234      return 1;
1235   else if (d1 < d2)
1236      return -1;
1237   else
1238      return 0;
1239}
1240
1241void SceneContainer::initRadiusSearch(const Point3F& searchPoint,
1242                                 const F32      searchRadius,
1243                                 const U32      searchMask)
1244{
1245   cleanupSearchVectors();
1246
1247   mSearchReferencePoint = searchPoint;
1248
1249   Box3F queryBox(searchPoint, searchPoint);
1250   queryBox.minExtents -= Point3F(searchRadius, searchRadius, searchRadius);
1251   queryBox.maxExtents += Point3F(searchRadius, searchRadius, searchRadius);
1252
1253   SimpleQueryList queryList;
1254   findObjects(queryBox, searchMask, SimpleQueryList::insertionCallback, &queryList);
1255
1256   F32 radiusSquared = searchRadius * searchRadius;
1257
1258   const F32* pPoint = &searchPoint.x;
1259   for (U32 i = 0; i < queryList.mList.size(); i++)
1260   {
1261      const F32* bMins;
1262      const F32* bMaxs;
1263      bMins = &queryList.mList[i]->getWorldBox().minExtents.x;
1264      bMaxs = &queryList.mList[i]->getWorldBox().maxExtents.x;
1265      F32 sum = 0;
1266      for (U32 j = 0; j < 3; j++)
1267      {
1268         if (pPoint[j] < bMins[j])
1269            sum += (pPoint[j] - bMins[j])*(pPoint[j] - bMins[j]);
1270         else if (pPoint[j] > bMaxs[j])
1271            sum += (pPoint[j] - bMaxs[j])*(pPoint[j] - bMaxs[j]);
1272      }
1273      if (sum < radiusSquared || queryList.mList[i]->isGlobalBounds())
1274      {
1275         mSearchList.push_back(new SimObjectPtr<SceneObject>);
1276         *(mSearchList.last()) = queryList.mList[i];
1277      }
1278   }
1279   if (mSearchList.size() != 0)
1280   {
1281      sgSortReferencePoint = mSearchReferencePoint;
1282      dQsort(mSearchList.address(), mSearchList.size(),
1283             sizeof(SimObjectPtr<SceneObject>*), cmpSearchPointers);
1284   }
1285}
1286
1287//-----------------------------------------------------------------------------
1288
1289void SceneContainer::initTypeSearch(const U32      searchMask)
1290{
1291   cleanupSearchVectors();
1292
1293   SimpleQueryList queryList;
1294   findObjects(searchMask, SimpleQueryList::insertionCallback, &queryList);
1295
1296   for (U32 i = 0; i < queryList.mList.size(); i++)
1297   {
1298         mSearchList.push_back(new SimObjectPtr<SceneObject>);
1299         *(mSearchList.last()) = queryList.mList[i];
1300   }
1301   if (mSearchList.size() != 0)
1302   {
1303      sgSortReferencePoint = mSearchReferencePoint;
1304      dQsort(mSearchList.address(), mSearchList.size(),
1305             sizeof(SimObjectPtr<SceneObject>*), cmpSearchPointers);
1306   }
1307}
1308
1309//-----------------------------------------------------------------------------
1310
1311SceneObject* SceneContainer::containerSearchNextObject()
1312{
1313   if (mCurrSearchPos >= mSearchList.size())
1314      return NULL;
1315
1316   mCurrSearchPos++;
1317   while (mCurrSearchPos < mSearchList.size() && bool(*mSearchList[mCurrSearchPos]) == false)
1318      mCurrSearchPos++;
1319
1320   if (mCurrSearchPos == mSearchList.size())
1321      return NULL;
1322
1323   return (*mSearchList[mCurrSearchPos]);
1324}
1325
1326//-----------------------------------------------------------------------------
1327
1328U32 SceneContainer::containerSearchNext()
1329{
1330   SceneObject* object = containerSearchNextObject();
1331   if( !object )
1332      return 0;
1333   return object->getId();
1334}
1335
1336//-----------------------------------------------------------------------------
1337
1338F32 SceneContainer::containerSearchCurrDist()
1339{
1340   AssertFatal(mCurrSearchPos != -1, "Error, must call containerSearchNext before containerSearchCurrDist");
1341
1342   if (mCurrSearchPos == -1 || mCurrSearchPos >= mSearchList.size() ||
1343       bool(*mSearchList[mCurrSearchPos]) == false)
1344      return 0.0;
1345
1346   Point3F pos;
1347   (*mSearchList[mCurrSearchPos])->getWorldBox().getCenter(&pos);
1348   return (pos - mSearchReferencePoint).len();
1349}
1350
1351//-----------------------------------------------------------------------------
1352
1353F32 SceneContainer::containerSearchCurrRadiusDist()
1354{
1355   AssertFatal(mCurrSearchPos != -1, "Error, must call containerSearchNext before containerSearchCurrDist");
1356
1357   if (mCurrSearchPos == -1 || mCurrSearchPos >= mSearchList.size() ||
1358       bool(*mSearchList[mCurrSearchPos]) == false)
1359      return 0.0;
1360
1361   Point3F pos;
1362   Box3F worldBox = (*mSearchList[mCurrSearchPos])->getWorldBox();
1363   worldBox.getCenter(&pos);
1364
1365   F32 dist = (pos - mSearchReferencePoint).len();
1366
1367   F32 min = worldBox.len_x();
1368   if (worldBox.len_y() < min)
1369      min = worldBox.len_y();
1370   if (worldBox.len_z() < min)
1371      min = worldBox.len_z();
1372
1373   dist -= min;
1374   if (dist < 0)
1375      dist = 0;
1376
1377   return dist;
1378}
1379
1380//-----------------------------------------------------------------------------
1381
1382void SceneContainer::getBinRange( const F32 min, const F32 max, U32& minBin, U32& maxBin )
1383{
1384   AssertFatal(max >= min, avar("Error, bad range in getBinRange. min: %f, max: %f", min, max));
1385
1386   if ((max - min) >= (SceneContainer::csmTotalBinSize - SceneContainer::csmBinSize))
1387   {
1388      F32 minCoord = mFmod(min, SceneContainer::csmTotalBinSize);
1389      if (minCoord < 0.0f) 
1390      {
1391         minCoord += SceneContainer::csmTotalBinSize;
1392
1393         // This is truly lame, but it can happen.  There must be a better way to
1394         //  deal with this.
1395         if (minCoord == SceneContainer::csmTotalBinSize)
1396            minCoord = SceneContainer::csmTotalBinSize - 0.01f;
1397      }
1398
1399      AssertFatal(minCoord >= 0.0 && minCoord < SceneContainer::csmTotalBinSize, "Bad minCoord");
1400
1401      minBin = U32(minCoord / SceneContainer::csmBinSize);
1402      AssertFatal(minBin < SceneContainer::csmNumBins, avar("Error, bad clipping! (%g, %d)", minCoord, minBin));
1403
1404      maxBin = minBin + (SceneContainer::csmNumBins - 1);
1405      return;
1406   }
1407   else 
1408   {
1409
1410      F32 minCoord = mFmod(min, SceneContainer::csmTotalBinSize);
1411      
1412      if (minCoord < 0.0f) 
1413      {
1414         minCoord += SceneContainer::csmTotalBinSize;
1415
1416         // This is truly lame, but it can happen.  There must be a better way to
1417         //  deal with this.
1418         if (minCoord == SceneContainer::csmTotalBinSize)
1419            minCoord = SceneContainer::csmTotalBinSize - 0.01f;
1420      }
1421      AssertFatal(minCoord >= 0.0 && minCoord < SceneContainer::csmTotalBinSize, "Bad minCoord");
1422
1423      F32 maxCoord = mFmod(max, SceneContainer::csmTotalBinSize);
1424      if (maxCoord < 0.0f) {
1425         maxCoord += SceneContainer::csmTotalBinSize;
1426
1427         // This is truly lame, but it can happen.  There must be a better way to
1428         //  deal with this.
1429         if (maxCoord == SceneContainer::csmTotalBinSize)
1430            maxCoord = SceneContainer::csmTotalBinSize - 0.01f;
1431      }
1432      AssertFatal(maxCoord >= 0.0 && maxCoord < SceneContainer::csmTotalBinSize, "Bad maxCoord");
1433
1434      minBin = U32(minCoord / SceneContainer::csmBinSize);
1435      maxBin = U32(maxCoord / SceneContainer::csmBinSize);
1436      AssertFatal(minBin < SceneContainer::csmNumBins, avar("Error, bad clipping(min)! (%g, %d)", maxCoord, minBin));
1437      AssertFatal(minBin < SceneContainer::csmNumBins, avar("Error, bad clipping(max)! (%g, %d)", maxCoord, maxBin));
1438
1439      // MSVC6 seems to be generating some bad floating point code around
1440      // here when full optimizations are on.  The min != max test should
1441      // not be needed, but it clears up the VC issue.
1442      if (min != max && minCoord > maxCoord)
1443         maxBin += SceneContainer::csmNumBins;
1444
1445      AssertFatal(maxBin >= minBin, "Error, min should always be less than max!");
1446   }
1447}
1448
1449//=============================================================================
1450//    Console API.
1451//=============================================================================
1452// MARK: ---- Console API ----
1453
1454ConsoleFunctionGroupBegin( Containers,  "Functions for ray casting and spatial queries.\n\n");
1455
1456//-----------------------------------------------------------------------------
1457
1458DefineEngineFunction( containerBoxEmpty, bool,
1459   ( U32 mask, Point3F center, F32 xRadius, F32 yRadius, F32 zRadius, bool useClientContainer ), ( -1, -1, false ),
1460   "@brief See if any objects of the given types are present in box of given extent.\n\n"
1461   "@note Extent parameter is last since only one radius is often needed.  If "
1462   "one radius is provided, the yRadius and zRadius are assumed to be the same.  Unfortunately, "
1463   "if you need to use the client container, you'll need to set all of the radius parameters.  "
1464   "Fortunately, this function is mostly used on the server.\n"
1465   "@param  mask   Indicates the type of objects we are checking against.\n"
1466   "@param  center Center of box.\n"
1467   "@param  xRadius Search radius in the x-axis. See note above.\n"
1468   "@param  yRadius Search radius in the y-axis. See note above.\n"
1469   "@param  zRadius Search radius in the z-axis. See note above.\n"
1470   "@param useClientContainer Optionally indicates the search should be within the "
1471   "client container.\n"
1472   "@return true if the box is empty, false if any object is found.\n"
1473   "@ingroup Game")
1474{
1475   Point3F extent( xRadius, yRadius, zRadius );
1476   extent.y = extent.y >= 0 ? extent.y : extent.x;
1477   extent.z = extent.z >= 0 ? extent.z : extent.x;
1478
1479   Box3F    B(center - extent, center + extent, true);
1480
1481   EarlyOutPolyList polyList;
1482   polyList.mPlaneList.clear();
1483   polyList.mNormal.set(0,0,0);
1484   polyList.mPlaneList.setSize(6);
1485   polyList.mPlaneList[0].set(B.minExtents, VectorF(-1,0,0));
1486   polyList.mPlaneList[1].set(B.maxExtents, VectorF(0,1,0));
1487   polyList.mPlaneList[2].set(B.maxExtents, VectorF(1,0,0));
1488   polyList.mPlaneList[3].set(B.minExtents, VectorF(0,-1,0));
1489   polyList.mPlaneList[4].set(B.minExtents, VectorF(0,0,-1));
1490   polyList.mPlaneList[5].set(B.maxExtents, VectorF(0,0,1));
1491
1492   SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
1493
1494   return ! pContainer->buildPolyList(PLC_Collision, B, mask, &polyList);
1495}
1496
1497//-----------------------------------------------------------------------------
1498
1499DefineEngineFunction( initContainerRadiusSearch, void, ( Point3F pos, F32 radius, U32 mask, bool useClientContainer ), ( false ),
1500   "@brief Start a search for items at the given position and within the given radius, filtering by mask.\n\n"
1501
1502   "@param pos Center position for the search\n"
1503   "@param radius Search radius\n"
1504   "@param mask Bitmask of object types to include in the search\n"
1505   "@param useClientContainer Optionally indicates the search should be within the "
1506   "client container.\n"
1507
1508   "@see containerSearchNext\n" 
1509   "@ingroup Game")
1510{
1511   SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
1512
1513   pContainer->initRadiusSearch( pos, radius, mask );
1514}
1515
1516//-----------------------------------------------------------------------------
1517
1518DefineEngineFunction( initContainerTypeSearch, void, ( U32 mask, bool useClientContainer ), ( false ),
1519   "@brief Start a search for all items of the types specified by the bitset mask.\n\n"
1520
1521   "@param mask Bitmask of object types to include in the search\n"
1522   "@param useClientContainer Optionally indicates the search should be within the "
1523   "client container.\n"
1524
1525   "@see containerSearchNext\n" 
1526   "@ingroup Game")
1527{
1528   SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
1529
1530   pContainer->initTypeSearch( mask );
1531}
1532
1533//-----------------------------------------------------------------------------
1534
1535DefineEngineFunction( containerSearchNext, SceneObject*, ( bool useClientContainer ), ( false ),
1536   "@brief Get next item from a search started with initContainerRadiusSearch() or "
1537   "initContainerTypeSearch().\n\n"
1538
1539   "@param useClientContainer Optionally indicates the search should be within the "
1540   "client container.\n"
1541   "@return the next object found in the search, or null if no more\n"
1542
1543   "@tsexample\n"
1544   "// print the names of all nearby ShapeBase derived objects\n"
1545   "%position = %obj.getPosition;\n"
1546   "%radius = 20;\n"
1547   "%mask = $TypeMasks::ShapeBaseObjectType;\n"
1548   "initContainerRadiusSearch( %position, %radius, %mask );\n"
1549   "while ( (%targetObject = containerSearchNext()) != 0 )\n"
1550   "{\n"
1551   "   echo( \"Found: \" @ %targetObject.getName() );\n"
1552   "}\n"
1553   "@endtsexample\n"
1554
1555   "@see initContainerRadiusSearch()\n"
1556   "@see initContainerTypeSearch()\n"
1557   "@ingroup Game")
1558{
1559   SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
1560
1561   return pContainer->containerSearchNextObject();
1562}
1563
1564//-----------------------------------------------------------------------------
1565
1566DefineEngineFunction( containerSearchCurrDist, F32, ( bool useClientContainer ), ( false ),
1567   "@brief Get distance of the center of the current item from the center of the "
1568   "current initContainerRadiusSearch.\n\n"
1569
1570   "@param useClientContainer Optionally indicates the search should be within the "
1571   "client container.\n"
1572   "@return distance from the center of the current object to the center of "
1573   "the search\n"
1574
1575   "@see containerSearchNext\n"
1576   "@ingroup Game")
1577{
1578   SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
1579
1580   return pContainer->containerSearchCurrDist();
1581}
1582
1583//-----------------------------------------------------------------------------
1584
1585DefineEngineFunction( containerSearchCurrRadiusDist, F32, ( bool useClientContainer ), ( false ),
1586   "@brief Get the distance of the closest point of the current item from the center "
1587   "of the current initContainerRadiusSearch.\n\n"
1588
1589   "@param useClientContainer Optionally indicates the search should be within the "
1590   "client container.\n"
1591   "@return distance from the closest point of the current object to the "
1592   "center of the search\n"
1593
1594   "@see containerSearchNext\n" 
1595   "@ingroup Game")
1596{
1597   SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
1598
1599   return pContainer->containerSearchCurrRadiusDist();
1600}
1601
1602//-----------------------------------------------------------------------------
1603
1604//TODO: make RayInfo an API type
1605DefineEngineFunction( containerRayCast, const char*,
1606   ( Point3F start, Point3F end, U32 mask, SceneObject *pExempt, bool useClientContainer ), ( nullAsType<SceneObject*>(), false ),
1607   "@brief Cast a ray from start to end, checking for collision against items matching mask.\n\n"
1608
1609   "If pExempt is specified, then it is temporarily excluded from collision checks (For "
1610   "instance, you might want to exclude the player if said player was firing a weapon.)\n"
1611
1612   "@param start An XYZ vector containing the tail position of the ray.\n"
1613   "@param end An XYZ vector containing the head position of the ray\n"
1614   "@param mask A bitmask corresponding to the type of objects to check for\n"
1615   "@param pExempt An optional ID for a single object that ignored for this raycast\n"
1616   "@param useClientContainer Optionally indicates the search should be within the "
1617   "client container.\n"
1618
1619   "@returns A string containing either null, if nothing was struck, or these fields:\n"
1620   "<ul><li>The ID of the object that was struck.</li>"
1621   "<li>The x, y, z position that it was struck.</li>"
1622   "<li>The x, y, z of the normal of the face that was struck.</li>"
1623   "<li>The distance between the start point and the position we hit.</li></ul>" 
1624
1625   "@ingroup Game")
1626{
1627   if (pExempt)
1628      pExempt->disableCollision();
1629
1630   SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
1631
1632   RayInfo rinfo;
1633   S32 ret = 0;
1634   if (pContainer->castRay(start, end, mask, &rinfo) == true)
1635      ret = rinfo.object->getId();
1636
1637   if (pExempt)
1638      pExempt->enableCollision();
1639
1640   // add the hit position and normal?
1641   static const U32 bufSize = 256;
1642   char *returnBuffer = Con::getReturnBuffer(bufSize);
1643   if(ret)
1644   {
1645      dSprintf(returnBuffer, bufSize, "%d %g %g %g %g %g %g %g",
1646               ret, rinfo.point.x, rinfo.point.y, rinfo.point.z,
1647               rinfo.normal.x, rinfo.normal.y, rinfo.normal.z, rinfo.distance);
1648   }
1649   else
1650   {
1651      returnBuffer[0] = '0';
1652      returnBuffer[1] = '\0';
1653   }
1654
1655   return(returnBuffer);
1656}
1657
1658DefineEngineFunction(materialRayCast, const char*,
1659(Point3F start, Point3F end, U32 mask, SceneObject* pExempt, bool useClientContainer), (nullAsType<SceneObject*>(), false),
1660"@brief Cast a ray from start to end, checking for collision against items matching mask.\n\n"
1661
1662"If pExempt is specified, then it is temporarily excluded from collision checks (For "
1663"instance, you might want to exclude the player if said player was firing a weapon.)\n"
1664
1665"@param start An XYZ vector containing the tail position of the ray.\n"
1666"@param end An XYZ vector containing the head position of the ray\n"
1667"@param mask A bitmask corresponding to the type of objects to check for\n"
1668"@param pExempt An optional ID for a single object that ignored for this raycast\n"
1669"@param useClientContainer Optionally indicates the search should be within the "
1670"client container.\n"
1671
1672"@returns A string containing either null, if nothing was struck, or these fields:\n"
1673"<ul><li>The ID of the object that was struck.</li>"
1674"<li>The x, y, z position that it was struck.</li>"
1675"<li>The x, y, z of the normal of the face that was struck.</li>"
1676"<li>The distance between the start point and the position we hit.</li></ul>"
1677
1678"@ingroup Game")
1679{
1680   if (pExempt)
1681      pExempt->disableCollision();
1682
1683   SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer;
1684
1685   RayInfo rinfo;
1686   S32 ret = 0;
1687   if (pContainer->castRayRendered(start, end, mask, &rinfo) == true)
1688      ret = rinfo.object->getId();
1689
1690   if (pExempt)
1691      pExempt->enableCollision();
1692
1693   // add the hit position and normal?
1694   static const U32 bufSize = 512;
1695   char* returnBuffer = Con::getReturnBuffer(bufSize);
1696   if (ret)
1697   {
1698      dSprintf(returnBuffer, bufSize, "%d %g %g %g %g %g %g %g %g %g %s",
1699         ret, rinfo.point.x, rinfo.point.y, rinfo.point.z,
1700         rinfo.normal.x, rinfo.normal.y, rinfo.normal.z, rinfo.distance, rinfo.texCoord.x, rinfo.texCoord.y, rinfo.material ? rinfo.material->getMaterial()->getName() : "");
1701   }
1702   else
1703   {
1704      returnBuffer[0] = '0';
1705      returnBuffer[1] = '\0';
1706   }
1707
1708   return(returnBuffer);
1709}
1710
1711ConsoleFunctionGroupEnd( Containers );
1712