reduce array allocations when doing collision

This commit is contained in:
RandomityGuy 2026-03-27 17:34:32 +00:00
parent 95e75a220e
commit a6ae9259a1
7 changed files with 44 additions and 87 deletions

View file

@ -260,7 +260,8 @@ class Marble extends GameObject {
public var contacts:Array<CollisionInfo> = []; public var contacts:Array<CollisionInfo> = [];
public var bestContact:CollisionInfo; public var bestContact:CollisionInfo;
public var contactEntities:Array<CollisionEntity> = [];
static var contactScratch:Array<CollisionEntity> = [];
var queuedContacts:Array<CollisionInfo> = []; var queuedContacts:Array<CollisionInfo> = [];
var appliedImpulses:Array<{impulse:Vector, contactImpulse:Bool}> = []; var appliedImpulses:Array<{impulse:Vector, contactImpulse:Bool}> = [];
@ -700,9 +701,7 @@ class Marble extends GameObject {
function findContacts(collisiomWorld:CollisionWorld, timeState:TimeState) { function findContacts(collisiomWorld:CollisionWorld, timeState:TimeState) {
this.contacts = queuedContacts; this.contacts = queuedContacts;
CollisionPool.clear(); CollisionPool.clear();
var c = collisiomWorld.sphereIntersection(this.collider, timeState); collisiomWorld.sphereIntersection(this.collider, timeState, this.contacts);
this.contactEntities = c.foundEntities;
contacts = contacts.concat(c.contacts);
} }
public function queueCollision(collisionInfo:CollisionInfo) { public function queueCollision(collisionInfo:CollisionInfo) {
@ -1274,7 +1273,10 @@ class Marble extends GameObject {
searchbox.addSpherePos(position.x, position.y, position.z, _radius); searchbox.addSpherePos(position.x, position.y, position.z, _radius);
searchbox.addSpherePos(position.x + velocity.x * deltaT, position.y + velocity.y * deltaT, position.z + velocity.z * deltaT, _radius); searchbox.addSpherePos(position.x + velocity.x * deltaT, position.y + velocity.y * deltaT, position.z + velocity.z * deltaT, _radius);
var foundObjs = this.collisionWorld.boundingSearch(searchbox); contactScratch.resize(0);
this.collisionWorld.boundingSearch(searchbox, contactScratch);
var foundObjs = contactScratch;
var finalT = deltaT; var finalT = deltaT;
var found = false; var found = false;
@ -1904,7 +1906,7 @@ class Marble extends GameObject {
} }
// } // }
} }
this.queuedContacts = []; this.queuedContacts.resize(0);
newPos = this.collider.transform.getPosition(); // this.getAbsPos().getPosition().clone(); newPos = this.collider.transform.getPosition(); // this.getAbsPos().getPosition().clone();
@ -1983,7 +1985,9 @@ class Marble extends GameObject {
// marbleHitbox.offset(end.x, end.y, end.z); // marbleHitbox.offset(end.x, end.y, end.z);
// spherebounds.addSpherePos(gjkCapsule.p2.x, gjkCapsule.p2.y, gjkCapsule.p2.z, gjkCapsule.radius); // spherebounds.addSpherePos(gjkCapsule.p2.x, gjkCapsule.p2.y, gjkCapsule.p2.z, gjkCapsule.radius);
var contacts = this.collisionWorld.boundingSearch(box); contactScratch.resize(0);
this.collisionWorld.boundingSearch(box, contactScratch);
var contacts = contactScratch;
// var contacts = marble.contactEntities; // var contacts = marble.contactEntities;
var inside = []; var inside = [];
@ -2036,7 +2040,9 @@ class Marble extends GameObject {
var checkSphereRadius = checkBounds.getMax().sub(checkBoundsCenter).length(); var checkSphereRadius = checkBounds.getMax().sub(checkBoundsCenter).length();
var checkSphere = new Bounds(); var checkSphere = new Bounds();
checkSphere.addSpherePos(checkBoundsCenter.x, checkBoundsCenter.y, checkBoundsCenter.z, checkSphereRadius); checkSphere.addSpherePos(checkBoundsCenter.x, checkBoundsCenter.y, checkBoundsCenter.z, checkSphereRadius);
var endpadBB = this.collisionWorld.boundingSearch(checkSphere, false); contactScratch.resize(0);
this.collisionWorld.boundingSearch(checkSphere, contactScratch, false);
var endpadBB = contactScratch;
var found = false; var found = false;
for (collider in endpadBB) { for (collider in endpadBB) {
if (collider.go == @:privateAccess this.level.endPad) { if (collider.go == @:privateAccess this.level.endPad) {
@ -2820,7 +2826,6 @@ class Marble extends GameObject {
this.megaMarbleUseTick = 0; this.megaMarbleUseTick = 0;
this.netFlags = MarbleNetFlags.DoBlast | MarbleNetFlags.DoMega | MarbleNetFlags.DoHelicopter | MarbleNetFlags.DoShockAbsorber | MarbleNetFlags.DoSuperBounce | MarbleNetFlags.PickupPowerup | MarbleNetFlags.GravityChange | MarbleNetFlags.UsePowerup; this.netFlags = MarbleNetFlags.DoBlast | MarbleNetFlags.DoMega | MarbleNetFlags.DoHelicopter | MarbleNetFlags.DoShockAbsorber | MarbleNetFlags.DoSuperBounce | MarbleNetFlags.PickupPowerup | MarbleNetFlags.GravityChange | MarbleNetFlags.UsePowerup;
this.lastContactNormal = new Vector(0, 0, 1); this.lastContactNormal = new Vector(0, 0, 1);
this.contactEntities = [];
this.cloak = false; this.cloak = false;
this._firstTick = true; this._firstTick = true;
this.lastRespawnTick = -100000; this.lastRespawnTick = -100000;

View file

@ -56,7 +56,5 @@ class BoxCollisionEntity extends CollisionEntity implements IBVHObject {
return Math.POSITIVE_INFINITY; return Math.POSITIVE_INFINITY;
} }
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState) { public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState, contacts:Array<CollisionInfo>) {}
return [];
}
} }

View file

@ -200,7 +200,7 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
this.priority = priority; this.priority = priority;
} }
public function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState) { public function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState, contacts:Array<CollisionInfo>) {
var position = collisionEntity.transform.getPosition(); var position = collisionEntity.transform.getPosition();
var radius = collisionEntity.radius + 0.001; var radius = collisionEntity.radius + 0.001;
@ -227,8 +227,6 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
invtform.load(Matrix.I()); invtform.load(Matrix.I());
} }
var contacts = [];
for (obj in surfaces) { for (obj in surfaces) {
var surface:CollisionSurface = cast obj; var surface:CollisionSurface = cast obj;
@ -301,7 +299,5 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
// if (surfaceBestContact != null) // if (surfaceBestContact != null)
// contacts.push(surfaceBestContact); // contacts.push(surfaceBestContact);
} }
return contacts;
} }
} }

View file

@ -18,7 +18,7 @@ class CollisionHull extends CollisionEntity {
super(go); super(go);
} }
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState):Array<CollisionInfo> { public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState, contacts:Array<CollisionInfo>) {
var bbox = this.boundingBox; var bbox = this.boundingBox;
var box = new Bounds(); var box = new Bounds();
var pos = collisionEntity.transform.getPosition(); var pos = collisionEntity.transform.getPosition();
@ -51,10 +51,9 @@ class CollisionHull extends CollisionEntity {
cinfo.friction = friction; cinfo.friction = friction;
cinfo.force = force; cinfo.force = force;
this.go.onMarbleContact(collisionEntity.marble, timeState, cinfo); this.go.onMarbleContact(collisionEntity.marble, timeState, cinfo);
return [cinfo]; contacts.push(cinfo);
} }
} }
return [];
} }
public override function addSurface(surface:CollisionSurface) { public override function addSurface(surface:CollisionSurface) {

View file

@ -38,11 +38,12 @@ class CollisionWorld {
this.dynamicGrid.build(); this.dynamicGrid.build();
} }
public function sphereIntersection(spherecollision:SphereCollisionEntity, timeState:TimeState):SphereIntersectionResult { var contactList:Array<CollisionInfo> = [];
var intersectionList:Array<CollisionEntity> = [];
public function sphereIntersection(spherecollision:SphereCollisionEntity, timeState:TimeState, contacts:Array<CollisionInfo>) {
var position = spherecollision.transform.getPosition(); var position = spherecollision.transform.getPosition();
var radius = spherecollision.radius; var radius = spherecollision.radius;
// var velocity = spherecollision.velocity;
// var intersections = this.octree.radiusSearch(position, searchdist);
var box = new Bounds(); var box = new Bounds();
box.addSpherePos(0, 0, 0, radius); box.addSpherePos(0, 0, 0, radius);
@ -50,60 +51,24 @@ class CollisionWorld {
box.transform(rotQuat.toMatrix()); box.transform(rotQuat.toMatrix());
box.offset(position.x, position.y, position.z); box.offset(position.x, position.y, position.z);
// box.addSpherePos(position.x + velocity.x * timeState.dt, position.y + velocity.y * timeState.dt, position.z + velocity.z * timeState.dt, radius); // box.addSpherePos(position.x + velocity.x * timeState.dt, position.y + velocity.y * timeState.dt, position.z + velocity.z * timeState.dt, radius);
var intersections = this.grid.boundingSearch(box); this.intersectionList.resize(0);
this.grid.boundingSearch(box, this.intersectionList);
dynamicGrid.boundingSearch(box, this.intersectionList);
// var intersections = this.rtree.search([box.xMin, box.yMax, box.zMin], [box.xSize, box.ySize, box.zSize]); for (obj in this.intersectionList) {
var contacts = [];
var foundEntities = [];
for (obj in intersections) {
var entity:CollisionEntity = cast obj;
foundEntities.push(entity);
if (entity.go.isCollideable) {
contacts = contacts.concat(entity.sphereIntersection(spherecollision, timeState));
}
}
// if (marbleEntities.length > 1) {
// marbleSap.recompute();
// var sapCollisions = marbleSap.getIntersections(spherecollision);
// for (obj in sapCollisions) {
// if (obj.go.isCollideable) {
// contacts = contacts.concat(obj.sphereIntersection(spherecollision, timeState));
// }
// }
// }
// contacts = contacts.concat(this.staticWorld.sphereIntersection(spherecollision, timeState));
var dynSearch = dynamicGrid.boundingSearch(box);
for (obj in dynSearch) {
if (obj != spherecollision) { if (obj != spherecollision) {
var col = cast(obj, CollisionEntity); var entity = obj;
if (col.boundingBox.collide(box) && col.go.isCollideable)
contacts = contacts.concat(col.sphereIntersection(spherecollision, timeState)); if (obj.boundingBox.collide(box) && entity.go.isCollideable) {
entity.sphereIntersection(spherecollision, timeState, contacts);
}
} }
} }
// for (marb in marbleEntities) {
// if (marb != spherecollision) {
// if (spherecollision.go.isCollideable) {
// var isecs = marb.sphereIntersection(spherecollision, timeState);
// if (isecs.length > 0)
// foundEntities.push(marb);
// contacts = contacts.concat(isecs);
// }
// }
// }
return {foundEntities: foundEntities, contacts: contacts};
} }
public function boundingSearch(bounds:Bounds, useCache:Bool = true) { public function boundingSearch(bounds:Bounds, contacts:Array<CollisionEntity>, useCache:Bool = true) {
var contacts = this.grid.boundingSearch(bounds).map(x -> cast(x, CollisionEntity)); this.grid.boundingSearch(bounds, contacts);
contacts = contacts.concat(dynamicGrid.boundingSearch(bounds).map(x -> cast(x, CollisionEntity))); dynamicGrid.boundingSearch(bounds, contacts);
return contacts;
} }
public function rayCast(rayStart:Vector, rayDirection:Vector, rayLength:Float) { public function rayCast(rayStart:Vector, rayDirection:Vector, rayLength:Float) {
@ -116,19 +81,17 @@ class CollisionWorld {
+ rayDirection.x * rayLength, rayStart.y + rayDirection.x * rayLength, rayStart.y
+ rayDirection.y * rayLength, rayStart.z + rayDirection.y * rayLength, rayStart.z
+ rayDirection.z * rayLength); + rayDirection.z * rayLength);
var objs = this.grid.boundingSearch(bounds); this.intersectionList.splice(0, this.intersectionList.length);
var dynObjs = dynamicGrid.boundingSearch(bounds);
this.grid.boundingSearch(bounds, this.intersectionList);
dynamicGrid.boundingSearch(bounds, this.intersectionList);
var results = []; var results = [];
for (obj in objs) { for (obj in this.intersectionList) {
var oo = cast(obj, CollisionEntity); var oo = obj;
oo.rayCast(rayStart, rayDirection, results, rayLength); oo.rayCast(rayStart, rayDirection, results, rayLength);
} }
for (obj in dynObjs) {
var oo = cast(obj, CollisionEntity);
oo.rayCast(rayStart, rayDirection, results, rayLength);
}
// results = results.concat(this.staticWorld.rayCast(rayStart, rayDirection));
return results; return results;
} }

View file

@ -221,7 +221,7 @@ class GridBroadphase {
} }
// searchbox should be in LOCAL coordinates // searchbox should be in LOCAL coordinates
public function boundingSearch(searchbox:Bounds) { public function boundingSearch(searchbox:Bounds, foundSurfaces:Array<CollisionEntity>) {
var queryMinX = Math.max(searchbox.xMin, bounds.xMin); var queryMinX = Math.max(searchbox.xMin, bounds.xMin);
var queryMinY = Math.max(searchbox.yMin, bounds.yMin); var queryMinY = Math.max(searchbox.yMin, bounds.yMin);
var queryMaxX = Math.min(searchbox.xMax, bounds.xMax); var queryMaxX = Math.min(searchbox.xMax, bounds.xMax);
@ -240,8 +240,6 @@ class GridBroadphase {
if (yEnd > CELL_SIZE) if (yEnd > CELL_SIZE)
yEnd = CELL_SIZE; yEnd = CELL_SIZE;
var foundSurfaces = [];
searchKey++; searchKey++;
// Insert the surface references from [xStart, yStart, zStart] to [xEnd, yEnd, zEnd] into the map // Insert the surface references from [xStart, yStart, zStart] to [xEnd, yEnd, zEnd] into the map

View file

@ -75,10 +75,9 @@ class SphereCollisionEntity extends CollisionEntity {
return Math.POSITIVE_INFINITY; return Math.POSITIVE_INFINITY;
} }
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState) { public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState, contacts:Array<CollisionInfo>) {
if (ignore) if (ignore)
return []; return;
var contacts = [];
var thispos = transform.getPosition(); var thispos = transform.getPosition();
var position = collisionEntity.transform.getPosition(); var position = collisionEntity.transform.getPosition();
var velocity = collisionEntity.velocity; var velocity = collisionEntity.velocity;
@ -113,6 +112,5 @@ class SphereCollisionEntity extends CollisionEntity {
// othercontact.penetration = this.radius - (thispos.sub(othercontact.point).dot(othercontact.normal)); // othercontact.penetration = this.radius - (thispos.sub(othercontact.point).dot(othercontact.normal));
// this.marble.queueCollision(othercontact); // this.marble.queueCollision(othercontact);
} }
return contacts;
} }
} }