mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-19 20:24:49 +00:00
874 lines
27 KiB
C++
874 lines
27 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2012 GarageGames, LLC
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifndef _SCOPETRACKER_H_
|
|
#define _SCOPETRACKER_H_
|
|
|
|
#ifndef _TYPETRAITS_H_
|
|
#include "platform/typetraits.h"
|
|
#endif
|
|
#ifndef _BITSET_H_
|
|
#include "core/bitSet.h"
|
|
#endif
|
|
#ifndef _TVECTOR_H_
|
|
#include "core/util/tVector.h"
|
|
#endif
|
|
#ifndef _CONSOLE_H_
|
|
#include "console/console.h"
|
|
#endif
|
|
#ifndef _PROFILER_H_
|
|
#include "platform/profiler.h"
|
|
#endif
|
|
|
|
//#define DEBUG_SPEW
|
|
|
|
|
|
/// @file
|
|
/// A mechanism for continuous tracking of point/box intersections.
|
|
|
|
|
|
/// Base class for objects registered with a ScopeTracker.
|
|
template< S32 NUM_DIMENSIONS >
|
|
class ScopeTrackerObject
|
|
{
|
|
public:
|
|
|
|
typedef void Parent;
|
|
|
|
/// TrackingNodes are used to track object bounds along individual world axes.
|
|
class TrackingNode
|
|
{
|
|
public:
|
|
|
|
typedef void Parent;
|
|
|
|
enum EFlags
|
|
{
|
|
FLAG_Min = BIT( 0 ),
|
|
FLAG_Max = BIT( 1 ),
|
|
FLAG_Reference = BIT( 2 ),
|
|
};
|
|
|
|
///
|
|
BitSet32 mFlags;
|
|
|
|
///
|
|
TrackingNode* mOpposite;
|
|
|
|
/// Distance along axis.
|
|
F32 mPosition;
|
|
|
|
/// The object being tracked by this node or NULL.
|
|
ScopeTrackerObject* mObject;
|
|
|
|
/// Next node on axis tracking chain.
|
|
TrackingNode* mNext;
|
|
|
|
/// Previous node on axis tracking chain.
|
|
TrackingNode* mPrev;
|
|
|
|
///
|
|
TrackingNode()
|
|
: mOpposite( NULL ), mPosition( 0.0f ), mObject( NULL ), mNext( NULL ), mPrev( NULL ) {}
|
|
|
|
/// Return the object to which this tracking node belongs.
|
|
ScopeTrackerObject* getObject() const { return mObject; }
|
|
|
|
///
|
|
TrackingNode* getOpposite() const { return mOpposite; }
|
|
|
|
///
|
|
F32 getPosition() const { return mPosition; }
|
|
|
|
///
|
|
void setPosition( F32 value ) { mPosition = value; }
|
|
|
|
///
|
|
TrackingNode* getNext() const { return mNext; }
|
|
|
|
///
|
|
void setNext( TrackingNode* node ) { mNext = node; }
|
|
|
|
///
|
|
TrackingNode* getPrev() const { return mPrev; }
|
|
|
|
///
|
|
void setPrev( TrackingNode* node ) { mPrev = node; }
|
|
|
|
/// Return true if this is left/lower bound node of an object.
|
|
bool isMin() const { return mFlags.test( FLAG_Min ); }
|
|
|
|
/// Return true if this is the right/upper bound node of an object.
|
|
bool isMax() const { return mFlags.test( FLAG_Max ); }
|
|
|
|
/// Return true if this is the reference center tracking node. There will only
|
|
/// ever be one such node on each tracking list.
|
|
bool isReference() const { return mFlags.test( FLAG_Reference ); }
|
|
};
|
|
|
|
enum
|
|
{
|
|
AllInScope = ( 0x01010101 >> ( ( 4 - NUM_DIMENSIONS ) * 8 ) )
|
|
};
|
|
|
|
protected:
|
|
|
|
///
|
|
union
|
|
{
|
|
U8 mBytes[ 4 ];
|
|
U32 mDWord;
|
|
} mScopeMask;
|
|
|
|
///
|
|
TrackingNode mTrackingNodes[ NUM_DIMENSIONS ][ 2 ];
|
|
|
|
public:
|
|
|
|
///
|
|
ScopeTrackerObject( U32 flags = 0 )
|
|
{
|
|
clearScopeMask();
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
{
|
|
TrackingNode* minNode = getMinTrackingNode( n );
|
|
TrackingNode* maxNode = getMaxTrackingNode( n );
|
|
|
|
minNode->mFlags = flags;
|
|
maxNode->mFlags = flags;
|
|
|
|
minNode->mObject = this;
|
|
maxNode->mObject = this;
|
|
|
|
minNode->mOpposite = maxNode;
|
|
maxNode->mOpposite = minNode;
|
|
|
|
minNode->mFlags.set( TrackingNode::FLAG_Min );
|
|
maxNode->mFlags.set( TrackingNode::FLAG_Max );
|
|
}
|
|
}
|
|
|
|
/// Return true if the object is currently being tracked.
|
|
bool isRegistered() const
|
|
{
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
if( getMinTrackingNode( n )->getNext() != NULL )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// Return true if the reference center lies within the object bound's on all axes.
|
|
bool isInScope() const { return ( mScopeMask.mDWord == AllInScope ); }
|
|
|
|
///
|
|
bool isInScope( U32 dimension ) const { return mScopeMask.mBytes[ dimension ]; }
|
|
|
|
///
|
|
void setInScope( U32 dimension, bool state ) { mScopeMask.mBytes[ dimension ] = ( state ? 1 : 0 ); }
|
|
|
|
///
|
|
void clearScopeMask() { mScopeMask.mDWord = 0; }
|
|
|
|
///
|
|
TrackingNode* getMinTrackingNode( U32 dimension ) { return &mTrackingNodes[ dimension ][ 0 ]; }
|
|
const TrackingNode* getMinTrackingNode( U32 dimension ) const { return &mTrackingNodes[ dimension ][ 0 ]; }
|
|
|
|
///
|
|
TrackingNode* getMaxTrackingNode( U32 dimension ) { return &mTrackingNodes[ dimension ][ 1 ]; }
|
|
const TrackingNode* getMaxTrackingNode( U32 dimension ) const { return &mTrackingNodes[ dimension ][ 1 ]; }
|
|
|
|
/// @name Implementor Interface
|
|
///
|
|
/// The following methods must be implemented by the client. They are defined here
|
|
/// just for reference. If you don't override them, you'll get link errors.
|
|
///
|
|
/// @{
|
|
|
|
/// Return the position of the object in world-space.
|
|
void getPosition( F32 pos[ NUM_DIMENSIONS ] ) const;
|
|
|
|
/// If this object is the reference object, this method should return the world-space pivot
|
|
/// point in the object that will be the world reference center.
|
|
void getReferenceCenter( F32 pos[ NUM_DIMENSIONS ] ) const;
|
|
|
|
/// Return the object's bounding box in world-space.
|
|
void getBounds( F32 minBounds[ NUM_DIMENSIONS ], F32 maxBounds[ NUM_DIMENSIONS ] ) const;
|
|
|
|
///
|
|
String describeSelf() const;
|
|
|
|
/// @}
|
|
};
|
|
|
|
|
|
/// Helper class to track the position of a point in N-dimensional space relative to a collection
|
|
/// of N-dimensional volumes.
|
|
///
|
|
/// This class works by breaking the N-dimensional case down into N one-dimensional cases. By tracking
|
|
/// objects independently along each of their axes, intersection testing becomes a trivial matter of
|
|
/// doing point-on-line tests. The line segments can be conveniently represented as ordered linked
|
|
/// lists along which the reference point is being moved.
|
|
///
|
|
/// To determine N-dimensional containment of the reference point, the result of each of the one-dimensional
|
|
/// point-on-line tests simply has to be combined. If the point lies on each of N 1D segments of a
|
|
/// given volume, then the point is fully contained in the volume.
|
|
///
|
|
/// This class may be used in places where otherwise a spatial subdivision scheme would be necessary in
|
|
/// order to limit the number of containment tests triggered by each movement of the reference point.
|
|
/// Once the tracker has been set up, each position change of an object or the reference center will result
|
|
/// in a usually small number of incremental list updates.
|
|
///
|
|
/// Another advantage is that this class makes it easy to reduce 3D tracking to 2D tracking if tracking on
|
|
/// the height axis isn't important.
|
|
///
|
|
/// The following interface must be implemented by the given "Object" type:
|
|
///
|
|
/// @code
|
|
/// struct Object : public ScopeTrackerObject< NUM_DIMENSIONS >
|
|
/// {
|
|
/// /// Return the position of the object in world-space.
|
|
/// void getPosition( F32 pos[ NUM_DIMENSIONS ] ) const;
|
|
///
|
|
/// /// If this object is the reference object, this method should return the world-space pivot
|
|
/// /// point in the object that will be the world reference center.
|
|
/// void getReferenceCenter( F32 pos[ NUM_DIMENSIONS ] ) const;
|
|
///
|
|
/// /// Return the object's bounding box in world-space.
|
|
/// void getBounds( F32 minBounds[ NUM_DIMENSIONS ], F32 maxBounds[ NUM_DIMENSIONS ] ) const;
|
|
/// };
|
|
/// @endcode
|
|
///
|
|
/// Terminology:
|
|
///
|
|
/// - "In Scope": A volume is in scope if it fully contains the reference center.
|
|
/// - "Reference Object": Object that is the designated center of the world.
|
|
///
|
|
/// @param NUM_DIMENSIONS Number of dimensions to track; must be <=4.
|
|
/// @param Object Value type for objects tracked by the ScopeTracker. Must have pointer behavior.
|
|
template< S32 NUM_DIMENSIONS, typename Object >
|
|
class ScopeTracker
|
|
{
|
|
public:
|
|
|
|
typedef void Parent;
|
|
typedef typename TypeTraits< Object >::BaseType ObjectType;
|
|
typedef typename ObjectType::TrackingNode NodeType;
|
|
|
|
protected:
|
|
|
|
enum
|
|
{
|
|
MIN = 0,
|
|
MAX = 1
|
|
};
|
|
|
|
/// The reference object. This is the center relative to which all
|
|
/// tracking occurs. Any other object is in scope when it contains the
|
|
/// reference object.
|
|
Object mReferenceObject;
|
|
|
|
///
|
|
NodeType* mTrackingList[ NUM_DIMENSIONS ][ 2 ];
|
|
|
|
///
|
|
NodeType mBoundaryNodes[ NUM_DIMENSIONS ][ 2 ];
|
|
|
|
///
|
|
Vector< Object > mPotentialScopeInObjects;
|
|
|
|
/// @name Scoping
|
|
/// @{
|
|
|
|
virtual void _onScopeIn( Object object ) {}
|
|
|
|
virtual void _onScopeOut( Object object ) {}
|
|
|
|
/// Set the scoping state of the given object.
|
|
void _setScope( Object object );
|
|
|
|
/// @}
|
|
|
|
/// @name Tracking
|
|
/// @{
|
|
|
|
///
|
|
void _insertTrackingNode( U32 dimension, NodeType* node );
|
|
|
|
///
|
|
void _removeTrackingNode( U32 dimension, NodeType* node );
|
|
|
|
///
|
|
void _moveTrackingNode( U32 dimension, NodeType* node, F32 newPos );
|
|
|
|
///
|
|
void _initTracking();
|
|
|
|
///
|
|
void _uninitTracking();
|
|
|
|
/// @}
|
|
|
|
public:
|
|
|
|
///
|
|
ScopeTracker();
|
|
|
|
/// Add a volume object to the world.
|
|
void registerObject( Object object );
|
|
|
|
/// Remove a volume object from the world.
|
|
void unregisterObject( Object object );
|
|
|
|
/// Update the position of the object in the world.
|
|
void updateObject( Object object );
|
|
|
|
///
|
|
Object getReferenceObject() const { return mReferenceObject; }
|
|
|
|
///
|
|
///
|
|
/// @note Switching reference centers is potentially costly.
|
|
void setReferenceObject( Object object );
|
|
|
|
///
|
|
void debugDump();
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
ScopeTracker< NUM_DIMENSIONS, Object >::ScopeTracker()
|
|
: mReferenceObject( NULL )
|
|
{
|
|
VECTOR_SET_ASSOCIATION( mPotentialScopeInObjects );
|
|
|
|
// Initialize the tracking lists. Put the boundary
|
|
// nodes in place that will always be the heads and tails
|
|
// of each list.
|
|
|
|
dMemset( mTrackingList, 0, sizeof( mTrackingList ) );
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
{
|
|
mBoundaryNodes[ n ][ MIN ].setPosition( TypeTraits< F32 >::MIN );
|
|
mBoundaryNodes[ n ][ MAX ].setPosition( TypeTraits< F32 >::MAX );
|
|
|
|
mBoundaryNodes[ n ][ MIN ].setNext( &mBoundaryNodes[ n ][ MAX ] );
|
|
mBoundaryNodes[ n ][ MAX ].setPrev( &mBoundaryNodes[ n ][ MIN ] );
|
|
|
|
mBoundaryNodes[ n ][ MIN ].mOpposite = &mBoundaryNodes[ n ][ MAX ];
|
|
mBoundaryNodes[ n ][ MAX ].mOpposite = &mBoundaryNodes[ n ][ MIN ];
|
|
|
|
mTrackingList[ n ][ MIN ] = &mBoundaryNodes[ n ][ MIN ];
|
|
mTrackingList[ n ][ MAX ] = &mBoundaryNodes[ n ][ MAX ];
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::setReferenceObject( Object object )
|
|
{
|
|
AssertFatal( !object || !Deref( object ).isRegistered(),
|
|
"ScopeTracker::setReferenceObject - reference object must not be volume object" );
|
|
|
|
if( mReferenceObject == object )
|
|
return;
|
|
|
|
// If object is invalid, remove the reference center
|
|
// tracking.
|
|
|
|
if( !object )
|
|
{
|
|
// Transition all objects to out-of-scope and
|
|
// deactivate tracking.
|
|
|
|
_uninitTracking();
|
|
mReferenceObject = object;
|
|
return;
|
|
}
|
|
|
|
if( mReferenceObject )
|
|
{
|
|
//RDFIXME: this is very disruptive
|
|
|
|
// We have an existing reference object so we need to update
|
|
// the scoping to match it. Brute-force this for now.
|
|
|
|
_uninitTracking();
|
|
mReferenceObject = object;
|
|
_initTracking();
|
|
}
|
|
else
|
|
{
|
|
// No reference object yet.
|
|
|
|
mReferenceObject = object;
|
|
_initTracking();
|
|
}
|
|
|
|
#ifdef DEBUG_SPEW
|
|
Platform::outputDebugString( "[ScopeTracker] Reference object is now 0x%x", object );
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::registerObject( Object object )
|
|
{
|
|
PROFILE_SCOPE( ScopeTracker_registerObject );
|
|
|
|
// Get the object bounds.
|
|
|
|
F32 minBounds[ NUM_DIMENSIONS ];
|
|
F32 maxBounds[ NUM_DIMENSIONS ];
|
|
|
|
Deref( object ).getBounds( minBounds, maxBounds );
|
|
|
|
// Insert the object's tracking nodes.
|
|
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
{
|
|
NodeType* minNode = Deref( object ).getMinTrackingNode( n );
|
|
NodeType* maxNode = Deref( object ).getMaxTrackingNode( n );
|
|
|
|
minNode->setPosition( minBounds[ n ] );
|
|
maxNode->setPosition( maxBounds[ n ] );
|
|
|
|
// Insert max before min so that max always comes out
|
|
// to the right of min.
|
|
|
|
_insertTrackingNode( n, maxNode );
|
|
_insertTrackingNode( n, minNode );
|
|
}
|
|
|
|
// Set the scoping state of the object.
|
|
|
|
_setScope( object );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::unregisterObject( Object object )
|
|
{
|
|
PROFILE_SCOPE( ScopeTracker_unregisterObject );
|
|
|
|
if( !Deref( object ).isRegistered() )
|
|
return;
|
|
|
|
// Clear its scoping state.
|
|
|
|
if( Deref( object ).isInScope() )
|
|
_onScopeOut( object );
|
|
Deref( object ).clearScopeMask();
|
|
|
|
// Remove the tracking state.
|
|
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
{
|
|
_removeTrackingNode( n, Deref( object ).getMinTrackingNode( n ) );
|
|
_removeTrackingNode( n, Deref( object ).getMaxTrackingNode( n ) );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::updateObject( Object object )
|
|
{
|
|
PROFILE_SCOPE( ScopeTracker_updateObject );
|
|
|
|
if( object == mReferenceObject )
|
|
{
|
|
// Get the reference center position.
|
|
|
|
F32 position[ NUM_DIMENSIONS ];
|
|
Deref( mReferenceObject ).getReferenceCenter( position );
|
|
|
|
// Move the reference tracking node.
|
|
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
_moveTrackingNode( n, Deref( mReferenceObject ).getMinTrackingNode( n ), position[ n ] );
|
|
|
|
// Flush the potential-scope-in list.
|
|
|
|
while( !mPotentialScopeInObjects.empty() )
|
|
{
|
|
Object obj = mPotentialScopeInObjects.last();
|
|
mPotentialScopeInObjects.decrement();
|
|
|
|
if( Deref(obj).isInScope() )
|
|
_onScopeIn(obj);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get the object bounds.
|
|
|
|
F32 minBounds[ NUM_DIMENSIONS ];
|
|
F32 maxBounds[ NUM_DIMENSIONS ];
|
|
|
|
Deref( object ).getBounds( minBounds, maxBounds );
|
|
|
|
// Move the object's tracking nodes.
|
|
|
|
bool wasInScope = Deref( object ).isInScope();
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
{
|
|
NodeType* minNode = Deref( object ).getMinTrackingNode( n );
|
|
NodeType* maxNode = Deref( object ).getMaxTrackingNode( n );
|
|
|
|
_moveTrackingNode( n, minNode, minBounds[ n ] );
|
|
_moveTrackingNode( n, maxNode, maxBounds[ n ] );
|
|
}
|
|
|
|
// Rescope the object, if necessary.
|
|
|
|
if( wasInScope && !Deref( object ).isInScope() )
|
|
_onScopeOut( object );
|
|
else if( !wasInScope && Deref( object ).isInScope() )
|
|
_onScopeIn( object );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::_insertTrackingNode( U32 dimension, NodeType* node )
|
|
{
|
|
//RDTODO: substitute brute-force search with some smarter insertion algorithm
|
|
// (at least dynamically decide on direction for search)
|
|
|
|
F32 pos = node->getPosition();
|
|
NodeType* current = mTrackingList[ dimension ][ MIN ]->getNext();
|
|
NodeType* prev = mTrackingList[ dimension ][ MIN ];
|
|
|
|
while( current->getPosition() < pos )
|
|
{
|
|
prev = current;
|
|
current = current->getNext();
|
|
}
|
|
|
|
prev->setNext( node );
|
|
current->setPrev( node );
|
|
|
|
node->setPrev( prev );
|
|
node->setNext( current );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::_removeTrackingNode( U32 dimension, NodeType* node )
|
|
{
|
|
NodeType* next = node->getNext();
|
|
NodeType* prev = node->getPrev();
|
|
|
|
AssertFatal( next != NULL, "ScopeTracker::_insertTrackingNode - invalid list state (no next node)!" );
|
|
AssertFatal( prev != NULL, "ScopeTracker::_insertTrackingNode - invalid list state (no prev node)!" );
|
|
|
|
next->setPrev( prev );
|
|
prev->setNext( next );
|
|
|
|
node->setNext( NULL );
|
|
node->setPrev( NULL );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::_moveTrackingNode( U32 dimension, NodeType* node, F32 newPosition )
|
|
{
|
|
PROFILE_SCOPE( ScopeTracker_moveTrackingNode );
|
|
|
|
AssertFatal( TypeTraits< F32 >::MIN <= newPosition && newPosition <= TypeTraits< F32 >::MAX, "Invalid float in object coordinate!" );
|
|
|
|
enum EDirection
|
|
{
|
|
DIRECTION_Up,
|
|
DIRECTION_Down
|
|
};
|
|
|
|
// Determine in which direction we are sliding the node.
|
|
|
|
EDirection direction;
|
|
if( newPosition < node->getPosition() )
|
|
{
|
|
direction = DIRECTION_Down;
|
|
if( node->getPrev()->getPosition() <= newPosition )
|
|
{
|
|
node->setPosition( newPosition );
|
|
return; // Nothing to do.
|
|
}
|
|
}
|
|
else if( newPosition > node->getPosition() )
|
|
{
|
|
direction = DIRECTION_Up;
|
|
if( node->getNext()->getPosition() >= newPosition )
|
|
{
|
|
node->setPosition( newPosition );
|
|
return; // Nothing to do.
|
|
}
|
|
}
|
|
else
|
|
return; // Nothing to to.
|
|
|
|
const bool isReferenceNode = node->isReference();
|
|
|
|
// Unlink the node.
|
|
|
|
NodeType* next = node->getNext();
|
|
NodeType* prev = node->getPrev();
|
|
|
|
next->setPrev( prev );
|
|
prev->setNext( next );
|
|
|
|
// Iterate through to the node's new position.
|
|
|
|
while( ( direction == DIRECTION_Up && next->getPosition() < newPosition )
|
|
|| ( direction == DIRECTION_Down && prev->getPosition() > newPosition ) )
|
|
{
|
|
NodeType* current = 0;
|
|
switch( direction )
|
|
{
|
|
case DIRECTION_Up: current = next; break;
|
|
case DIRECTION_Down: current = prev; break;
|
|
}
|
|
|
|
if( isReferenceNode )
|
|
{
|
|
Object object = ( Object ) current->getObject();
|
|
if( ( direction == DIRECTION_Up && current->isMin() )
|
|
|| ( direction == DIRECTION_Down && current->isMax() ) )
|
|
{
|
|
Deref( object ).setInScope( dimension, true );
|
|
mPotentialScopeInObjects.push_back( object );
|
|
}
|
|
else
|
|
{
|
|
const bool wasInScope = Deref( object ).isInScope();
|
|
Deref( object ).setInScope( dimension, false );
|
|
if( wasInScope )
|
|
_onScopeOut( object );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( current->isReference() )
|
|
{
|
|
Object object = ( Object ) node->getObject();
|
|
if( ( direction == DIRECTION_Up && node->isMin() )
|
|
|| ( direction == DIRECTION_Down && node->isMax() ) )
|
|
Deref( object ).setInScope( dimension, false );
|
|
else
|
|
Deref( object ).setInScope( dimension, true );
|
|
}
|
|
}
|
|
|
|
switch( direction )
|
|
{
|
|
case DIRECTION_Down:
|
|
next = current;
|
|
prev = current->getPrev();
|
|
break;
|
|
|
|
case DIRECTION_Up:
|
|
prev = current;
|
|
next = current->getNext();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Relink the node.
|
|
|
|
prev->setNext( node );
|
|
next->setPrev( node );
|
|
|
|
node->setPrev( prev );
|
|
node->setNext( next );
|
|
|
|
node->setPosition( newPosition );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::_setScope( Object object )
|
|
{
|
|
// If there's no reference object, all objects are out of scope.
|
|
|
|
if( !mReferenceObject || object == mReferenceObject )
|
|
{
|
|
Deref( object ).clearScopeMask();
|
|
return;
|
|
}
|
|
|
|
const bool wasInScope = Deref( object ).isInScope();
|
|
|
|
// Set the scoping state on each axis.
|
|
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
{
|
|
const F32 referencePos = Deref( mReferenceObject ).getMinTrackingNode( n )->getPosition();
|
|
const F32 objectMin = Deref( object ).getMinTrackingNode( n )->getPosition();
|
|
const F32 objectMax = Deref( object ).getMaxTrackingNode( n )->getPosition();
|
|
|
|
bool isInScope = referencePos >= objectMin
|
|
&& referencePos <= objectMax;
|
|
|
|
Deref( object ).setInScope( n, isInScope );
|
|
}
|
|
|
|
// Scope in/out if the scoping state has changed.
|
|
|
|
if( Deref( object ).isInScope() )
|
|
{
|
|
if( !wasInScope )
|
|
_onScopeIn( object );
|
|
}
|
|
else
|
|
{
|
|
if( wasInScope )
|
|
_onScopeOut( object );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::_initTracking()
|
|
{
|
|
PROFILE_SCOPE( ScopeTracker_initTracking );
|
|
|
|
AssertFatal( bool( getReferenceObject() ),
|
|
"ScopeTracker::_initTracking - can only be called with a valid reference object" );
|
|
|
|
// Put a single tracking node onto each of the lists for
|
|
// the reference object center.
|
|
|
|
F32 position[ NUM_DIMENSIONS ];
|
|
Deref( mReferenceObject ).getReferenceCenter( position );
|
|
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
{
|
|
AssertFatal( TypeTraits< F32 >::MIN <= position[ n ] && position[ n ] <= TypeTraits< F32 >::MAX, "Invalid float in object coordinate!" );
|
|
|
|
NodeType* node = Deref( mReferenceObject ).getMinTrackingNode( n );
|
|
node->mFlags.set( NodeType::FLAG_Reference );
|
|
|
|
node->setPosition( position[ n ] );
|
|
|
|
_insertTrackingNode( n, node );
|
|
}
|
|
|
|
// Update the surroundings of the reference object
|
|
// in the tracking lists for each dimension.
|
|
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
{
|
|
//TODO: this could be optimized by dynamically determining whether to walk upwards
|
|
// or downwards depending on which span has fewer nodes; finding that out is not immediately
|
|
// obvious, though
|
|
|
|
// Walk from the left bound node upwards until we reach the
|
|
// reference object's marker. Everything that has its max node
|
|
// past the reference object is in scope.
|
|
|
|
F32 referencePos = Deref( mReferenceObject ).getMinTrackingNode( n )->getPosition();
|
|
for( NodeType* node = mTrackingList[ n ][ 0 ]->getNext(); node->getPosition() < referencePos; node = node->getNext() )
|
|
if( !node->isMax() && node->getOpposite()->getPosition() > referencePos )
|
|
{
|
|
node->getObject()->setInScope( n, true );
|
|
|
|
// If this is the last dimension we're working on and
|
|
// the current object is in-scope on all dimension,
|
|
// promote to in-scope status.
|
|
|
|
if( n == ( NUM_DIMENSIONS - 1 ) && node->getObject()->isInScope() )
|
|
_onScopeIn( ( Object ) node->getObject() );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::_uninitTracking()
|
|
{
|
|
PROFILE_SCOPE( ScopeTracker_uninitTracking );
|
|
|
|
AssertFatal( bool( getReferenceObject() ),
|
|
"ScopeTracker::_uninitTracking - can only be called with a valid reference object" );
|
|
|
|
// Put all objects currently in scope, out of scope.
|
|
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
{
|
|
U32 referencePos = Deref( mReferenceObject ).getMinTrackingNode( n )->getPosition();
|
|
for( NodeType* node = mTrackingList[ n ][ 0 ]->getNext(); node->getPosition() < referencePos; node = node->getNext() )
|
|
{
|
|
if( node->getObject()->isInScope() )
|
|
_onScopeOut( ( Object ) node->getObject() );
|
|
node->getObject()->clearScopeMask();
|
|
}
|
|
}
|
|
|
|
// Remove the reference object's tracking nodes.
|
|
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
_removeTrackingNode( n, Deref( mReferenceObject ).getMinTrackingNode( n ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template< S32 NUM_DIMENSIONS, class Object >
|
|
void ScopeTracker< NUM_DIMENSIONS, Object >::debugDump()
|
|
{
|
|
for( U32 n = 0; n < NUM_DIMENSIONS; ++ n )
|
|
{
|
|
Con::printf( "Dimension %i", n );
|
|
Con::printf( "----------------" );
|
|
|
|
for( NodeType* node = mTrackingList[ n ][ 0 ]; node != NULL; node = node->getNext() )
|
|
{
|
|
String desc;
|
|
if( node->getObject() )
|
|
{
|
|
Object object = ( Object ) node->getObject();
|
|
desc = Deref( object ).describeSelf();
|
|
}
|
|
|
|
Con::printf( "pos=%f, type=%s, scope=%s, object=%s",
|
|
node->getPosition(),
|
|
node->isReference() ? "reference" : node->isMin() ? "min" : "max",
|
|
node->getObject() ? node->getObject()->isInScope( n ) ? "1" : "0" : "0",
|
|
desc.c_str() );
|
|
}
|
|
|
|
Con::printf( "" );
|
|
}
|
|
}
|
|
|
|
#endif // !_SCOPETRACKER_H_
|