/// Found Object Tests
        protected override bool _FoundObject(ISceneContainerObject obj, SceneContainerQueryData queryData)
        {
            // Check type on objects
            Assert.Fatal(obj is ISceneObject2D, "Invalid object passed to _FoundObject");
            Assert.Fatal(queryData is T2DSceneContainerQueryData, "Invalid query data object passed to _FoundObject");
            ISceneObject2D sceneObject = (ISceneObject2D)obj; // this is slow due to not inlined properties below, would be better to use T2DSceneObject, but lights can be in the graph and are not scene objects

            T2DSceneContainerQueryData query = (T2DSceneContainerQueryData)queryData;

            if (sceneObject.Visible || query.FindInvisible)
            {
                // Check if the Group / Layer masks match.
                int layerMask = 1 << sceneObject.Layer;
                if ((layerMask & query._layerMask) != 0)
                {
                    // Yes, so fetch Clip Rectangle.
                    if (_IntersectsWith(sceneObject, query))
                        return true;
                }
            }

            return false;
        }
        protected override void _GetBins(SceneContainerQueryData iQueryData, out uint minBinX, out uint minBinY, out uint maxBinX, out uint maxBinY)
        {
            // Check type on query data object
            Assert.Fatal(iQueryData is T2DSceneContainerQueryData, "Invalid query data object passed to _GetBins");
            T2DSceneContainerQueryData query = (T2DSceneContainerQueryData)iQueryData;

            _GetBinRange(query.Rectangle.Point.X, query.Rectangle.Point.X + query.Rectangle.Width,
                out minBinX, out maxBinX);

            _GetBinRange(query.Rectangle.Point.Y, query.Rectangle.Point.Y + query.Rectangle.Height,
                out minBinY, out maxBinY);
        }
        protected override bool _IntersectsWith(ISceneContainerObject obj, SceneContainerQueryData iQueryData)
        {
            // Check type on objects
            Assert.Fatal(obj is ISceneObject2D, "Invalid object passed to _FoundObject");
            Assert.Fatal(iQueryData is T2DSceneContainerQueryData, "Invalid query data object passed to _FoundObject");
            ISceneObject2D sceneObject = (ISceneObject2D)obj;
            T2DSceneContainerQueryData query = (T2DSceneContainerQueryData)iQueryData;

            return _IntersectsWith(sceneObject, query);
        }
 protected abstract void _GetBins(SceneContainerQueryData query, out uint minBinX, out uint minBinY, out uint maxBinX, out uint maxBinY);
 protected abstract bool _IntersectsWith(ISceneContainerObject obj, SceneContainerQueryData query);
 /// Found Object Tests
 protected abstract bool _FoundObject(ISceneContainerObject obj, SceneContainerQueryData query);
        protected void _FindObjectsInefficientHelper(SceneContainerQueryData query, SceneContainerBinReference bin, ref int objectsFound)
        {
            SceneContainerBinReference walk = bin;
            while (walk != null)
            {
                if (walk._sceneObject != null)
                {
                    // Have we dealt with this object already?
                    if (walk._sceneObject.SceneContainerData._sequenceKey == _currentSequenceKey)
                    {
                        // Move to next bin reference.
                        walk = walk._nextBinReference;
                        continue;
                    }
                    walk._sceneObject.SceneContainerData._sequenceKey = _currentSequenceKey;

                    if (_IntersectsWith(walk._sceneObject, query))
                        objectsFound++;
                }
                walk = walk._nextBinReference;
            }
        }
        protected void _FindInBin(SceneContainerBinReference bin, SceneContainerQueryData queryData)
        {
            object ignoreObject = queryData._ignoreObject;
            object[] ignoredObjects = queryData._ignoreObjects;
            ulong typebits = queryData._objectTypes._bits;

            // Step through Chain.
            while (bin != null)
            {
                // Fetch Scene Object Reference.
                ISceneContainerObject sceneObjectRef = bin._sceneObject;

                // Note: the following tries to get the object type directly from a TorqueObject rather than
                // going through the ISceneContainerObject interface.  This ends up being a LOT (a LOT) faster
                // for T2DSceneObjects even though it adds a branch.  It won't help when the object isn't a
                // TorqueObject as is the case of our T3D code, but we can consider other measures if that
                // becomes a problem (like making bin ref's point to SceneContainerData instead of scene object
                // and adding a pointer to iscene object in SceneContainerData as well as object type, but current
                // scheme is simpler and more effective for T2D).
                TorqueObject tobj = sceneObjectRef as TorqueObject;
                ulong ourType = tobj == null ? sceneObjectRef.ObjectType._bits : tobj._objectType;

                if ((ourType & typebits) == 0)
                {
                    // Move to next bin reference.
                    bin = bin._nextBinReference;
                    continue;
                }

                // get the scene container data for the object
                SceneContainerData scd = sceneObjectRef.SceneContainerData;

                // Have we dealt with this object already?
                if (scd._sequenceKey == _currentSequenceKey)
                {
                    // Move to next bin reference.
                    bin = bin._nextBinReference;
                    continue;
                }

                // Set the container sequence key to indicate that we've dealt with this object.
                scd._sequenceKey = _currentSequenceKey;

                // Check for ignored objects
                bool ignored = false;
                if (ignoreObject == sceneObjectRef)
                {
                    ignored = true;
                }
                else if (ignoredObjects != null)
                {
                    foreach (object o in ignoredObjects)
                        if (o == sceneObjectRef)
                        {
                            ignored = true;
                            break;
                        }
                }

                // Is the Object Not being ignored?
                if (!ignored)
                    _FoundObject(sceneObjectRef, queryData);

                // Move to next bin reference.
                bin = bin._nextBinReference;
            }
        }
        /// <summary>
        /// Inefficient, but always finds all the appropriate objects.  Good for verification of FindObjects.
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        protected internal int _FindObjectsInefficiently(SceneContainerQueryData query)
        {
            int objectsFound = 0;
            _currentSequenceKey++;

            foreach (SceneContainerBinReference binRef in _sceneBinArray)
                if (binRef != null)
                    _FindObjectsInefficientHelper(query, binRef, ref objectsFound);
            _FindObjectsInefficientHelper(query, _sceneOverflowBin, ref objectsFound);

            return objectsFound;
        }
        /// <summary>
        /// Perform a spatial query on the container.  Fields on the query will determine
        /// which objects are found.
        /// </summary>
        /// <param name="queryData">Query to perform.</param>
        public void FindObjects(SceneContainerQueryData queryData)
        {
            // Find which bins are covered.
            uint minX, minY, maxX, maxY;
            _GetBins(queryData, out minX, out minY, out maxX, out maxY);

            // Change the Current Sequence Key.
            _currentSequenceKey++;

            // *******************************************************************************
            // Fetch Standard Container Bins.
            // *******************************************************************************
            SceneContainerBinReference binChain = null;

            // Step through Bin ranges.
            for (uint y = minY; y <= maxY; y++)
            {
                // Calculate Bin Position.
                uint offsetY = (y % _binCount) * _binCount;

                for (uint x = minX; x <= maxX; x++)
                {
                    // Calculate Bin Position.
                    uint arrayIndex = offsetY + (x % _binCount);

                    // Fetch Bin Chain.
                    if (_sceneBinArray[arrayIndex] == null)
                        continue; // Bin is empty, continue

                    binChain = _sceneBinArray[arrayIndex]._nextBinReference;
                    _FindInBin(binChain, queryData);
                }
            }

            // *******************************************************************************
            // Fetch Overflow Bin Chain.
            // *******************************************************************************
            _FindInBin(_sceneOverflowBin._nextBinReference, queryData);

            #if TORQUE_SCENECONTAINER_DEBUG
            int count2 = _FindObjectsInefficiently(iQueryData);
            Assert.Fatal(count2 == objectsFound, "doh!");
            #endif
        }