scopeTracker.h 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _SCOPETRACKER_H_
  23. #define _SCOPETRACKER_H_
  24. #ifndef _TYPETRAITS_H_
  25. #include "platform/typetraits.h"
  26. #endif
  27. #ifndef _BITSET_H_
  28. #include "core/bitSet.h"
  29. #endif
  30. #ifndef _TVECTOR_H_
  31. #include "core/util/tVector.h"
  32. #endif
  33. #ifndef _CONSOLE_H_
  34. #include "console/console.h"
  35. #endif
  36. #ifndef _PROFILER_H_
  37. #include "platform/profiler.h"
  38. #endif
  39. //#define DEBUG_SPEW
  40. /// @file
  41. /// A mechanism for continuous tracking of point/box intersections.
  42. /// Base class for objects registered with a ScopeTracker.
  43. template< S32 NUM_DIMENSIONS >
  44. class ScopeTrackerObject
  45. {
  46. public:
  47. typedef void Parent;
  48. /// TrackingNodes are used to track object bounds along individual world axes.
  49. class TrackingNode
  50. {
  51. public:
  52. typedef void Parent;
  53. enum EFlags
  54. {
  55. FLAG_Min = BIT( 0 ),
  56. FLAG_Max = BIT( 1 ),
  57. FLAG_Reference = BIT( 2 ),
  58. };
  59. ///
  60. BitSet32 mFlags;
  61. ///
  62. TrackingNode* mOpposite;
  63. /// Distance along axis.
  64. F32 mPosition;
  65. /// The object being tracked by this node or NULL.
  66. ScopeTrackerObject* mObject;
  67. /// Next node on axis tracking chain.
  68. TrackingNode* mNext;
  69. /// Previous node on axis tracking chain.
  70. TrackingNode* mPrev;
  71. ///
  72. TrackingNode()
  73. : mOpposite( NULL ), mPosition( 0.0f ), mObject( NULL ), mNext( NULL ), mPrev( NULL ) {}
  74. /// Return the object to which this tracking node belongs.
  75. ScopeTrackerObject* getObject() const { return mObject; }
  76. ///
  77. TrackingNode* getOpposite() const { return mOpposite; }
  78. ///
  79. F32 getPosition() const { return mPosition; }
  80. ///
  81. void setPosition( F32 value ) { mPosition = value; }
  82. ///
  83. TrackingNode* getNext() const { return mNext; }
  84. ///
  85. void setNext( TrackingNode* node ) { mNext = node; }
  86. ///
  87. TrackingNode* getPrev() const { return mPrev; }
  88. ///
  89. void setPrev( TrackingNode* node ) { mPrev = node; }
  90. /// Return true if this is left/lower bound node of an object.
  91. bool isMin() const { return mFlags.test( FLAG_Min ); }
  92. /// Return true if this is the right/upper bound node of an object.
  93. bool isMax() const { return mFlags.test( FLAG_Max ); }
  94. /// Return true if this is the reference center tracking node. There will only
  95. /// ever be one such node on each tracking list.
  96. bool isReference() const { return mFlags.test( FLAG_Reference ); }
  97. };
  98. enum
  99. {
  100. AllInScope = ( 0x01010101 >> ( ( 4 - NUM_DIMENSIONS ) * 8 ) )
  101. };
  102. protected:
  103. ///
  104. union
  105. {
  106. U8 mBytes[ 4 ];
  107. U32 mDWord;
  108. } mScopeMask;
  109. ///
  110. TrackingNode mTrackingNodes[ NUM_DIMENSIONS ][ 2 ];
  111. public:
  112. ///
  113. ScopeTrackerObject( U32 flags = 0 )
  114. {
  115. clearScopeMask();
  116. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  117. {
  118. TrackingNode* minNode = getMinTrackingNode( n );
  119. TrackingNode* maxNode = getMaxTrackingNode( n );
  120. minNode->mFlags = flags;
  121. maxNode->mFlags = flags;
  122. minNode->mObject = this;
  123. maxNode->mObject = this;
  124. minNode->mOpposite = maxNode;
  125. maxNode->mOpposite = minNode;
  126. minNode->mFlags.set( TrackingNode::FLAG_Min );
  127. maxNode->mFlags.set( TrackingNode::FLAG_Max );
  128. }
  129. }
  130. /// Return true if the object is currently being tracked.
  131. bool isRegistered() const
  132. {
  133. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  134. if( getMinTrackingNode( n )->getNext() != NULL )
  135. return true;
  136. return false;
  137. }
  138. /// Return true if the reference center lies within the object bound's on all axes.
  139. bool isInScope() const { return ( mScopeMask.mDWord == AllInScope ); }
  140. ///
  141. bool isInScope( U32 dimension ) const { return mScopeMask.mBytes[ dimension ]; }
  142. ///
  143. void setInScope( U32 dimension, bool state ) { mScopeMask.mBytes[ dimension ] = ( state ? 1 : 0 ); }
  144. ///
  145. void clearScopeMask() { mScopeMask.mDWord = 0; }
  146. ///
  147. TrackingNode* getMinTrackingNode( U32 dimension ) { return &mTrackingNodes[ dimension ][ 0 ]; }
  148. const TrackingNode* getMinTrackingNode( U32 dimension ) const { return &mTrackingNodes[ dimension ][ 0 ]; }
  149. ///
  150. TrackingNode* getMaxTrackingNode( U32 dimension ) { return &mTrackingNodes[ dimension ][ 1 ]; }
  151. const TrackingNode* getMaxTrackingNode( U32 dimension ) const { return &mTrackingNodes[ dimension ][ 1 ]; }
  152. /// @name Implementor Interface
  153. ///
  154. /// The following methods must be implemented by the client. They are defined here
  155. /// just for reference. If you don't override them, you'll get link errors.
  156. ///
  157. /// @{
  158. /// Return the position of the object in world-space.
  159. void getPosition( F32 pos[ NUM_DIMENSIONS ] ) const;
  160. /// If this object is the reference object, this method should return the world-space pivot
  161. /// point in the object that will be the world reference center.
  162. void getReferenceCenter( F32 pos[ NUM_DIMENSIONS ] ) const;
  163. /// Return the object's bounding box in world-space.
  164. void getBounds( F32 minBounds[ NUM_DIMENSIONS ], F32 maxBounds[ NUM_DIMENSIONS ] ) const;
  165. ///
  166. String describeSelf() const;
  167. /// @}
  168. };
  169. /// Helper class to track the position of a point in N-dimensional space relative to a collection
  170. /// of N-dimensional volumes.
  171. ///
  172. /// This class works by breaking the N-dimensional case down into N one-dimensional cases. By tracking
  173. /// objects independently along each of their axes, intersection testing becomes a trivial matter of
  174. /// doing point-on-line tests. The line segments can be conveniently represented as ordered linked
  175. /// lists along which the reference point is being moved.
  176. ///
  177. /// To determine N-dimensional containment of the reference point, the result of each of the one-dimensional
  178. /// point-on-line tests simply has to be combined. If the point lies on each of N 1D segments of a
  179. /// given volume, then the point is fully contained in the volume.
  180. ///
  181. /// This class may be used in places where otherwise a spatial subdivision scheme would be necessary in
  182. /// order to limit the number of containment tests triggered by each movement of the reference point.
  183. /// Once the tracker has been set up, each position change of an object or the reference center will result
  184. /// in a usually small number of incremental list updates.
  185. ///
  186. /// Another advantage is that this class makes it easy to reduce 3D tracking to 2D tracking if tracking on
  187. /// the height axis isn't important.
  188. ///
  189. /// The following interface must be implemented by the given "Object" type:
  190. ///
  191. /// @code
  192. /// struct Object : public ScopeTrackerObject< NUM_DIMENSIONS >
  193. /// {
  194. /// /// Return the position of the object in world-space.
  195. /// void getPosition( F32 pos[ NUM_DIMENSIONS ] ) const;
  196. ///
  197. /// /// If this object is the reference object, this method should return the world-space pivot
  198. /// /// point in the object that will be the world reference center.
  199. /// void getReferenceCenter( F32 pos[ NUM_DIMENSIONS ] ) const;
  200. ///
  201. /// /// Return the object's bounding box in world-space.
  202. /// void getBounds( F32 minBounds[ NUM_DIMENSIONS ], F32 maxBounds[ NUM_DIMENSIONS ] ) const;
  203. /// };
  204. /// @endcode
  205. ///
  206. /// Terminology:
  207. ///
  208. /// - "In Scope": A volume is in scope if it fully contains the reference center.
  209. /// - "Reference Object": Object that is the designated center of the world.
  210. ///
  211. /// @param NUM_DIMENSIONS Number of dimensions to track; must be <=4.
  212. /// @param Object Value type for objects tracked by the ScopeTracker. Must have pointer behavior.
  213. template< S32 NUM_DIMENSIONS, typename Object >
  214. class ScopeTracker
  215. {
  216. public:
  217. typedef void Parent;
  218. typedef typename TypeTraits< Object >::BaseType ObjectType;
  219. typedef typename ObjectType::TrackingNode NodeType;
  220. protected:
  221. enum
  222. {
  223. MIN = 0,
  224. MAX = 1
  225. };
  226. /// The reference object. This is the center relative to which all
  227. /// tracking occurs. Any other object is in scope when it contains the
  228. /// reference object.
  229. Object mReferenceObject;
  230. ///
  231. NodeType* mTrackingList[ NUM_DIMENSIONS ][ 2 ];
  232. ///
  233. NodeType mBoundaryNodes[ NUM_DIMENSIONS ][ 2 ];
  234. ///
  235. Vector< Object > mPotentialScopeInObjects;
  236. /// @name Scoping
  237. /// @{
  238. virtual void _onScopeIn( Object object ) {}
  239. virtual void _onScopeOut( Object object ) {}
  240. /// Set the scoping state of the given object.
  241. void _setScope( Object object );
  242. /// @}
  243. /// @name Tracking
  244. /// @{
  245. ///
  246. void _insertTrackingNode( U32 dimension, NodeType* node );
  247. ///
  248. void _removeTrackingNode( U32 dimension, NodeType* node );
  249. ///
  250. void _moveTrackingNode( U32 dimension, NodeType* node, F32 newPos );
  251. ///
  252. void _initTracking();
  253. ///
  254. void _uninitTracking();
  255. /// @}
  256. public:
  257. ///
  258. ScopeTracker();
  259. /// Add a volume object to the world.
  260. void registerObject( Object object );
  261. /// Remove a volume object from the world.
  262. void unregisterObject( Object object );
  263. /// Update the position of the object in the world.
  264. void updateObject( Object object );
  265. ///
  266. Object getReferenceObject() const { return mReferenceObject; }
  267. ///
  268. ///
  269. /// @note Switching reference centers is potentially costly.
  270. void setReferenceObject( Object object );
  271. ///
  272. void debugDump();
  273. };
  274. //-----------------------------------------------------------------------------
  275. template< S32 NUM_DIMENSIONS, class Object >
  276. ScopeTracker< NUM_DIMENSIONS, Object >::ScopeTracker()
  277. : mReferenceObject( NULL )
  278. {
  279. VECTOR_SET_ASSOCIATION( mPotentialScopeInObjects );
  280. // Initialize the tracking lists. Put the boundary
  281. // nodes in place that will always be the heads and tails
  282. // of each list.
  283. dMemset( mTrackingList, 0, sizeof( mTrackingList ) );
  284. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  285. {
  286. mBoundaryNodes[ n ][ MIN ].setPosition( TypeTraits< F32 >::MIN );
  287. mBoundaryNodes[ n ][ MAX ].setPosition( TypeTraits< F32 >::MAX );
  288. mBoundaryNodes[ n ][ MIN ].setNext( &mBoundaryNodes[ n ][ MAX ] );
  289. mBoundaryNodes[ n ][ MAX ].setPrev( &mBoundaryNodes[ n ][ MIN ] );
  290. mBoundaryNodes[ n ][ MIN ].mOpposite = &mBoundaryNodes[ n ][ MAX ];
  291. mBoundaryNodes[ n ][ MAX ].mOpposite = &mBoundaryNodes[ n ][ MIN ];
  292. mTrackingList[ n ][ MIN ] = &mBoundaryNodes[ n ][ MIN ];
  293. mTrackingList[ n ][ MAX ] = &mBoundaryNodes[ n ][ MAX ];
  294. }
  295. }
  296. //-----------------------------------------------------------------------------
  297. template< S32 NUM_DIMENSIONS, class Object >
  298. void ScopeTracker< NUM_DIMENSIONS, Object >::setReferenceObject( Object object )
  299. {
  300. AssertFatal( !object || !Deref( object ).isRegistered(),
  301. "ScopeTracker::setReferenceObject - reference object must not be volume object" );
  302. if( mReferenceObject == object )
  303. return;
  304. // If object is invalid, remove the reference center
  305. // tracking.
  306. if( !object )
  307. {
  308. // Transition all objects to out-of-scope and
  309. // deactivate tracking.
  310. _uninitTracking();
  311. mReferenceObject = object;
  312. return;
  313. }
  314. if( mReferenceObject )
  315. {
  316. //RDFIXME: this is very disruptive
  317. // We have an existing reference object so we need to update
  318. // the scoping to match it. Brute-force this for now.
  319. _uninitTracking();
  320. mReferenceObject = object;
  321. _initTracking();
  322. }
  323. else
  324. {
  325. // No reference object yet.
  326. mReferenceObject = object;
  327. _initTracking();
  328. }
  329. #ifdef DEBUG_SPEW
  330. Platform::outputDebugString( "[ScopeTracker] Reference object is now 0x%x", object );
  331. #endif
  332. }
  333. //-----------------------------------------------------------------------------
  334. template< S32 NUM_DIMENSIONS, class Object >
  335. void ScopeTracker< NUM_DIMENSIONS, Object >::registerObject( Object object )
  336. {
  337. PROFILE_SCOPE( ScopeTracker_registerObject );
  338. // Get the object bounds.
  339. F32 minBounds[ NUM_DIMENSIONS ];
  340. F32 maxBounds[ NUM_DIMENSIONS ];
  341. Deref( object ).getBounds( minBounds, maxBounds );
  342. // Insert the object's tracking nodes.
  343. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  344. {
  345. NodeType* minNode = Deref( object ).getMinTrackingNode( n );
  346. NodeType* maxNode = Deref( object ).getMaxTrackingNode( n );
  347. minNode->setPosition( minBounds[ n ] );
  348. maxNode->setPosition( maxBounds[ n ] );
  349. // Insert max before min so that max always comes out
  350. // to the right of min.
  351. _insertTrackingNode( n, maxNode );
  352. _insertTrackingNode( n, minNode );
  353. }
  354. // Set the scoping state of the object.
  355. _setScope( object );
  356. }
  357. //-----------------------------------------------------------------------------
  358. template< S32 NUM_DIMENSIONS, class Object >
  359. void ScopeTracker< NUM_DIMENSIONS, Object >::unregisterObject( Object object )
  360. {
  361. PROFILE_SCOPE( ScopeTracker_unregisterObject );
  362. if( !Deref( object ).isRegistered() )
  363. return;
  364. // Clear its scoping state.
  365. if( Deref( object ).isInScope() )
  366. _onScopeOut( object );
  367. Deref( object ).clearScopeMask();
  368. // Remove the tracking state.
  369. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  370. {
  371. _removeTrackingNode( n, Deref( object ).getMinTrackingNode( n ) );
  372. _removeTrackingNode( n, Deref( object ).getMaxTrackingNode( n ) );
  373. }
  374. }
  375. //-----------------------------------------------------------------------------
  376. template< S32 NUM_DIMENSIONS, class Object >
  377. void ScopeTracker< NUM_DIMENSIONS, Object >::updateObject( Object object )
  378. {
  379. PROFILE_SCOPE( ScopeTracker_updateObject );
  380. if( object == mReferenceObject )
  381. {
  382. // Get the reference center position.
  383. F32 position[ NUM_DIMENSIONS ];
  384. Deref( mReferenceObject ).getReferenceCenter( position );
  385. // Move the reference tracking node.
  386. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  387. _moveTrackingNode( n, Deref( mReferenceObject ).getMinTrackingNode( n ), position[ n ] );
  388. // Flush the potential-scope-in list.
  389. while( !mPotentialScopeInObjects.empty() )
  390. {
  391. Object obj = mPotentialScopeInObjects.last();
  392. mPotentialScopeInObjects.decrement();
  393. if( Deref(obj).isInScope() )
  394. _onScopeIn(obj);
  395. }
  396. }
  397. else
  398. {
  399. // Get the object bounds.
  400. F32 minBounds[ NUM_DIMENSIONS ];
  401. F32 maxBounds[ NUM_DIMENSIONS ];
  402. Deref( object ).getBounds( minBounds, maxBounds );
  403. // Move the object's tracking nodes.
  404. bool wasInScope = Deref( object ).isInScope();
  405. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  406. {
  407. NodeType* minNode = Deref( object ).getMinTrackingNode( n );
  408. NodeType* maxNode = Deref( object ).getMaxTrackingNode( n );
  409. _moveTrackingNode( n, minNode, minBounds[ n ] );
  410. _moveTrackingNode( n, maxNode, maxBounds[ n ] );
  411. }
  412. // Rescope the object, if necessary.
  413. if( wasInScope && !Deref( object ).isInScope() )
  414. _onScopeOut( object );
  415. else if( !wasInScope && Deref( object ).isInScope() )
  416. _onScopeIn( object );
  417. }
  418. }
  419. //-----------------------------------------------------------------------------
  420. template< S32 NUM_DIMENSIONS, class Object >
  421. void ScopeTracker< NUM_DIMENSIONS, Object >::_insertTrackingNode( U32 dimension, NodeType* node )
  422. {
  423. //RDTODO: substitute brute-force search with some smarter insertion algorithm
  424. // (at least dynamically decide on direction for search)
  425. F32 pos = node->getPosition();
  426. NodeType* current = mTrackingList[ dimension ][ MIN ]->getNext();
  427. NodeType* prev = mTrackingList[ dimension ][ MIN ];
  428. while( current->getPosition() < pos )
  429. {
  430. prev = current;
  431. current = current->getNext();
  432. }
  433. prev->setNext( node );
  434. current->setPrev( node );
  435. node->setPrev( prev );
  436. node->setNext( current );
  437. }
  438. //-----------------------------------------------------------------------------
  439. template< S32 NUM_DIMENSIONS, class Object >
  440. void ScopeTracker< NUM_DIMENSIONS, Object >::_removeTrackingNode( U32 dimension, NodeType* node )
  441. {
  442. NodeType* next = node->getNext();
  443. NodeType* prev = node->getPrev();
  444. AssertFatal( next != NULL, "ScopeTracker::_insertTrackingNode - invalid list state (no next node)!" );
  445. AssertFatal( prev != NULL, "ScopeTracker::_insertTrackingNode - invalid list state (no prev node)!" );
  446. next->setPrev( prev );
  447. prev->setNext( next );
  448. node->setNext( NULL );
  449. node->setPrev( NULL );
  450. }
  451. //-----------------------------------------------------------------------------
  452. template< S32 NUM_DIMENSIONS, class Object >
  453. void ScopeTracker< NUM_DIMENSIONS, Object >::_moveTrackingNode( U32 dimension, NodeType* node, F32 newPosition )
  454. {
  455. PROFILE_SCOPE( ScopeTracker_moveTrackingNode );
  456. AssertFatal( TypeTraits< F32 >::MIN <= newPosition && newPosition <= TypeTraits< F32 >::MAX, "Invalid float in object coordinate!" );
  457. enum EDirection
  458. {
  459. DIRECTION_Up,
  460. DIRECTION_Down
  461. };
  462. // Determine in which direction we are sliding the node.
  463. EDirection direction;
  464. if( newPosition < node->getPosition() )
  465. {
  466. direction = DIRECTION_Down;
  467. if( node->getPrev()->getPosition() <= newPosition )
  468. {
  469. node->setPosition( newPosition );
  470. return; // Nothing to do.
  471. }
  472. }
  473. else if( newPosition > node->getPosition() )
  474. {
  475. direction = DIRECTION_Up;
  476. if( node->getNext()->getPosition() >= newPosition )
  477. {
  478. node->setPosition( newPosition );
  479. return; // Nothing to do.
  480. }
  481. }
  482. else
  483. return; // Nothing to to.
  484. const bool isReferenceNode = node->isReference();
  485. // Unlink the node.
  486. NodeType* next = node->getNext();
  487. NodeType* prev = node->getPrev();
  488. next->setPrev( prev );
  489. prev->setNext( next );
  490. // Iterate through to the node's new position.
  491. while( ( direction == DIRECTION_Up && next->getPosition() < newPosition )
  492. || ( direction == DIRECTION_Down && prev->getPosition() > newPosition ) )
  493. {
  494. NodeType* current = 0;
  495. switch( direction )
  496. {
  497. case DIRECTION_Up: current = next; break;
  498. case DIRECTION_Down: current = prev; break;
  499. }
  500. if( isReferenceNode )
  501. {
  502. Object object = ( Object ) current->getObject();
  503. if( ( direction == DIRECTION_Up && current->isMin() )
  504. || ( direction == DIRECTION_Down && current->isMax() ) )
  505. {
  506. Deref( object ).setInScope( dimension, true );
  507. mPotentialScopeInObjects.push_back( object );
  508. }
  509. else
  510. {
  511. const bool wasInScope = Deref( object ).isInScope();
  512. Deref( object ).setInScope( dimension, false );
  513. if( wasInScope )
  514. _onScopeOut( object );
  515. }
  516. }
  517. else
  518. {
  519. if( current->isReference() )
  520. {
  521. Object object = ( Object ) node->getObject();
  522. if( ( direction == DIRECTION_Up && node->isMin() )
  523. || ( direction == DIRECTION_Down && node->isMax() ) )
  524. Deref( object ).setInScope( dimension, false );
  525. else
  526. Deref( object ).setInScope( dimension, true );
  527. }
  528. }
  529. switch( direction )
  530. {
  531. case DIRECTION_Down:
  532. next = current;
  533. prev = current->getPrev();
  534. break;
  535. case DIRECTION_Up:
  536. prev = current;
  537. next = current->getNext();
  538. break;
  539. }
  540. }
  541. // Relink the node.
  542. prev->setNext( node );
  543. next->setPrev( node );
  544. node->setPrev( prev );
  545. node->setNext( next );
  546. node->setPosition( newPosition );
  547. }
  548. //-----------------------------------------------------------------------------
  549. template< S32 NUM_DIMENSIONS, class Object >
  550. void ScopeTracker< NUM_DIMENSIONS, Object >::_setScope( Object object )
  551. {
  552. // If there's no reference object, all objects are out of scope.
  553. if( !mReferenceObject || object == mReferenceObject )
  554. {
  555. Deref( object ).clearScopeMask();
  556. return;
  557. }
  558. const bool wasInScope = Deref( object ).isInScope();
  559. // Set the scoping state on each axis.
  560. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  561. {
  562. const F32 referencePos = Deref( mReferenceObject ).getMinTrackingNode( n )->getPosition();
  563. const F32 objectMin = Deref( object ).getMinTrackingNode( n )->getPosition();
  564. const F32 objectMax = Deref( object ).getMaxTrackingNode( n )->getPosition();
  565. bool isInScope = referencePos >= objectMin
  566. && referencePos <= objectMax;
  567. Deref( object ).setInScope( n, isInScope );
  568. }
  569. // Scope in/out if the scoping state has changed.
  570. if( Deref( object ).isInScope() )
  571. {
  572. if( !wasInScope )
  573. _onScopeIn( object );
  574. }
  575. else
  576. {
  577. if( wasInScope )
  578. _onScopeOut( object );
  579. }
  580. }
  581. //-----------------------------------------------------------------------------
  582. template< S32 NUM_DIMENSIONS, class Object >
  583. void ScopeTracker< NUM_DIMENSIONS, Object >::_initTracking()
  584. {
  585. PROFILE_SCOPE( ScopeTracker_initTracking );
  586. AssertFatal( bool( getReferenceObject() ),
  587. "ScopeTracker::_initTracking - can only be called with a valid reference object" );
  588. // Put a single tracking node onto each of the lists for
  589. // the reference object center.
  590. F32 position[ NUM_DIMENSIONS ];
  591. Deref( mReferenceObject ).getReferenceCenter( position );
  592. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  593. {
  594. AssertFatal( TypeTraits< F32 >::MIN <= position[ n ] && position[ n ] <= TypeTraits< F32 >::MAX, "Invalid float in object coordinate!" );
  595. NodeType* node = Deref( mReferenceObject ).getMinTrackingNode( n );
  596. node->mFlags.set( NodeType::FLAG_Reference );
  597. node->setPosition( position[ n ] );
  598. _insertTrackingNode( n, node );
  599. }
  600. // Update the surroundings of the reference object
  601. // in the tracking lists for each dimension.
  602. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  603. {
  604. //TODO: this could be optimized by dynamically determining whether to walk upwards
  605. // or downwards depending on which span has fewer nodes; finding that out is not immediately
  606. // obvious, though
  607. // Walk from the left bound node upwards until we reach the
  608. // reference object's marker. Everything that has its max node
  609. // past the reference object is in scope.
  610. F32 referencePos = Deref( mReferenceObject ).getMinTrackingNode( n )->getPosition();
  611. for( NodeType* node = mTrackingList[ n ][ 0 ]->getNext(); node->getPosition() < referencePos; node = node->getNext() )
  612. if( !node->isMax() && node->getOpposite()->getPosition() > referencePos )
  613. {
  614. node->getObject()->setInScope( n, true );
  615. // If this is the last dimension we're working on and
  616. // the current object is in-scope on all dimension,
  617. // promote to in-scope status.
  618. if( n == ( NUM_DIMENSIONS - 1 ) && node->getObject()->isInScope() )
  619. _onScopeIn( ( Object ) node->getObject() );
  620. }
  621. }
  622. }
  623. //-----------------------------------------------------------------------------
  624. template< S32 NUM_DIMENSIONS, class Object >
  625. void ScopeTracker< NUM_DIMENSIONS, Object >::_uninitTracking()
  626. {
  627. PROFILE_SCOPE( ScopeTracker_uninitTracking );
  628. AssertFatal( bool( getReferenceObject() ),
  629. "ScopeTracker::_uninitTracking - can only be called with a valid reference object" );
  630. // Put all objects currently in scope, out of scope.
  631. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  632. {
  633. U32 referencePos = Deref( mReferenceObject ).getMinTrackingNode( n )->getPosition();
  634. for( NodeType* node = mTrackingList[ n ][ 0 ]->getNext(); node->getPosition() < referencePos; node = node->getNext() )
  635. {
  636. if( node->getObject()->isInScope() )
  637. _onScopeOut( ( Object ) node->getObject() );
  638. node->getObject()->clearScopeMask();
  639. }
  640. }
  641. // Remove the reference object's tracking nodes.
  642. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  643. _removeTrackingNode( n, Deref( mReferenceObject ).getMinTrackingNode( n ) );
  644. }
  645. //-----------------------------------------------------------------------------
  646. template< S32 NUM_DIMENSIONS, class Object >
  647. void ScopeTracker< NUM_DIMENSIONS, Object >::debugDump()
  648. {
  649. for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
  650. {
  651. Con::printf( "Dimension %i", n );
  652. Con::printf( "----------------" );
  653. for( NodeType* node = mTrackingList[ n ][ 0 ]; node != NULL; node = node->getNext() )
  654. {
  655. String desc;
  656. if( node->getObject() )
  657. {
  658. Object object = ( Object ) node->getObject();
  659. desc = Deref( object ).describeSelf();
  660. }
  661. Con::printf( "pos=%f, type=%s, scope=%s, object=%s",
  662. node->getPosition(),
  663. node->isReference() ? "reference" : node->isMin() ? "min" : "max",
  664. node->getObject() ? node->getObject()->isInScope( n ) ? "1" : "0" : "0",
  665. desc.c_str() );
  666. }
  667. Con::printf( "" );
  668. }
  669. }
  670. #endif // !_SCOPETRACKER_H_