Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 26 additions & 16 deletions src/collision.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ static inline bool Collision_matchesTarget(DataWin* dataWin, Instance* inst, int
return VM_isObjectOrDescendant(dataWin, inst->objectIndex, target);
}

typedef struct {
GMLReal left, right, top, bottom;
bool valid;
} InstanceBBox;

// Returns the collision sprite for an instance (mask sprite if set, else display sprite)
static inline Sprite* Collision_getSprite(DataWin* dataWin, Instance* inst) {
int32_t sprIdx = (inst->maskIndex >= 0) ? inst->maskIndex : inst->spriteIndex;
Expand All @@ -32,8 +27,15 @@ static inline Sprite* Collision_getSprite(DataWin* dataWin, Instance* inst) {

// Computes the axis-aligned bounding box for an instance using its collision sprite
static inline InstanceBBox Collision_computeBBox(Runner* runner, Instance* inst) {
// Fast path: return cached AABB when nothing bbox-affecting has changed.
if (inst->bboxCacheValid) return inst->cachedBBox;

Sprite* spr = Collision_getSprite(runner->dataWin, inst);
if (spr == nullptr) return (InstanceBBox){0, 0, 0, 0, false};
if (spr == nullptr) {
inst->cachedBBox = (InstanceBBox){0, 0, 0, 0, false};
inst->bboxCacheValid = true;
return inst->cachedBBox;
}

GMLReal marginL = (GMLReal) spr->marginLeft;
GMLReal marginR = (GMLReal) (spr->marginRight + 1);
Expand All @@ -42,6 +44,7 @@ static inline InstanceBBox Collision_computeBBox(Runner* runner, Instance* inst)
GMLReal originX = (GMLReal) spr->originX;
GMLReal originY = (GMLReal) spr->originY;

InstanceBBox result;
GMLReal left, right, top, bottom;
if (GMLReal_fabs(inst->imageAngle) > 0.0001) {
// Compute rotated AABB: transform the 4 corners of the unrotated bbox
Expand All @@ -62,13 +65,10 @@ static inline InstanceBBox Collision_computeBBox(Runner* runner, Instance* inst)
cx[2] = cs * lx0 + sn * ly1; cy[2] = -sn * lx0 + cs * ly1;
cx[3] = cs * lx1 + sn * ly1; cy[3] = -sn * lx1 + cs * ly1;

GMLReal minX = cx[0], maxX = cx[0], minY = cy[0], maxY = cy[0];
for (int c = 1; 4 > c; c++) {
if (minX > cx[c]) minX = cx[c];
if (cx[c] > maxX) maxX = cx[c];
if (minY > cy[c]) minY = cy[c];
if (cy[c] > maxY) maxY = cy[c];
}
GMLReal minX = GMLReal_fmin(GMLReal_fmin(cx[0], cx[1]), GMLReal_fmin(cx[2], cx[3]));
GMLReal maxX = GMLReal_fmax(GMLReal_fmax(cx[0], cx[1]), GMLReal_fmax(cx[2], cx[3]));
GMLReal minY = GMLReal_fmin(GMLReal_fmin(cy[0], cy[1]), GMLReal_fmin(cy[2], cy[3]));
GMLReal maxY = GMLReal_fmax(GMLReal_fmax(cy[0], cy[1]), GMLReal_fmax(cy[2], cy[3]));

left = inst->x + minX;
right = inst->x + maxX;
Expand All @@ -82,8 +82,14 @@ static inline InstanceBBox Collision_computeBBox(Runner* runner, Instance* inst)
bottom = inst->y + inst->imageYscale * (marginB - originY);

// Normalize if negative scale
if (left > right) { GMLReal tmp = left; left = right; right = tmp; }
if (top > bottom) { GMLReal tmp = top; top = bottom; bottom = tmp; }
GMLReal tmp_left = GMLReal_fmin(left, right);
GMLReal tmp_right = GMLReal_fmax(left, right);
GMLReal tmp_top = GMLReal_fmin(top, bottom);
GMLReal tmp_bottom = GMLReal_fmax(top, bottom);
left = tmp_left;
right = tmp_right;
top = tmp_top;
bottom = tmp_bottom;
}

if (runner->collisionCompatibilityMode) {
Expand All @@ -93,7 +99,11 @@ static inline InstanceBBox Collision_computeBBox(Runner* runner, Instance* inst)
bottom = GMLReal_bankersRound(bottom);
}

return (InstanceBBox){left, right, top, bottom, true};
result = (InstanceBBox){left, right, top, bottom, true};

inst->cachedBBox = result;
inst->bboxCacheValid = true;
return result;
}

static inline bool Collision_hasFrameMasks(Sprite* sprite) {
Expand Down
7 changes: 7 additions & 0 deletions src/collision_types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once
#include "real_type.h"

typedef struct {
GMLReal left, right, top, bottom;
bool valid;
} InstanceBBox;
1 change: 1 addition & 0 deletions src/instance.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Instance* Instance_create(uint32_t instanceId, int32_t objectIndex, GMLReal x, G
inst->gravityDirection = 270.0f;
inst->pathIndex = -1;
inst->pathScale = 1.0f;
inst->bboxCacheValid = false;
inst->timelineIndex = -1;
inst->timelinePosition = 0.0f;
inst->timelineSpeed = 1.0f;
Expand Down
5 changes: 5 additions & 0 deletions src/instance.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "common.h"
#include "collision_types.h"
#include <stdint.h>
#include "rvalue.h"
#include "gml_array.h"
Expand Down Expand Up @@ -62,6 +63,10 @@ struct Instance {
float pathXStart; // origin for relative paths
float pathYStart;

// AABB cache for fast path reuse
InstanceBBox cachedBBox;
bool bboxCacheValid;

int32_t alarm[GML_ALARM_COUNT];

// Timeline following state
Expand Down
2 changes: 2 additions & 0 deletions src/spatial_grid.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ void SpatialGrid_markInstanceAsDirty(SpatialGrid* grid, Instance* dirtyInstance)
return;
}

dirtyInstance->bboxCacheValid = false;

if (dirtyInstance->spatialGridDirty)
return;

Expand Down
Loading