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

317 lines
11 KiB
TypeScript
Raw Normal View History

2020-07-23 09:10:27 +08:00
module es {
export class SpatialHash {
public gridBounds: Rectangle = new Rectangle();
public _raycastParser: RaycastResultParser;
/**
*
*/
public _cellSize: number;
/**
* 1使
*/
public _inverseCellSize: number;
/**
*
*/
public _overlapTestCircle: Circle = new Circle(0);
/**
*
*/
public _cellDict: NumberDictionary = new NumberDictionary();
/**
* HashSet
*/
public _tempHashSet: Collider[] = [];
constructor(cellSize: number = 100) {
this._cellSize = cellSize;
this._inverseCellSize = 1 / this._cellSize;
this._raycastParser = new RaycastResultParser();
2020-06-15 10:42:06 +08:00
}
2020-06-16 11:59:40 +08:00
2020-07-23 09:10:27 +08:00
/**
* SpatialHash
* @param collider
*/
public register(collider: Collider) {
let bounds = collider.bounds;
collider.registeredPhysicsBounds = bounds;
let p1 = this.cellCoords(bounds.x, bounds.y);
let p2 = this.cellCoords(bounds.right, bounds.bottom);
// 更新边界以跟踪网格大小
if (!this.gridBounds.contains(p1.x, p1.y)) {
this.gridBounds = RectangleExt.union(this.gridBounds, p1);
2020-06-16 11:59:40 +08:00
}
2020-06-15 10:42:06 +08:00
2020-07-23 09:10:27 +08:00
if (!this.gridBounds.contains(p2.x, p2.y)) {
this.gridBounds = RectangleExt.union(this.gridBounds, p2);
}
2020-07-23 09:10:27 +08:00
for (let x = p1.x; x <= p2.x; x++) {
for (let y = p1.y; y <= p2.y; y++) {
// 如果没有单元格,我们需要创建它
2020-07-31 19:33:04 +08:00
let c: Collider[] = this.cellAtPosition(x, y, true);
if (!c.firstOrDefault(c => c.hashCode == collider.hashCode))
2020-07-23 09:10:27 +08:00
c.push(collider);
}
}
2020-07-23 09:10:27 +08:00
}
2020-07-23 09:10:27 +08:00
/**
* SpatialHash中删除对象
* @param collider
*/
public remove(collider: Collider) {
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++) {
// 单元格应该始终存在,因为这个碰撞器应该在所有查询的单元格中
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);
}
}
}
2020-07-23 09:10:27 +08:00
/**
* 使SpatialHash中删除对象
* @param obj
*/
2020-07-28 16:25:20 +08:00
public removeWithBruteForce(obj: Collider) {
2020-07-23 09:10:27 +08:00
this._cellDict.remove(obj);
}
2020-06-12 08:47:13 +08:00
2020-07-28 16:25:20 +08:00
public clear() {
2020-07-23 09:10:27 +08:00
this._cellDict.clear();
}
2020-06-12 08:47:13 +08:00
2020-07-23 09:10:27 +08:00
/**
* debug绘制空间散列的内容
* @param secondsToDisplay
* @param textScale
*/
2020-07-28 16:25:20 +08:00
public debugDraw(secondsToDisplay: number, textScale: number = 1) {
for (let x = this.gridBounds.x; x <= this.gridBounds.right; x++) {
for (let y = this.gridBounds.y; y <= this.gridBounds.bottom; y++) {
2020-07-23 09:10:27 +08:00
let cell = this.cellAtPosition(x, y);
if (cell && cell.length > 0)
this.debugDrawCellDetails(x, y, cell.length, secondsToDisplay, textScale);
}
}
}
2020-06-12 08:47:13 +08:00
2020-07-23 09:10:27 +08:00
/**
*
* @param bounds
* @param excludeCollider
* @param layerMask
*/
2020-07-28 16:25:20 +08:00
public aabbBroadphase(bounds: Rectangle, excludeCollider: Collider, layerMask: number): Collider[] {
2020-07-23 09:10:27 +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++) {
let cell = this.cellAtPosition(x, y);
if (!cell)
2020-06-12 08:47:13 +08:00
continue;
2020-07-23 09:10:27 +08:00
// 当cell不为空。循环并取回所有碰撞器
for (let i = 0; i < cell.length; i++) {
let collider = cell[i];
// 如果它是自身或者如果它不匹配我们的层掩码 跳过这个碰撞器
if (collider == excludeCollider || !Flags.isFlagSet(layerMask, collider.physicsLayer))
continue;
2020-07-28 16:25:20 +08:00
if (bounds.intersects(collider.bounds)) {
2020-07-31 19:33:04 +08:00
if (!this._tempHashSet.firstOrDefault(c => c.hashCode == collider.hashCode))
2020-07-23 09:10:27 +08:00
this._tempHashSet.push(collider);
}
}
2020-06-12 08:47:13 +08:00
}
}
2020-07-23 09:10:27 +08:00
return this._tempHashSet;
2020-06-12 08:47:13 +08:00
}
2020-07-23 09:10:27 +08:00
/**
*
* @param circleCenter
* @param radius
* @param results
* @param layerMask
*/
public overlapCircle(circleCenter: Vector2, radius: number, results: Collider[], layerMask): number {
2020-07-23 09:10:27 +08:00
let bounds = new Rectangle(circleCenter.x - radius, circleCenter.y - radius, radius * 2, radius * 2);
this._overlapTestCircle.radius = radius;
this._overlapTestCircle.position = circleCenter;
let resultCounter = 0;
let potentials = this.aabbBroadphase(bounds, null, layerMask);
2020-07-23 09:10:27 +08:00
for (let i = 0; i < potentials.length; i++) {
let collider = potentials[i];
if (collider instanceof BoxCollider) {
results[resultCounter] = collider;
resultCounter++;
} else if (collider instanceof CircleCollider) {
2020-07-28 16:25:20 +08:00
if (collider.shape.overlaps(this._overlapTestCircle)) {
2020-07-23 09:10:27 +08:00
results[resultCounter] = collider;
2020-07-28 16:25:20 +08:00
resultCounter++;
2020-07-23 09:10:27 +08:00
}
2020-07-28 16:25:20 +08:00
} else if (collider instanceof PolygonCollider) {
if (collider.shape.overlaps(this._overlapTestCircle)) {
2020-07-23 09:10:27 +08:00
results[resultCounter] = collider;
2020-07-28 16:25:20 +08:00
resultCounter++;
2020-07-23 09:10:27 +08:00
}
} else {
throw new Error("overlapCircle against this collider type is not implemented!");
}
2020-06-12 08:47:13 +08:00
2020-07-23 09:10:27 +08:00
// 如果我们所有的结果数据有了则返回
if (resultCounter == results.length)
return resultCounter;
2020-06-12 08:47:13 +08:00
}
2020-07-23 09:10:27 +08:00
return resultCounter;
}
2020-07-28 16:25:20 +08:00
/**
* x,y值作为世界空间的x,y值
* @param x
* @param y
*/
private cellCoords(x: number, y: number): Vector2 {
return new Vector2(Math.floor(x * this._inverseCellSize), Math.floor(y * this._inverseCellSize));
}
/**
* x,y值的单元格
* createCellIfEmpty为true
* @param x
* @param y
* @param createCellIfEmpty
*/
2020-07-31 19:33:04 +08:00
private cellAtPosition(x: number, y: number, createCellIfEmpty: boolean = false): Collider[] {
2020-07-28 16:25:20 +08:00
let cell: Collider[] = this._cellDict.tryGetValue(x, y);
if (!cell) {
if (createCellIfEmpty) {
cell = [];
this._cellDict.add(x, y, cell);
}
}
return cell;
}
private debugDrawCellDetails(x: number, y: number, cellCount: number, secondsToDisplay = 0.5, textScale = 1) {
}
2020-06-12 08:47:13 +08:00
}
2020-07-09 16:16:04 +08:00
/**
2020-07-23 09:10:27 +08:00
* Unit32
* intint xy坐标散列到单个Uint32键中使O(1)
2020-07-09 16:16:04 +08:00
*/
2020-07-23 09:10:27 +08:00
export class NumberDictionary {
public _store: Map<string, Collider[]> = new Map<string, Collider[]>();
public add(x: number, y: number, list: Collider[]) {
this._store.set(this.getKey(x, y), list);
}
2020-06-12 08:47:13 +08:00
2020-07-23 09:10:27 +08:00
/**
* 使
* @param obj
*/
public remove(obj: Collider) {
this._store.forEach(list => {
if (list.contains(obj))
list.remove(obj);
})
}
2020-06-12 08:47:13 +08:00
2020-07-23 09:10:27 +08:00
public tryGetValue(x: number, y: number): Collider[] {
return this._store.get(this.getKey(x, y));
}
2020-06-12 08:47:13 +08:00
2020-07-23 09:10:27 +08:00
/**
*
*/
public clear() {
this._store.clear();
}
2020-07-28 16:25:20 +08:00
/**
* x和y值计算并返回散列键
* @param x
* @param y
*/
private getKey(x: number, y: number): string {
return Long.fromNumber(x).shiftLeft(32).or(Long.fromNumber(y, true)).toString();
}
2020-06-12 08:47:13 +08:00
}
2020-07-23 09:10:27 +08:00
export class RaycastResultParser {
2020-07-31 19:33:04 +08:00
public hitCounter: number;
public static compareRaycastHits = (a: RaycastHit, b: RaycastHit) => {
return a.distance - b.distance;
};
public _hits: RaycastHit[];
public _tempHit: RaycastHit;
public _checkedColliders: Collider[] = [];
public _cellHits: RaycastHit[] = [];
public _ray: Ray2D;
public _layerMask: number;
public start(ray: Ray2D, hits: RaycastHit[], layerMask: number){
this._ray = ray;
this._hits = hits;
this._layerMask = layerMask;
this.hitCounter = 0;
}
2020-06-12 08:47:13 +08:00
2020-07-31 19:33:04 +08:00
/**
* hits数组被填充true!
* @param cellX
* @param cellY
* @param cell
*/
public checkRayIntersection(cellX: number, cellY: number, cell: Collider[]): boolean{
let fraction: number;
for (let i = 0; i < cell.length; i ++) {
let potential = cell[i];
// 管理我们已经处理过的碰撞器
if (this._checkedColliders.contains(potential))
continue;
this._checkedColliders.push(potential);
// 只有当我们被设置为这样做时才会点击触发器
if (potential.isTrigger && !Physics.raycastsHitTriggers)
continue;
// 确保碰撞器在图层蒙版上
if (!Flags.isFlagSet(this._layerMask, potential.physicsLayer))
continue;
// TODO: rayIntersects的性能够吗?需要测试它。Collisions.rectToLine可能更快
// TODO: 如果边界检查返回更多数据我们就不需要为BoxCollider检查做任何事情
// 在做形状测试之前先做一个边界检查
let colliderBounds = potential.bounds;
if (colliderBounds.)
}
}
2020-06-12 08:47:13 +08:00
}
2020-07-23 09:10:27 +08:00
}