sceneContainer.cpp
Engine/source/scene/sceneContainer.cpp
Public Variables
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" )
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