Files
esengine/source/src/Physics/Verlet/SpatialHash.ts

169 lines
5.2 KiB
TypeScript
Raw Normal View History

class SpatialHash {
2020-06-15 10:42:06 +08:00
public gridBounds: Rectangle = new Rectangle();
2020-06-12 08:47:13 +08:00
private _raycastParser: RaycastResultParser;
private _cellSize: number;
private _inverseCellSize: number;
private _overlapTestCircle: Circle;
private _tempHashSet: Collider[] = [];
private _cellDict: NumberDictionary = new NumberDictionary();
constructor(cellSize: number = 100) {
2020-06-12 08:47:13 +08:00
this._cellSize = cellSize;
this._inverseCellSize = 1 / this._cellSize;
this._raycastParser = new RaycastResultParser();
}
public remove(collider: Collider) {
2020-06-15 10:42:06 +08:00
let bounds = collider.registeredPhysicsBounds;
let p1 = this.cellCoords(bounds.x, bounds.y);
let p2 = this.cellCoords(bounds.right, bounds.bottom);
for (let x = p1.x; x <= p2.x; x++) {
for (let y = p1.y; y <= p2.y; y++) {
2020-06-15 10:42:06 +08:00
let cell = this.cellAtPosition(x, y);
if (!cell)
console.error(`removing Collider [${collider}] from a cell that it is not present in`);
else
cell.remove(collider);
}
}
}
public register(collider: Collider) {
2020-06-15 10:42:06 +08:00
let bounds = collider.bounds;
collider.registeredPhysicsBounds = bounds;
let p1 = this.cellCoords(bounds.x, bounds.y);
let p2 = this.cellCoords(bounds.right, bounds.bottom);
2020-06-16 11:59:40 +08:00
if (!this.gridBounds.contains(new Vector2(p1.x, p1.y))) {
2020-06-16 11:59:40 +08:00
this.gridBounds = RectangleExt.union(this.gridBounds, p1);
}
if (!this.gridBounds.contains(new Vector2(p2.x, p2.y))) {
2020-06-16 11:59:40 +08:00
this.gridBounds = RectangleExt.union(this.gridBounds, p2);
}
for (let x = p1.x; x <= p2.x; x++) {
for (let y = p1.y; y <= p2.y; y++) {
2020-06-16 11:59:40 +08:00
let c = this.cellAtPosition(x, y, true);
c.push(collider);
}
}
2020-06-15 10:42:06 +08:00
}
public clear(){
this._cellDict.clear();
}
public overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask) {
2020-06-12 08:47:13 +08:00
let bounds = new Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2);
this._overlapTestCircle.radius = radius;
this._overlapTestCircle.position = circleCenter;
2020-06-17 20:40:56 +08:00
let resultCounter = 0;
2020-06-12 08:47:13 +08:00
let potentials = this.aabbBroadphase(bounds, null, layerMask);
2020-06-17 20:40:56 +08:00
for (let i = 0; i < potentials.length; i++) {
let collider = potentials[i];
2020-06-17 20:40:56 +08:00
if (collider instanceof BoxCollider) {
results[resultCounter] = collider;
2020-06-17 20:40:56 +08:00
resultCounter++;
} else {
throw new Error("overlapCircle against this collider type is not implemented!");
}
2020-06-12 08:47:13 +08:00
if (resultCounter == results.length)
return resultCounter;
}
return resultCounter;
}
2020-06-12 08:47:13 +08:00
public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number) {
2020-06-12 08:47:13 +08:00
this._tempHashSet.length = 0;
let p1 = this.cellCoords(bounds.x, bounds.y);
let p2 = this.cellCoords(bounds.right, bounds.bottom);
for (let x = p1.x; x <= p2.x; x++) {
for (let y = p1.y; y <= p2.y; y++) {
2020-06-12 08:47:13 +08:00
let cell = this.cellAtPosition(x, y);
if (!cell)
continue;
for (let i = 0; i < cell.length; i++) {
2020-06-12 08:47:13 +08:00
let collider = cell[i];
if (collider == excludeCollider || !Flags.isFlagSet(layerMask, collider.physicsLayer))
continue;
if (bounds.intersects(collider.bounds)){
if (this._tempHashSet.indexOf(collider) == -1)
this._tempHashSet.push(collider);
}
2020-06-12 08:47:13 +08:00
}
}
}
return this._tempHashSet;
}
private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false) {
2020-06-12 08:47:13 +08:00
let cell: Collider[] = this._cellDict.tryGetValue(x, y);
if (!cell) {
if (createCellIfEmpty) {
2020-06-12 08:47:13 +08:00
cell = [];
this._cellDict.add(x, y, cell);
}
}
return cell;
}
private cellCoords(x: number, y: number): Point {
return new Point(Math.floor(x * this._inverseCellSize), Math.floor(y * this._inverseCellSize));
}
}
class RaycastResultParser {
}
class NumberDictionary {
private _store: Map<number, Collider[]> = new Map<number, Collider[]>();
2020-06-16 20:22:22 +08:00
/**
* x和y值计算并返回散列键
* @param x
* @param y
*/
private getKey(x: number, y: number): number {
2020-06-17 23:19:25 +08:00
return Long.fromNumber(x).shiftLeft(32).or(this.intToUint(y)).toString();
2020-06-16 20:22:22 +08:00
}
2020-06-17 20:40:56 +08:00
private intToUint(i) {
2020-06-16 20:22:22 +08:00
if (i >= 0)
return i;
else
return 4294967296 + i;
2020-06-12 08:47:13 +08:00
}
public add(x: number, y: number, list: Collider[]) {
2020-06-12 08:47:13 +08:00
this._store.set(this.getKey(x, y), list);
}
public remove(obj: Collider) {
2020-06-12 08:47:13 +08:00
this._store.forEach(list => {
if (list.contains(obj))
list.remove(obj);
})
}
public tryGetValue(x: number, y: number): Collider[] {
2020-06-12 08:47:13 +08:00
return this._store.get(this.getKey(x, y));
}
public clear() {
2020-06-12 08:47:13 +08:00
this._store.clear();
}
}