Revamped collision system

This commit is contained in:
Jan Racek
2026-03-27 16:39:10 +01:00
parent 090402f71a
commit 480323f5b9
11 changed files with 278 additions and 252 deletions

View File

@@ -178,7 +178,6 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData, LoadingSc
SPLASHPACKCollider* collider = sceneSetup.colliders[i];
if (collider == nullptr) continue;
// Convert fixed-point values from binary format to AABB
AABB bounds;
bounds.min.x.value = collider->minX;
bounds.min.y.value = collider->minY;
@@ -187,10 +186,8 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData, LoadingSc
bounds.max.y.value = collider->maxY;
bounds.max.z.value = collider->maxZ;
// Convert collision type
CollisionType type = static_cast<CollisionType>(collider->collisionType);
// Register with collision system
m_collisionSystem.registerCollider(
collider->gameObjectIndex,
bounds,
@@ -199,6 +196,22 @@ void psxsplash::SceneManager::InitializeScene(uint8_t* splashpackData, LoadingSc
);
}
// Register trigger boxes from splashpack data
for (size_t i = 0; i < sceneSetup.triggerBoxes.size(); i++) {
SPLASHPACKTriggerBox* tb = sceneSetup.triggerBoxes[i];
if (tb == nullptr) continue;
AABB bounds;
bounds.min.x.value = tb->minX;
bounds.min.y.value = tb->minY;
bounds.min.z.value = tb->minZ;
bounds.max.x.value = tb->maxX;
bounds.max.y.value = tb->maxY;
bounds.max.z.value = tb->maxZ;
m_collisionSystem.registerTriggerBox(bounds, tb->luaFileIndex);
}
// Load Lua files - order is important here. We need
// to load the Lua files before we register the game objects,
// as the game objects may reference Lua files by index.
@@ -289,21 +302,44 @@ void psxsplash::SceneManager::GameTick(psyqo::GPU &gpu) {
// Collision detection
uint32_t collisionStart = gpu.now();
int collisionCount = m_collisionSystem.detectCollisions();
// Process solid collisions - call OnCollision on BOTH objects
const CollisionResult* results = m_collisionSystem.getResults();
for (int i = 0; i < collisionCount; i++) {
auto* objA = getGameObject(results[i].objectA);
auto* objB = getGameObject(results[i].objectB);
if (objA && objB) {
L.OnCollision(objA, objB);
L.OnCollision(objB, objA); // Call on both objects
// Build player AABB from position + radius/height
AABB playerAABB;
{
psyqo::FixedPoint<12> r;
r.value = m_playerRadius;
psyqo::FixedPoint<12> px = static_cast<psyqo::FixedPoint<12>>(m_playerPosition.x);
psyqo::FixedPoint<12> py = static_cast<psyqo::FixedPoint<12>>(m_playerPosition.y);
psyqo::FixedPoint<12> pz = static_cast<psyqo::FixedPoint<12>>(m_playerPosition.z);
psyqo::FixedPoint<12> h = static_cast<psyqo::FixedPoint<12>>(m_playerHeight);
playerAABB.min = psyqo::Vec3{px - r, py - h, pz - r};
playerAABB.max = psyqo::Vec3{px + r, py, pz + r};
}
psyqo::Vec3 pushBack;
int collisionCount = m_collisionSystem.detectCollisions(playerAABB, pushBack);
// Apply push-back to player position
{
psyqo::FixedPoint<12> zero;
if (pushBack.x != zero || pushBack.z != zero) {
m_playerPosition.x = m_playerPosition.x + pushBack.x;
m_playerPosition.z = m_playerPosition.z + pushBack.z;
}
}
// Process trigger events (enter/stay/exit)
m_collisionSystem.processTriggerEvents(*this);
// Fire onCollideWithPlayer Lua events on collided objects
const CollisionResult* results = m_collisionSystem.getResults();
for (int i = 0; i < collisionCount; i++) {
if (results[i].objectA != 0xFFFF) continue;
auto* obj = getGameObject(results[i].objectB);
if (obj) {
L.OnCollideWithPlayer(obj);
}
}
// Process trigger boxes (enter/exit)
m_collisionSystem.detectTriggers(playerAABB, *this);
gpu.pumpCallbacks();
uint32_t collisionEnd = gpu.now();
@@ -489,29 +525,14 @@ void psxsplash::SceneManager::GameTick(psyqo::GPU &gpu) {
processPendingSceneLoad();
}
// Trigger event callbacks
void psxsplash::SceneManager::fireTriggerEnter(uint16_t triggerObjIdx, uint16_t otherObjIdx) {
auto* trigger = getGameObject(triggerObjIdx);
auto* other = getGameObject(otherObjIdx);
if (trigger && other) {
L.OnTriggerEnter(trigger, other);
}
void psxsplash::SceneManager::fireTriggerEnter(int16_t luaFileIndex, uint16_t triggerIndex) {
if (luaFileIndex < 0) return;
L.OnTriggerEnterScript(luaFileIndex, triggerIndex);
}
void psxsplash::SceneManager::fireTriggerStay(uint16_t triggerObjIdx, uint16_t otherObjIdx) {
auto* trigger = getGameObject(triggerObjIdx);
auto* other = getGameObject(otherObjIdx);
if (trigger && other) {
L.OnTriggerStay(trigger, other);
}
}
void psxsplash::SceneManager::fireTriggerExit(uint16_t triggerObjIdx, uint16_t otherObjIdx) {
auto* trigger = getGameObject(triggerObjIdx);
auto* other = getGameObject(otherObjIdx);
if (trigger && other) {
L.OnTriggerExit(trigger, other);
}
void psxsplash::SceneManager::fireTriggerExit(int16_t luaFileIndex, uint16_t triggerIndex) {
if (luaFileIndex < 0) return;
L.OnTriggerExitScript(luaFileIndex, triggerIndex);
}
// ============================================================================
@@ -746,8 +767,12 @@ void psxsplash::SceneManager::shrinkBuffer() {
if (m_liveDataSize == 0 || m_currentSceneData == nullptr) return;
uint8_t* oldBase = m_currentSceneData;
uint8_t* newBase = new uint8_t[m_liveDataSize];
if (!newBase) return;
// Allocate the shrunk buffer. The volatile cast prevents the compiler
// from assuming operator new never returns NULL (it does with
// -fno-exceptions), which would let it optimize away the null check.
uint8_t* volatile newBaseV = new uint8_t[m_liveDataSize];
uint8_t* newBase = newBaseV;
if (!newBase) return; // Heap exhausted — keep the full buffer
__builtin_memcpy(newBase, oldBase, m_liveDataSize);
intptr_t delta = reinterpret_cast<intptr_t>(newBase) - reinterpret_cast<intptr_t>(oldBase);