using System; using System.Collections.Generic; using FarseerPhysics; using FarseerPhysics.Collision; using FarseerPhysics.Dynamics; using Microsoft.Xna.Framework; public class QuadTreeBroadPhase : IBroadPhase { private const int TreeUpdateThresh = 10000; private int _currID; private Dictionary> _idRegister; private List> _moveBuffer; private List _pairBuffer; private QuadTree _quadTree; private int _treeMoveNum; /// /// Creates a new quad tree broadphase with the specified span. /// /// the maximum span of the tree (world size) public QuadTreeBroadPhase(AABB span) { _quadTree = new QuadTree(span, 5, 10); _idRegister = new Dictionary>(); _moveBuffer = new List>(); _pairBuffer = new List(); } #region IBroadPhase Members /// /// The number of proxies /// public int ProxyCount { get { return _idRegister.Count; } } public void GetFatAABB(int proxyID, out AABB aabb) { if (_idRegister.ContainsKey(proxyID)) aabb = _idRegister[proxyID].Span; else throw new KeyNotFoundException("proxyID not found in register"); } public void UpdatePairs(BroadphaseDelegate callback) { _pairBuffer.Clear(); foreach (Element qtnode in _moveBuffer) { // Query tree, create pairs and add them pair buffer. Query(proxyID => PairBufferQueryCallback(proxyID, qtnode.Value.ProxyId), ref qtnode.Span); } _moveBuffer.Clear(); // Sort the pair buffer to expose duplicates. _pairBuffer.Sort(); // Send the pairs back to the client. int i = 0; while (i < _pairBuffer.Count) { Pair primaryPair = _pairBuffer[i]; FixtureProxy userDataA = GetProxy(primaryPair.ProxyIdA); FixtureProxy userDataB = GetProxy(primaryPair.ProxyIdB); callback(ref userDataA, ref userDataB); ++i; // Skip any duplicate pairs. while (i < _pairBuffer.Count && _pairBuffer[i].ProxyIdA == primaryPair.ProxyIdA && _pairBuffer[i].ProxyIdB == primaryPair.ProxyIdB) ++i; } } /// /// Test overlap of fat AABBs. /// /// The proxy id A. /// The proxy id B. /// public bool TestOverlap(int proxyIdA, int proxyIdB) { AABB aabb1; AABB aabb2; GetFatAABB(proxyIdA, out aabb1); GetFatAABB(proxyIdB, out aabb2); return AABB.TestOverlap(ref aabb1, ref aabb2); } public int AddProxy(ref FixtureProxy proxy) { int proxyID = _currID++; proxy.ProxyId = proxyID; AABB aabb = Fatten(ref proxy.AABB); Element qtnode = new Element(proxy, aabb); _idRegister.Add(proxyID, qtnode); _quadTree.AddNode(qtnode); return proxyID; } public void RemoveProxy(int proxyId) { if (_idRegister.ContainsKey(proxyId)) { Element qtnode = _idRegister[proxyId]; UnbufferMove(qtnode); _idRegister.Remove(proxyId); _quadTree.RemoveNode(qtnode); } else throw new KeyNotFoundException("proxyID not found in register"); } public void MoveProxy(int proxyId, ref AABB aabb, Vector2 displacement) { AABB fatAABB; GetFatAABB(proxyId, out fatAABB); //exit if movement is within fat aabb if (fatAABB.Contains(ref aabb)) return; // Extend AABB. AABB b = aabb; Vector2 r = new Vector2(Settings.AABBExtension, Settings.AABBExtension); b.LowerBound = b.LowerBound - r; b.UpperBound = b.UpperBound + r; // Predict AABB displacement. Vector2 d = Settings.AABBMultiplier * displacement; if (d.X < 0.0f) b.LowerBound.X += d.X; else b.UpperBound.X += d.X; if (d.Y < 0.0f) b.LowerBound.Y += d.Y; else b.UpperBound.Y += d.Y; Element qtnode = _idRegister[proxyId]; qtnode.Value.AABB = b; //not neccesary for QTree, but might be accessed externally qtnode.Span = b; ReinsertNode(qtnode); BufferMove(qtnode); } public FixtureProxy GetProxy(int proxyId) { if (_idRegister.ContainsKey(proxyId)) return _idRegister[proxyId].Value; else throw new KeyNotFoundException("proxyID not found in register"); } public void TouchProxy(int proxyId) { if (_idRegister.ContainsKey(proxyId)) BufferMove(_idRegister[proxyId]); else throw new KeyNotFoundException("proxyID not found in register"); } public void Query(Func callback, ref AABB query) { _quadTree.QueryAABB(TransformPredicate(callback), ref query); } public void RayCast(Func callback, ref RayCastInput input) { _quadTree.RayCast(TransformRayCallback(callback), ref input); } #endregion private AABB Fatten(ref AABB aabb) { Vector2 r = new Vector2(Settings.AABBExtension, Settings.AABBExtension); return new AABB(aabb.LowerBound - r, aabb.UpperBound + r); } private Func, bool> TransformPredicate(Func idPredicate) { Func, bool> qtPred = qtnode => idPredicate(qtnode.Value.ProxyId); return qtPred; } private Func, float> TransformRayCallback( Func callback) { Func, float> newCallback = (input, qtnode) => callback(input, qtnode.Value.ProxyId); return newCallback; } private bool PairBufferQueryCallback(int proxyID, int baseID) { // A proxy cannot form a pair with itself. if (proxyID == baseID) return true; Pair p = new Pair(); p.ProxyIdA = Math.Min(proxyID, baseID); p.ProxyIdB = Math.Max(proxyID, baseID); _pairBuffer.Add(p); return true; } private void ReconstructTree() { //this is faster than _quadTree.Reconstruct(), since the quadtree method runs a recusive query to find all nodes. _quadTree.Clear(); foreach (Element elem in _idRegister.Values) _quadTree.AddNode(elem); } private void ReinsertNode(Element qtnode) { _quadTree.RemoveNode(qtnode); _quadTree.AddNode(qtnode); if (++_treeMoveNum > TreeUpdateThresh) { ReconstructTree(); _treeMoveNum = 0; } } private void BufferMove(Element proxy) { _moveBuffer.Add(proxy); } private void UnbufferMove(Element proxy) { _moveBuffer.Remove(proxy); } }