public MarkContext(cpBBTree tree, Node staticRoot, Func <object, object, ulong, object, ulong> func, object data) { this.tree = tree; this.staticRoot = staticRoot; this.func = func; this.data = data; }
///////////////////////////////////////////////// public cpBBTree(cpBBTree staticIndex) { this.staticIndex = staticIndex; if (staticIndex != null) { if (staticIndex.dynamicIndex != null) { throw new NotImplementedException("This static index is already associated with a dynamic index."); } staticIndex.dynamicIndex = this; } this.velocityFunc = null; // This is a hash from object ID -> object for the objects stored in the BBTree. leaves = new Dictionary <ulong, Leaf>(); // elements = new Dictionary<int, object>(); root = null; // A linked list containing an object pool of tree nodes and pairs. this.pooledNodes = null; this.pooledPairs = null; stamp = 0; }
public void ClearPairs(cpBBTree tree) { Pair pair = this.pairs; Pair next; this.pairs = null; while (pair != null) { if (pair.a.leaf == this) { next = pair.a.next; Thread.Unlink(pair.b.prev, pair.b.leaf, pair.b.next); } else { next = pair.b.next; Thread.Unlink(pair.a.prev, pair.a.leaf, pair.a.next); } tree.PairRecycle(pair); pair = next; } }
public virtual void MarkLeafQuery(Leaf leaf, bool left, cpBBTree tree, Func <object, object, ulong, object, ulong> func) { if (cp.bbTreeIntersectsNode(leaf, this)) { this.A.MarkLeafQuery(leaf, left, tree, func); this.B.MarkLeafQuery(leaf, left, tree, func); } }
public Node(Node a, Node b, cpBBTree tree) { this.obj = null; bb = cpBB.Merge(a.bb, b.bb); parent = null; this.SetA(a); this.SetB(b); }
// Collide the objects in an index against the objects in a staticIndex using the query callback function. public void CollideStatic(cpBBTree staticIndex, Func <object, object, ulong, object, ulong> func, object data) { if (staticIndex != null && staticIndex.Count > 0) { Each((obj) => { // dynamicToStaticContext context = new dynamicToStaticContext(dynamicIndex->bbfunc, staticIndex, func, data); staticIndex.Query(staticIndex, new cpBB(obj.bb.l, obj.bb.b, obj.bb.r, obj.bb.t), func, data); }); } }
/// <summary> /// /// </summary> /// <param name="cpBBTree"></param> /// <param name="value"></param> public Leaf(cpBBTree tree, IObjectBox obj) : base() { this.obj = obj; //THIS IS THE GENERIC REAL VALUE tree.GetBB(obj, this); this.parent = null; this.STAMP = 1; this.PAIRS = null; cp.numLeaves++; }
public void AddPairs(cpBBTree tree) { cpBBTree dynamicIndex = tree.dynamicIndex; if (dynamicIndex != null) { var dynamicRoot = dynamicIndex.root; if (dynamicRoot != null) { dynamicRoot.MarkLeafQuery(this, true, dynamicIndex, null); } } else { var staticRoot = tree.staticIndex.root; this.MarkSubtree(tree, staticRoot, null); } }
public bool Update(cpBBTree tree) { var root = tree.root; var obj = this.obj; if (!this.ContainsObj(obj)) { this.bb = tree.GetBB(this.obj); root = tree.SubtreeRemove(root, this); tree.root = tree.SubtreeInsert(root, this); //tree.root = SubtreeInsert(root, this, tree); this.ClearPairs(tree); this.stamp = tree.GetStamp(); return(true); } return(false); }
public override void MarkSubtree(cpBBTree tree, Node staticRoot, Func <object, object, ulong, object, ulong> func) { if (this.stamp == tree.GetStamp()) { if (staticRoot != null) { staticRoot.MarkLeafQuery(this, false, tree, func); } for (Node node = this; node.parent != null; node = node.parent) { if (node == node.parent.A) { node.parent.B.MarkLeafQuery(this, true, tree, func); } else { node.parent.A.MarkLeafQuery(this, false, tree, func); } } } else { var pair = this.pairs; while (pair != null) { if (this == pair.b.leaf) { if (func != null) { func(pair.a.leaf.obj, this.obj, pair.id, null); } pair = pair.b.next; } else { pair = pair.a.next; } } } }
public void ReplaceChild(Node child, Node value, cpBBTree tree) { cp.AssertSoft(child == this.A || child == this.B, "Node is not a child of parent."); if (this.A == child) { tree.NodeRecycle(this.A); //.Recycle(tree); this.SetA(value); } else { tree.NodeRecycle(this.B); this.SetB(value); } for (var node = this; node != null; node = node.parent) { node.bb = node.A.bb.Merge(node.B.bb); } }
public override void MarkLeafQuery(Leaf leaf, bool left, cpBBTree tree, Func <object, object, ulong, object, ulong> func) { if (cp.bbTreeIntersectsNode(leaf, this)) { if (left) { tree.PairInsert(leaf, this); } else { if (this.stamp < leaf.stamp) { tree.PairInsert(this, leaf); } if (func != null) { func(leaf.obj, this.obj, (ulong)leaf.stamp, null); } } } }
public virtual void Recycle(cpBBTree tree) { this.parent = tree.pooledNodes; tree.pooledNodes = this; }
public virtual void MarkSubtree(cpBBTree tree, Node staticRoot, Func<object, object, ulong, object, ulong> func) { this.a.MarkSubtree(tree, staticRoot, func); this.b.MarkSubtree(tree, staticRoot, func); }
public MarkContext(cpBBTree tree, Node staticRoot, Func<object, object, ulong, object, ulong> func, object data) { this.tree = tree; this.staticRoot = staticRoot; this.func = func; this.data = data; }
public virtual void MarkLeafQuery(Leaf leaf, bool left, cpBBTree tree, Func<object, object, ulong, object, ulong> func) { if (cp.bbTreeIntersectsNode(leaf, this)) { this.A.MarkLeafQuery(leaf, left, tree, func); this.B.MarkLeafQuery(leaf, left, tree, func); } }
///////////////////////////////////////////////// public cpBBTree(cpBBTree staticIndex) { this.staticIndex = staticIndex; if (staticIndex != null) { if (staticIndex.dynamicIndex != null) { throw new NotImplementedException("This static index is already associated with a dynamic index."); } staticIndex.dynamicIndex = this; } this.velocityFunc = null; // This is a hash from object ID -> object for the objects stored in the BBTree. leaves = new Dictionary<ulong, Leaf>(); // elements = new Dictionary<int, object>(); root = null; // A linked list containing an object pool of tree nodes and pairs. this.pooledNodes = null; this.pooledPairs = null; stamp = 0; }
public override void MarkSubtree(cpBBTree tree, Node staticRoot, Func<object, object, ulong, object, ulong> func) { if (this.stamp == tree.GetStamp()) { if (staticRoot != null) staticRoot.MarkLeafQuery(this, false, tree, func); for (Node node = this; node?.parent != null; node = node.parent) { if (node == node.parent.A) { node.parent.B.MarkLeafQuery(this, true, tree, func); } else { node.parent.A.MarkLeafQuery(this, false, tree, func); } } } else { var pair = this.pairs; while (pair != null) { if (this == pair.b.leaf) { if (func != null) func(pair.a.leaf.obj, this.obj, pair.id, null); pair = pair.b.next; } else { pair = pair.a.next; } } } }
public override void Recycle(cpBBTree tree) { }
public void ReplaceChild(Node child, Node value, cpBBTree tree) { cp.AssertSoft(child == this.A || child == this.B, "Node is not a child of parent."); if (this.A == child) { tree.NodeRecycle(this.A);//.Recycle(tree); this.SetA(value); } else { tree.NodeRecycle(this.B); this.SetB(value); } for (var node = this; node != null; node = node.parent) { node.bb = node.A.bb.Merge(node.B.bb); } }
// Used for disposing of collision handlers. //static void FreeWrap(void* ptr, void* unused) { cpfree(ptr); } //MARK: Memory Management Functions public cpSpace() { #if DEBUG Debug.WriteLine("Initializing cpSpace - Chipmunk v{0} (Debug Enabled)\n", cp.cpVersionString); Debug.WriteLine("Compile with -DNDEBUG defined to disable debug mode and runtime assertion checks\n"); #endif /// Number of iterations to use in the impulse solver to solve contacts. this.iterations = 10; /// Gravity to pass to rigid bodies when integrating velocity. this.gravity = cpVect.Zero; /// Damping rate expressed as the fraction of velocity bodies retain each second. /// A value of 0.9 would mean that each body's velocity will drop 10% per second. /// The default value is 1.0, meaning no damping is applied. /// @note This damping value is different than those of cpDampedSpring and cpDampedRotarySpring. this.damping = 1; /// Amount of encouraged penetration between colliding shapes.. /// Used to reduce oscillating contacts and keep the collision cache warm. /// Defaults to 0.1. If you have poor simulation quality, /// increase this number as much as possible without allowing visible amounts of overlap. this.collisionSlop = 0.1f; /// Determines how fast overlapping shapes are pushed apart. /// Expressed as a fraction of the error remaining after each second. /// Defaults to pow(1.0 - 0.1, 60.0) meaning that Chipmunk fixes 10% of overlap each frame at 60Hz. this.collisionBias = cp.cpfpow(1f - 0.1f, 60f); /// Number of frames that contact information should persist. /// Defaults to 3. There is probably never a reason to change this value. this.collisionPersistence = 3; this.locked = 0; this.stamp = 0; this.staticShapes = new cpBBTree(null); this.dynamicShapes = new cpBBTree(this.staticShapes); this.dynamicShapes.SetVelocityFunc(o => ShapeVelocityFunc(o as cpShape)); this.dynamicBodies = new List <cpBody>(); this.staticBodies = new List <cpBody>(); this.rousedBodies = new List <cpBody>(); this.sleepingComponents = new List <cpBody>(); /// Time a group of bodies must remain idle in order to fall asleep. /// Enabling sleeping also implicitly enables the the contact graph. /// The default value of Infinity disables the sleeping algorithm. this.sleepTimeThreshold = cp.Infinity; /// Speed threshold for a body to be considered idle. /// The default value of 0 means to let the space guess a good threshold based on gravity. this.idleSpeedThreshold = 0; this.arbiters = new List <cpArbiter>(); this.cachedArbiters = new Dictionary <ulong, cpArbiter>(); this.constraints = new List <cpConstraint>(); this.usesWildcards = false; this.defaultHandler = cpCollisionHandlerDoNothing; this.collisionHandlers = new Dictionary <ulong, cpCollisionHandler>(); this.postStepCallbacks = new List <cpPostStepCallback>(); this.skipPostStep = false; /// The designated static body for this space. /// You can modify this body, or replace it with your own static body. /// By default it points to a statically allocated cpBody in the cpSpace struct. this.staticBody = cpBody.NewStatic(); }
public void Recycle(cpBBTree tree) { this.a.prev = tree.pooledPairs; tree.pooledPairs = this; }
// Collide the objects in an index against the objects in a staticIndex using the query callback function. public void CollideStatic(cpBBTree staticIndex, Func<object, object, ulong, object, ulong> func, object data) { if (staticIndex != null && staticIndex.Count > 0) { Each((obj) => { // dynamicToStaticContext context = new dynamicToStaticContext(dynamicIndex->bbfunc, staticIndex, func, data); staticIndex.Query(staticIndex, new cpBB(obj.bb.l, obj.bb.b, obj.bb.r, obj.bb.t), func, data); }); } }
public virtual void MarkSubtree(cpBBTree tree, Node staticRoot, Func <object, object, ulong, object, ulong> func) { this.a.MarkSubtree(tree, staticRoot, func); this.b.MarkSubtree(tree, staticRoot, func); }
public override void MarkLeafQuery(Leaf leaf, bool left, cpBBTree tree, Func<object, object, ulong, object, ulong> func) { if (cp.bbTreeIntersectsNode(leaf, this)) { if (left) { tree.PairInsert(leaf, this); } else { if (this.stamp < leaf.stamp) tree.PairInsert(this, leaf); if (func != null) func(leaf.obj, this.obj, (ulong)leaf.stamp, null); } } }
public cpBBTree GetMasterTree() { cpBBTree dynamicTree = this.dynamicIndex; return(dynamicTree != null ? dynamicTree : this); }
public bool Update(cpBBTree tree) { var root = tree.root; var obj = this.obj; if (!this.ContainsObj(obj)) { this.bb = tree.GetBB(this.obj); root = tree.SubtreeRemove(root, this); tree.root = tree.SubtreeInsert(root, this);//tree.root = SubtreeInsert(root, this, tree); this.ClearPairs(tree); this.stamp = tree.GetStamp(); return true; } return false; }
public void SetBodyType(cpBodyType type) { cpBodyType oldType = bodyType; if (oldType == type) { return; } // Static bodies have their idle timers set to infinity. // Non-static bodies should have their idle timer reset. nodeIdleTime = (type == cpBodyType.STATIC ? cp.Infinity : 0.0f); if (type == cpBodyType.DYNAMIC) { this.m = this.i = 0.0f; this.m_inv = this.i_inv = cp.Infinity; AccumulateMassFromShapes(); } else { this.m = this.i = cp.Infinity; this.m_inv = this.i_inv = 0.0f; this.v = cpVect.Zero; this.w = 0.0f; } // If the body is added to a space already, we'll need to update some space data structures. if (space != null) { cp.AssertSpaceUnlocked(space); if (oldType == cpBodyType.STATIC) { // TODO This is probably not necessary // cpBodyActivateStatic(body, NULL); } else { Activate(); } // Move the bodies to the correct array. List <cpBody> fromArray = space.ArrayForBodyType(oldType); List <cpBody> toArray = space.ArrayForBodyType(type); if (fromArray != toArray) { fromArray.Remove(this); toArray.Add(this); } // Move the body's shapes to the correct spatial index. cpBBTree fromIndex = (oldType == cpBodyType.STATIC ? space.staticShapes : space.dynamicShapes); cpBBTree toIndex = (type == cpBodyType.STATIC ? space.staticShapes : space.dynamicShapes); if (fromIndex != toIndex) { eachShape((s, o) => { fromIndex.Remove(s.hashid); toIndex.Insert(s.hashid, s); }, null); } } }
// Used for disposing of collision handlers. //static void FreeWrap(void* ptr, void* unused) { cpfree(ptr); } //MARK: Memory Management Functions public cpSpace() { #if DEBUG Debug.WriteLine("Initializing cpSpace - Chipmunk v{0} (Debug Enabled)\n", cp.cpVersionString); Debug.WriteLine("Compile with -DNDEBUG defined to disable debug mode and runtime assertion checks\n"); #endif /// Number of iterations to use in the impulse solver to solve contacts. this.iterations = 10; /// Gravity to pass to rigid bodies when integrating velocity. this.gravity = cpVect.Zero; /// Damping rate expressed as the fraction of velocity bodies retain each second. /// A value of 0.9 would mean that each body's velocity will drop 10% per second. /// The default value is 1.0, meaning no damping is applied. /// @note This damping value is different than those of cpDampedSpring and cpDampedRotarySpring. this.damping = 1; /// Amount of encouraged penetration between colliding shapes.. /// Used to reduce oscillating contacts and keep the collision cache warm. /// Defaults to 0.1. If you have poor simulation quality, /// increase this number as much as possible without allowing visible amounts of overlap. this.collisionSlop = 0.1f; /// Determines how fast overlapping shapes are pushed apart. /// Expressed as a fraction of the error remaining after each second. /// Defaults to pow(1.0 - 0.1, 60.0) meaning that Chipmunk fixes 10% of overlap each frame at 60Hz. this.collisionBias = cp.cpfpow(1f - 0.1f, 60f); /// Number of frames that contact information should persist. /// Defaults to 3. There is probably never a reason to change this value. this.collisionPersistence = 3; this.locked = 0; this.stamp = 0; this.staticShapes = new cpBBTree(null); this.dynamicShapes = new cpBBTree(this.staticShapes); this.dynamicShapes.SetVelocityFunc(o => ShapeVelocityFunc(o as cpShape)); this.dynamicBodies = new List<cpBody>(); this.staticBodies = new List<cpBody>(); this.rousedBodies = new List<cpBody>(); this.sleepingComponents = new List<cpBody>(); /// Time a group of bodies must remain idle in order to fall asleep. /// Enabling sleeping also implicitly enables the the contact graph. /// The default value of Infinity disables the sleeping algorithm. this.sleepTimeThreshold = cp.Infinity; /// Speed threshold for a body to be considered idle. /// The default value of 0 means to let the space guess a good threshold based on gravity. this.idleSpeedThreshold = 0; this.arbiters = new List<cpArbiter>(); this.cachedArbiters = new Dictionary<ulong, cpArbiter>(); this.constraints = new List<cpConstraint>(); this.usesWildcards = false; this.defaultHandler = cpCollisionHandlerDoNothing; this.collisionHandlers = new Dictionary<ulong, cpCollisionHandler>(); this.postStepCallbacks = new List<cpPostStepCallback>(); this.skipPostStep = false; /// The designated static body for this space. /// You can modify this body, or replace it with your own static body. /// By default it points to a statically allocated cpBody in the cpSpace struct. this.staticBody = cpBody.NewStatic(); }