/// <summary> /// Initializes a new CollisionSystem which uses a grid to speed up collision detection. /// Use this system for larger scenes with many objects. /// </summary> /// <param name="nx">Number of GridEntries in X Direction.</param> /// <param name="ny">Number of GridEntries in Y Direction.</param> /// <param name="nz">Number of GridEntries in Z Direction.</param> /// <param name="dx">Size of a single GridEntry in X Direction.</param> /// <param name="dy">Size of a single GridEntry in Y Direction.</param> /// <param name="dz">Size of a single GridEntry in Z Direction.</param> public CollisionSystemGrid(int nx, int ny, int nz, float dx, float dy, float dz) { this.nx = nx; this.ny = ny; this.nz = nz; this.dx = dx; this.dy = dy; this.dz = dz; this.sizeX = nx * dx; this.sizeY = ny * dy; this.sizeZ = nz * dz; this.minDelta = System.Math.Min(System.Math.Min(dx, dy), dz); int numEntries = nx * ny * nz * 2; // we allocate twice as many as need for collision skins gridEntries = new List<GridEntry>(numEntries); gridBoxes = new List<AABox>(numEntries); tempGridLists = new List<GridEntry>(numEntries); freeGrids = new Stack<GridEntry>(numEntries); for (int i = 0; i < numEntries; ++i) { GridEntry entry = new GridEntry(); entry.GridIndex = -2; freeGrids.Push(entry); } for (int i = 0; i < (nx * ny * nz); ++i) { GridEntry gridEntry = freeGrids.Pop(); gridEntry.GridIndex = i; gridEntries.Add(gridEntry); gridBoxes.Add(null); } overflowEntries = freeGrids.Pop(); overflowEntries.GridIndex = -1; for (int iX = 0; iX < nx; ++iX) { for (int iY = 0; iY < ny; ++iY) { for (int iZ = 0; iZ < nz; ++iZ) { AABox box = new AABox(); box.AddPoint(new Vector3(iX * dx, iY * dy, iZ + dz)); box.AddPoint(new Vector3(iX * dx + dx, iY * dy + dy, iZ * dz + dz)); int index = CalcIndex(iX, iY, iZ); gridBoxes[index] = box; } } } }
/// <summary> /// Removes the entry by updating its neighbours. Also zaps the prev/next /// pointers in the entry, to help debugging /// </summary> /// <param name="entry"></param> public static void RemoveGridEntry(GridEntry entry) { // link the previous to the next (may be 0) entry.Previous.Next = entry.Next; // link the next (if it exists) to the previous. if (entry.Next != null) entry.Next.Previous = entry.Previous; // tidy up this entry entry.Previous = entry.Next = null; entry.GridIndex = -2; }
/// <summary> /// Inserts an entry after prev, updating all links /// </summary> /// <param name="entry"></param> /// <param name="prev"></param> public static void InsertGridEntryAfter(GridEntry entry, GridEntry prev) { GridEntry next = prev.Next; prev.Next = entry; entry.Previous = prev; entry.Next = next; if (next != null) next.Previous = entry; entry.GridIndex = prev.GridIndex; }
public GridEntry(CollisionSkin skin) { this.Skin = skin; this.Previous = this.Next = null; }
/// <summary> /// DetectAllCollisions /// </summary> /// <param name="bodies"></param> /// <param name="collisionFunctor"></param> /// <param name="collisionPredicate"></param> /// <param name="collTolerance"></param> public override void DetectAllCollisions(List <Body> bodies, CollisionFunctor collisionFunctor, CollisionSkinPredicate2 collisionPredicate, float collTolerance) { int numBodies = bodies.Count; CollDetectInfo info = new CollDetectInfo(); for (int iBody = 0; iBody < numBodies; ++iBody) { Body body = bodies[iBody]; if (!body.IsActive) { continue; } info.Skin0 = body.CollisionSkin; if (info.Skin0 == null) { continue; } tempGridLists.Clear(); GetListsToCheck(tempGridLists, info.Skin0); for (int iList = tempGridLists.Count; iList-- != 0;) { // first one is a placeholder; GridEntry entry = tempGridLists[iList]; for (entry = entry.Next; entry != null; entry = entry.Next) { info.Skin1 = entry.Skin; if (info.Skin1 == info.Skin0) { continue; } // CHANGE if (info.Skin1 == null) { continue; } bool skinSleeping = true; if ((info.Skin1.Owner != null) && (info.Skin1.Owner.IsActive)) { skinSleeping = false; } // only do one per pair if ((skinSleeping == false) && (info.Skin1.ID < info.Skin0.ID)) { continue; } if ((collisionPredicate != null) && (!collisionPredicate.ConsiderSkinPair(info.Skin0, info.Skin1))) { continue; } // basic bbox test if (BoundingBoxHelper.OverlapTest(ref info.Skin1.WorldBoundingBox, ref info.Skin0.WorldBoundingBox, collTolerance)) { if (CheckCollidables(info.Skin0, info.Skin1)) { int bodyPrimitives = info.Skin0.NumPrimitives; int primitves = info.Skin1.NumPrimitives; for (info.IndexPrim0 = 0; info.IndexPrim0 < bodyPrimitives; ++info.IndexPrim0) { for (info.IndexPrim1 = 0; info.IndexPrim1 < primitves; ++info.IndexPrim1) { DetectFunctor f = GetCollDetectFunctor(info.Skin0.GetPrimitiveNewWorld(info.IndexPrim0).Type, info.Skin1.GetPrimitiveNewWorld(info.IndexPrim1).Type); if (f != null) { f.CollDetect(info, collTolerance, collisionFunctor); } } } } // check collidables } // overlapt test } // loop over entries } // loop over lists } // loop over bodies }
/// <summary> /// Constructor /// </summary> /// <param name="skin"></param> public GridEntry(CollisionSkin skin) { this.Skin = skin; this.Previous = this.Next = null; }
/// <summary> /// GetListsToCheck /// </summary> /// <param name="entries"></param> /// <param name="skin"></param> private void GetListsToCheck(List <GridEntry> entries, CollisionSkin skin) { entries.Clear(); GridEntry entry = (GridEntry)skin.ExternalData; if (entry == null) { System.Diagnostics.Debug.WriteLine("Warning skin has grid entry null!"); //TRACE("Warning = skin %s has grid entry 0!\n", skin); return; } // todo - work back from the mGridIndex rather than calculating it again... int i, j, k; float fi, fj, fk; CalcGridForSkin(out i, out j, out k, out fi, out fj, out fk, skin); if (i == -1) { // oh dear - add everything for (i = 0; i < gridEntries.Count; ++i) { if (gridEntries[i].Next != null) { entries.Add(gridEntries[i]); } } //entries = gridEntries; entries.Add(overflowEntries); return; } // always add the overflow entries.Add(overflowEntries); Vector3 delta = skin.WorldBoundingBox.Max - skin.WorldBoundingBox.Min; int maxI = 1, maxJ = 1, maxK = 1; if (fi + (delta.X / dx) < 1.0f) { maxI = 0; } if (fj + (delta.Y / dy) < 1.0f) { maxJ = 0; } if (fk + (delta.Z / dz) < 1.0f) { maxK = 0; } // now add the contents of all 18 grid boxes - their contents may extend beyond the bounds for (int di = -1; di <= maxI; ++di) { for (int dj = -1; dj <= maxJ; ++dj) { for (int dk = -1; dk <= maxK; ++dk) { int thisIndex = CalcIndex(nx + i + di, ny + j + dj, nz + k + dk); GridEntry start = gridEntries[thisIndex]; if (start.Next != null) { entries.Add(start); } } } } }