Work on raycasting in rooms

This commit is contained in:
James Lambert 2022-05-23 13:18:07 -06:00
parent 0b718811ec
commit 40ae68e455
9 changed files with 154 additions and 39 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -142,6 +142,8 @@ void rigidBodyCheckPortals(struct RigidBody* rigidBody) {
rigidBody->transform.rotation = newRotation;
newFlags |= RigidBodyFlagsCrossedPortal0 << i;
rigidBody->currentRoom = gCollisionScene.portalRooms[1 - i];
}
rigidBody->flags &= ~(

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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