perf: collision hashes

This commit is contained in:
cha0s 2024-10-11 13:07:05 -05:00
parent ebc4ebd957
commit 3a37cf4938
2 changed files with 144 additions and 14 deletions

View File

@ -17,7 +17,7 @@ export default class Colliders extends System {
if (!checked.has(entity)) {
checked.set(entity, new Set());
}
const within = this.ecs.system('MaintainColliderHash').within(entity.Collider.aabb);
const within = this.ecs.system('MaintainColliderHash').collisions(entity);
for (const other of within) {
if (entity === other || !other.Collider) {
continue;

View File

@ -3,12 +3,103 @@ import SpatialHash from '@/util/spatial-hash.js';
export default class MaintainColliderHash extends System {
hash;
AreaSize;
groups = new Map();
collisions(entity) {
const collisions = new Set();
const {aabb, bodies} = entity.Collider;
for (let {bits, group, mask} of bodies) {
const bitsList = [];
const maskList = [];
let bit = 0;
while (bits > 0) {
if (bits & 1) {
bitsList.push(bit);
}
bit += 1;
bits >>= 1;
}
if (mask !== 4294967295) {
bit = 0;
while (mask > 0) {
if (mask & 1) {
maskList.push(bit);
}
bit += 1;
mask >>= 1;
}
}
else {
maskList.push(-1);
}
for (const [key, hashes] of this.groups) {
if (group === key && group < 0) {
continue;
}
if (group === key && group > 0) {
const within = hashes.group.within(aabb);
for (const other of within) {
if (other !== entity.id) {
collisions.add(this.ecs.get(other));
}
}
}
if (key !== group || 0 === key) {
if (maskList.length > 0) {
if (-1 === maskList[0]) {
for (const [, hash] of hashes.bits) {
const within = hash.within(aabb);
for (const other of within) {
if (other !== entity.id) {
collisions.add(this.ecs.get(other));
}
}
}
}
else {
for (const mask of maskList) {
if (!hashes.bits.get(mask)) {
continue;
}
const within = hashes.bits.get(mask).within(aabb);
for (const other of within) {
if (other !== entity.id) {
collisions.add(this.ecs.get(other));
}
}
}
}
}
for (const bits of bitsList) {
if (!hashes.mask.get(bits)) {
continue;
}
const within = hashes.mask.get(bits).within(aabb);
for (const other of within) {
if (other !== entity.id) {
collisions.add(this.ecs.get(other));
}
}
}
}
}
}
return collisions;
}
deindex(entities) {
super.deindex(entities);
for (const id of entities) {
this.hash.remove(id);
for (const [, hashes] of this.groups) {
for (const id of entities) {
hashes.group.remove(id);
for (const map of [hashes.bits, hashes.mask]) {
for (const [, hash] of map) {
hash.remove(id);
}
}
}
}
}
@ -22,22 +113,61 @@ export default class MaintainColliderHash extends System {
for (const id of entities) {
if (1 === id) {
const {AreaSize} = this.ecs.get(1);
if (AreaSize) {
this.hash = new SpatialHash(AreaSize);
}
this.AreaSize = AreaSize;
this.groups.clear();
}
}
super.reindex(entities);
for (const id of entities) {
this.updateHash(this.ecs.get(id));
this.updateHashes(this.ecs.get(id));
}
}
updateHash(entity) {
if (!entity.Collider || !this.hash) {
updateHashes(entity) {
if (!entity.Collider || !this.AreaSize) {
return;
}
this.hash.update(entity.Collider.aabb, entity.id);
const {bodies} = entity.Collider;
for (let {bits, group, mask} of bodies) {
if (!this.groups.has(group)) {
this.groups.set(group, {
bits: new Map(),
group: new SpatialHash(this.AreaSize),
mask: new Map(),
});
}
this.groups.get(group).group.update(entity.Collider.aabb, entity.id);
let bit = 0;
while (bits > 0) {
if (bits & 1) {
if (!this.groups.get(group).bits.has(bit)) {
this.groups.get(group).bits.set(bit, new SpatialHash(this.AreaSize));
}
this.groups.get(group).bits.get(bit).update(entity.Collider.aabb, entity.id);
}
bit += 1;
bits >>= 1;
}
if (mask !== 4294967295) {
bit = 0;
while (mask > 0) {
if (mask & 1) {
if (!this.groups.get(group).mask.has(bit)) {
this.groups.get(group).mask.set(bit, new SpatialHash(this.AreaSize));
}
this.groups.get(group).mask.get(bit).update(entity.Collider.aabb, entity.id);
}
bit += 1;
mask >>= 1;
}
}
else {
if (!this.groups.get(group).mask.has(-1)) {
this.groups.get(group).mask.set(-1, new SpatialHash(this.AreaSize));
}
this.groups.get(group).mask.get(-1).update(entity.Collider.aabb, entity.id);
}
}
}
tick() {
@ -48,14 +178,14 @@ export default class MaintainColliderHash extends System {
entity.Collider.updateAabbs();
}
for (const entity of this.ecs.changed(['Position'])) {
this.updateHash(entity);
this.updateHashes(entity);
}
}
within(query) {
const within = new Set();
if (this.hash) {
for (const id of this.hash.within(query)) {
for (const [, hashes] of this.groups) {
for (const id of hashes.group.within(query)) {
within.add(this.ecs.get(id));
}
}