diff --git a/src/physics/collision_scene.c b/src/physics/collision_scene.c index 28b3f80..6bd8918 100644 --- a/src/physics/collision_scene.c +++ b/src/physics/collision_scene.c @@ -56,6 +56,11 @@ int mergeColliderList(short* a, int aCount, short* b, int bCount, short* output) #define COLLISION_GRID_CELL_SIZE 4 +#define GRID_CELL_X(room, worldX) floorf(((worldX) - room->cornerX) * (1.0f / COLLISION_GRID_CELL_SIZE)); +#define GRID_CELL_Z(room, worldZ) floorf(((worldZ) - room->cornerZ) * (1.0f / COLLISION_GRID_CELL_SIZE)); + +#define GRID_CELL_CONTENTS(room, x, z) (&room->cellContents[(x) * room->spanX + (z)]) + int collisionObjectRoomColliders(struct Room* room, struct Box3D* box, short output[MAX_COLLIDERS]) { short tmp[MAX_COLLIDERS]; @@ -63,15 +68,15 @@ int collisionObjectRoomColliders(struct Room* room, struct Box3D* box, short out short* currentResult = output; int result = 0; - int minX = floorf((box->min.x - room->cornerX) * (1.0f / COLLISION_GRID_CELL_SIZE)); - int maxX = floorf((box->max.x - room->cornerX) * (1.0f / COLLISION_GRID_CELL_SIZE)); + int minX = GRID_CELL_X(room, box->min.x); + int maxX = GRID_CELL_Z(room, box->min.z); - int minZ = floorf((box->min.z - room->cornerZ) * (1.0f / COLLISION_GRID_CELL_SIZE)); - int maxZ = floorf((box->max.z - room->cornerZ) * (1.0f / COLLISION_GRID_CELL_SIZE)); + int minZ = GRID_CELL_X(room, box->max.x); + int maxZ = GRID_CELL_Z(room, box->max.z); for (int x = MAX(minX, 0); x <= maxX && x < room->spanX; ++x) { for (int z = MAX(minZ, 0); z <= maxZ && z < room->spanZ; ++z) { - struct Rangeu16* range = &room->cellContents[x * room->spanX + z]; + struct Rangeu16* range = GRID_CELL_CONTENTS(room, x, z); result = mergeColliderList(currentSource, result, &room->quadIndices[range->min], range->max - range->min, currentResult); @@ -99,10 +104,6 @@ void collisionObjectCollideWithScene(struct CollisionObject* object, struct Coll for (int i = 0; i < quadCount; ++i) { collisionObjectCollideWithQuad(object, &scene->quads[colliderIndices[i]], contactSolver); } - - // for (int i = 0; i < scene->quadCount; ++i) { - // collisionObjectCollideWithQuad(object, &scene->quads[i], contactSolver); - // } } int collisionSceneFilterPortalContacts(struct ContactConstraintState* contact) { @@ -132,12 +133,15 @@ void collisionObjectQueryScene(struct CollisionObject* object, struct CollisionS return; } + short colliderIndices[MAX_COLLIDERS]; + int quadCount = collisionObjectRoomColliders(&scene->world->rooms[object->body->currentRoom], &object->boundingBox, colliderIndices); + struct ContactConstraintState localContact; - for (int i = 0; i < scene->quadCount; ++i) { + for (int i = 0; i < quadCount; ++i) { localContact.contactCount = 0; - if (quadCollider(object->collider->data, &object->body->transform, scene->quads[i].collider->data, &localContact) && + if (quadCollider(object->collider->data, &object->body->transform, scene->quads[colliderIndices[i]].collider->data, &localContact) && collisionSceneFilterPortalContacts(&localContact)) { callback(data, &localContact); } @@ -176,21 +180,111 @@ int collisionSceneIsPortalOpen() { return gCollisionScene.portalTransforms[0] != NULL && gCollisionScene.portalTransforms[1] != NULL; } -int collisionSceneRaycast(struct CollisionScene* scene, struct Ray* ray, float maxDistance, int passThroughPortals, struct RaycastHit* hit) { - hit->distance = maxDistance; - hit->throughPortal = NULL; - - for (int i = 0; i < scene->quadCount; ++i) { +void collisionSceneRaycastRoom(struct CollisionScene* scene, struct Room* room, struct Ray* ray, struct RaycastHit* hit) { + int currX = GRID_CELL_X(room, ray->origin.x); + int currZ = GRID_CELL_Z(room, ray->origin.z); + + float xDirInv = fabsf(ray->dir.x) > 0.00001f ? 1.0f / ray->dir.x : 0.0f; + float zDirInv = fabsf(ray->dir.z) > 0.00001f ? 1.0f / ray->dir.z : 0.0f; + + // TODO adjust currX and currZ if ray starts outside room + + while (currX >=0 && currX < room->spanX && currZ >= 0 && currZ < room->spanZ) { + struct Rangeu16* range = GRID_CELL_CONTENTS(room, currX, currZ); + + for (int i = range->min; i < range->max; ++i) { + struct RaycastHit hitTest; + + if (raycastQuad(&scene->quads[room->quadIndices[i]], ray, hit->distance, &hitTest) && hitTest.distance < hit->distance) { + hit->at = hitTest.at; + hit->normal = hitTest.normal; + hit->distance = hitTest.distance; + hit->object = hitTest.object; + } + } + + int xStep = 0; + int zStep = 0; + float cellDistance = hit->distance; + + if (xDirInv != 0.0f) { + float nextEdge = (currX + (ray->dir.x > 0.0f ? 1 : 0)) * COLLISION_GRID_CELL_SIZE + room->cornerX; + float distanceCheck = (nextEdge - ray->origin.x) * xDirInv; + + if (distanceCheck < cellDistance) { + cellDistance = distanceCheck; + xStep = ray->dir.x > 0.0f ? 1 : -1; + zStep = 0; + } + } + + if (zDirInv != 0.0f) { + float nextEdge = (currZ + (ray->dir.x > 0.0f ? 1 : 0)) * COLLISION_GRID_CELL_SIZE + room->cornerZ; + float distanceCheck = (nextEdge - ray->origin.z) * zDirInv; + + if (distanceCheck < cellDistance) { + cellDistance = distanceCheck; + xStep = 0; + zStep = ray->dir.z > 0.0f ? 1 : -1; + } + } + + if (!xStep && !zStep) { + return; + } + + currX += xStep; + currZ += zStep; + } +} + +int collisionSceneRaycastDoorways(struct CollisionScene* scene, struct Room* room, struct Ray* ray, float maxDistance, int currentRoom) { + int nextRoom = -1; + + float roomDistance = maxDistance; + + for (int i = 0; i < room->doorwayCount; ++i) { struct RaycastHit hitTest; - if (raycastQuad(&scene->quads[i], ray, hit->distance, &hitTest) && hitTest.distance < hit->distance) { - hit->at = hitTest.at; - hit->normal = hitTest.normal; - hit->distance = hitTest.distance; - hit->object = hitTest.object; + struct Doorway* doorway = &scene->world->doorways[room->doorwayIndices[i]]; + + if (raycastQuadShape(&doorway->quad, ray, roomDistance, &hitTest) && hitTest.distance < roomDistance) { + roomDistance = hitTest.distance; + // check that the doorway wasn't hit from the wrong side + int expectedRoom = vector3Dot(&doorway->quad.plane.normal, &hitTest.normal) < 0.0f ? doorway->roomA : doorway->roomB; + int otherRoom = currentRoom == doorway->roomA ? doorway->roomB : doorway->roomA; + + if (expectedRoom == otherRoom) { + nextRoom = otherRoom; + } } } + return nextRoom; +} + +int collisionSceneRaycast(struct CollisionScene* scene, int roomIndex, struct Ray* ray, float maxDistance, int passThroughPortals, struct RaycastHit* hit) { + hit->distance = maxDistance; + hit->throughPortal = NULL; + + int roomsToCheck = 5; + + while (roomsToCheck && roomIndex != -1) { + struct Room* room = &scene->world->rooms[roomIndex]; + collisionSceneRaycastRoom(scene, room, ray, hit); + + if (hit->distance != maxDistance) { + hit->roomIndex = roomIndex; + break; + } + + int nextRoom = collisionSceneRaycastDoorways(scene, room, ray, hit->distance, roomIndex); + + roomIndex = nextRoom; + + --roomsToCheck; + } + for (int i = 0; i < scene->dynamicObjectCount; ++i) { struct RaycastHit hitTest; @@ -203,6 +297,7 @@ int collisionSceneRaycast(struct CollisionScene* scene, struct Ray* ray, float m hit->normal = hitTest.normal; hit->distance = hitTest.distance; hit->object = hitTest.object; + hit->roomIndex = hitTest.roomIndex; } } @@ -221,7 +316,7 @@ int collisionSceneRaycast(struct CollisionScene* scene, struct Ray* ray, float m struct RaycastHit newHit; - int result = collisionSceneRaycast(scene, &newRay, maxDistance - hit->distance, 0, &newHit); + int result = collisionSceneRaycast(scene, gCollisionScene.portalRooms[1 - i], &newRay, maxDistance - hit->distance, 0, &newHit); if (result) { newHit.distance += hit->distance; diff --git a/src/physics/collision_scene.h b/src/physics/collision_scene.h index 7341333..d59d24c 100644 --- a/src/physics/collision_scene.h +++ b/src/physics/collision_scene.h @@ -13,6 +13,7 @@ struct CollisionScene { struct CollisionObject* quads; struct World* world; + short portalRooms[2]; struct Transform* portalTransforms[2]; struct CollisionObject* dynamicObjects[MAX_DYNAMIC_OBJECTS]; u16 dynamicObjectCount; @@ -31,7 +32,7 @@ int collisionSceneIsPortalOpen(); void collisionObjectQueryScene(struct CollisionObject* object, struct CollisionScene* scene, void* data, ManifoldCallback callback); -int collisionSceneRaycast(struct CollisionScene* scene, struct Ray* ray, float maxDistance, int passThroughPortals, struct RaycastHit* hit); +int collisionSceneRaycast(struct CollisionScene* scene, int roomIndex, struct Ray* ray, float maxDistance, int passThroughPortals, struct RaycastHit* hit); void collisionSceneGetPortalTransform(int fromPortal, struct Transform* out); diff --git a/src/physics/raycasting.c b/src/physics/raycasting.c index c91af66..a7b9df9 100644 --- a/src/physics/raycasting.c +++ b/src/physics/raycasting.c @@ -7,9 +7,7 @@ #define NEAR_DOT_ZERO 0.00001f #define MIN_RAY_LENGTH 0.05f -int raycastQuad(struct CollisionObject* quadObject, struct Ray* ray, float maxDistance, struct RaycastHit* contact) { - struct CollisionQuad* quad = (struct CollisionQuad*)quadObject->collider->data; - +int raycastQuadShape(struct CollisionQuad* quad, struct Ray* ray, float maxDistance, struct RaycastHit* contact) { float normalDot = vector3Dot(&ray->dir, &quad->plane.normal); if (fabsf(normalDot) < NEAR_DOT_ZERO) { @@ -29,11 +27,22 @@ int raycastQuad(struct CollisionObject* quadObject, struct Ray* ray, float maxDi } contact->normal = quad->plane.normal; - contact->object = quadObject; return 1; } +int raycastQuad(struct CollisionObject* quadObject, struct Ray* ray, float maxDistance, struct RaycastHit* contact) { + struct CollisionQuad* quad = (struct CollisionQuad*)quadObject->collider->data; + + int result = raycastQuadShape(quad, ray, maxDistance, contact); + + if (result) { + contact->object = quadObject; + } + + return result; +} + int raycastBox(struct CollisionObject* boxObject, struct Ray* ray, float maxDistance, struct RaycastHit* contact) { struct CollisionBox* box = (struct CollisionBox*)boxObject->collider->data; @@ -102,5 +111,7 @@ int raycastBox(struct CollisionObject* boxObject, struct Ray* ray, float maxDist quatMultVector(&boxObject->body->transform.rotation, &contact->normal, &contact->normal); } + contact->roomIndex = boxObject->body->currentRoom; + return contact->distance != maxDistance; } \ No newline at end of file diff --git a/src/physics/raycasting.h b/src/physics/raycasting.h index 173f4bd..3b14ffb 100644 --- a/src/physics/raycasting.h +++ b/src/physics/raycasting.h @@ -11,8 +11,10 @@ struct RaycastHit { float distance; struct CollisionObject* object; struct Transform* throughPortal; + short roomIndex; }; +int raycastQuadShape(struct CollisionQuad* quad, struct Ray* ray, float maxDistance, struct RaycastHit* contact); int raycastQuad(struct CollisionObject* quadObject, struct Ray* ray, float maxDistance, struct RaycastHit* contact); int raycastBox(struct CollisionObject* boxObject, struct Ray* ray, float maxDistance, struct RaycastHit* contact); diff --git a/src/physics/rigid_body.c b/src/physics/rigid_body.c index c6f4783..e1c5f2f 100644 --- a/src/physics/rigid_body.c +++ b/src/physics/rigid_body.c @@ -142,6 +142,8 @@ void rigidBodyCheckPortals(struct RigidBody* rigidBody) { rigidBody->transform.rotation = newRotation; newFlags |= RigidBodyFlagsCrossedPortal0 << i; + + rigidBody->currentRoom = gCollisionScene.portalRooms[1 - i]; } rigidBody->flags &= ~( diff --git a/src/player/player.c b/src/player/player.c index 95c670d..f0ba8a4 100644 --- a/src/player/player.c +++ b/src/player/player.c @@ -89,7 +89,7 @@ void playerUpdateGrabbedObject(struct Player* player) { struct RaycastHit hit; - if (collisionSceneRaycast(&gCollisionScene, &ray, GRAB_RAYCAST_DISTANCE, 1, &hit) && hit.object->body && (hit.object->body->flags & RigidBodyFlagsGrabbable)) { + if (collisionSceneRaycast(&gCollisionScene, player->body.currentRoom, &ray, GRAB_RAYCAST_DISTANCE, 1, &hit) && hit.object->body && (hit.object->body->flags & RigidBodyFlagsGrabbable)) { player->grabbing = hit.object->body; if (hit.throughPortal) { @@ -199,7 +199,7 @@ void playerUpdate(struct Player* player, struct Transform* cameraTransform) { struct Ray ray; ray.origin = player->body.transform.position; vector3Scale(&gUp, &ray.dir, -1.0f); - if (collisionSceneRaycast(&gCollisionScene, &ray, PLAYER_HEAD_HEIGHT, 1, &hit)) { + if (collisionSceneRaycast(&gCollisionScene, player->body.currentRoom, &ray, PLAYER_HEAD_HEIGHT, 1, &hit)) { vector3AddScaled(&hit.at, &gUp, PLAYER_HEAD_HEIGHT, &player->body.transform.position); player->body.velocity.y = 0.0f; @@ -280,5 +280,7 @@ void playerUpdate(struct Player* player, struct Transform* cameraTransform) { transformPoint(transform, &gCameraOffset, &cameraTransform->position); playerUpdateGrabbedObject(player); + collisionObjectUpdateBB(&player->collisionObject); + player->body.currentRoom = worldCheckDoorwayCrossings(&gCurrentLevel->world, &player->body.transform.position, player->body.currentRoom, doorwayMask); } diff --git a/src/scene/portal.c b/src/scene/portal.c index 0d7ec06..5a7c7fd 100644 --- a/src/scene/portal.c +++ b/src/scene/portal.c @@ -5,6 +5,7 @@ #include "../graphics/graphics.h" #include "../defs.h" #include "dynamic_scene.h" +#include "../physics/collision_scene.h" #define CALC_SCREEN_SPACE(clip_space, screen_size) ((clip_space + 1.0f) * ((screen_size) / 2)) @@ -151,8 +152,8 @@ void renderPropsNext(struct RenderProps* current, struct RenderProps* next, stru next->currentDepth = current->currentDepth - 1; next->fromPortalIndex = toPortal < fromPortal ? 0 : 1; - // TODO - next->fromRoom = current->fromRoom; + // Gross + next->fromRoom = gCollisionScene.portalRooms[toPortal == gCollisionScene.portalTransforms[0] ? 0 : 1]; #if !SHOW_EXTERNAL_VIEW gSPViewport(renderState->dl++, viewport); diff --git a/src/scene/scene.c b/src/scene/scene.c index 30485b3..0c1f61c 100644 --- a/src/scene/scene.c +++ b/src/scene/scene.c @@ -146,11 +146,11 @@ void sceneCheckPortals(struct Scene* scene) { quatMultVector(&scene->player.body.transform.rotation, &gUp, &playerUp); if (controllerGetButtonDown(0, Z_TRIG)) { - sceneFirePortal(scene, &raycastRay, &playerUp, 0); + sceneFirePortal(scene, &raycastRay, &playerUp, 0, scene->player.body.currentRoom); } if (controllerGetButtonDown(0, R_TRIG)) { - sceneFirePortal(scene, &raycastRay, &playerUp, 1); + sceneFirePortal(scene, &raycastRay, &playerUp, 1, scene->player.body.currentRoom); } } @@ -174,10 +174,10 @@ void sceneUpdate(struct Scene* scene) { scene->lastFrameStart = frameStart; } -int sceneFirePortal(struct Scene* scene, struct Ray* ray, struct Vector3* playerUp, int portalIndex) { +int sceneFirePortal(struct Scene* scene, struct Ray* ray, struct Vector3* playerUp, int portalIndex, int roomIndex) { struct RaycastHit hit; - if (!collisionSceneRaycast(&gCollisionScene, ray, 1000000.0f, 0, &hit)) { + if (!collisionSceneRaycast(&gCollisionScene, roomIndex, ray, 1000000.0f, 0, &hit)) { return 0; } @@ -203,10 +203,10 @@ int sceneFirePortal(struct Scene* scene, struct Ray* ray, struct Vector3* player quatLook(&hitDirection, playerUp, &portalLocation.rotation); } - return sceneOpenPortal(scene, &portalLocation, portalIndex, quadIndex); + return sceneOpenPortal(scene, &portalLocation, portalIndex, quadIndex, hit.roomIndex); } -int sceneOpenPortal(struct Scene* scene, struct Transform* at, int portalIndex, int quadIndex) { +int sceneOpenPortal(struct Scene* scene, struct Transform* at, int portalIndex, int quadIndex, int roomIndex) { struct PortalSurfaceMapping surfaceMapping = gCurrentLevel->portalSurfaceMapping[quadIndex]; for (int i = surfaceMapping.minPortalIndex; i < surfaceMapping.maxPortalIndex; ++i) { @@ -220,6 +220,7 @@ int sceneOpenPortal(struct Scene* scene, struct Transform* at, int portalIndex, scene->portals[portalIndex].transform = *at; gCollisionScene.portalTransforms[portalIndex] = &scene->portals[portalIndex].transform; + gCollisionScene.portalRooms[portalIndex] = roomIndex; return 1; } } diff --git a/src/scene/scene.h b/src/scene/scene.h index 1d9a008..6280908 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -28,7 +28,7 @@ void sceneInit(struct Scene* scene); void sceneRender(struct Scene* scene, struct RenderState* renderState, struct GraphicsTask* task); void sceneUpdate(struct Scene* scene); -int sceneFirePortal(struct Scene* scene, struct Ray* ray, struct Vector3* playerUp, int portalIndex); -int sceneOpenPortal(struct Scene* scene, struct Transform* at, int portalIndex, int quadIndex); +int sceneFirePortal(struct Scene* scene, struct Ray* ray, struct Vector3* playerUp, int portalIndex, int roomIndex); +int sceneOpenPortal(struct Scene* scene, struct Transform* at, int portalIndex, int quadIndex, int roomIndex); #endif \ No newline at end of file