diff --git a/README.md b/README.md index 671ad5a..4ebe7f7 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,8 @@ Where `/home/james/Blender/blender-2.93.1-linux-x64` is the folder where Blender ## Current TODO list +- [ ] level transition jump +- [ ] Z buffer allocation - [ ] Release grabbed objects when line of sight is cut - [ ] Correct elevator timing - [ ] Elevator and door sounds @@ -91,6 +93,9 @@ Where `/home/james/Blender/blender-2.93.1-linux-x64` is the folder where Blender - [ ] Cube dispenser - [ ] Signage should not always be on - [ ] Camera shake +- [x] collide player with dynamic objects +- [x] Render objects intersecting portals differently +- [x] Sliding against walls - [x] It is too easy to fall through portals - [x] Change the way player standing logic works - [x] crash on level transition diff --git a/assets/models/props/door_01.blend b/assets/models/props/door_01.blend index 869e397..044eb0e 100644 Binary files a/assets/models/props/door_01.blend and b/assets/models/props/door_01.blend differ diff --git a/src/decor/decor_object.c b/src/decor/decor_object.c index 7ed69aa..dd202dd 100644 --- a/src/decor/decor_object.c +++ b/src/decor/decor_object.c @@ -82,12 +82,6 @@ void decorObjectUpdate(struct DecorObject* decorObject) { soundPlayerUpdatePosition(decorObject->playingSound, &decorObject->rigidBody.transform.position); } - if (decorObject->rigidBody.flags & (RigidBodyIsTouchingPortalA | RigidBodyWasTouchingPortalA | RigidBodyIsTouchingPortalB | RigidBodyWasTouchingPortalB)) { - dynamicSceneSetFlags(decorObject->dynamicId, DYNAMIC_SCENE_OBJECT_FLAGS_TOUCHING_PORTAL); - } else { - dynamicSceneClearFlags(decorObject->dynamicId, DYNAMIC_SCENE_OBJECT_FLAGS_TOUCHING_PORTAL); - } - if (decorObject->rigidBody.flags & RigidBodyFizzled) { if (decorObject->fizzleTime == 0.0f) { vector3Scale(&decorObject->rigidBody.velocity, &decorObject->rigidBody.velocity, 0.25f); diff --git a/src/levels/cutscene_runner.c b/src/levels/cutscene_runner.c index f1931d9..a04fb18 100644 --- a/src/levels/cutscene_runner.c +++ b/src/levels/cutscene_runner.c @@ -148,9 +148,11 @@ void cutsceneRunnerStartStep(struct CutsceneRunner* runner) { struct Transform exitInverse; transformInvert(&gCurrentLevel->locations[step->loadLevel.fromLocation].transform, &exitInverse); struct Transform relativeExit; + struct Vector3 relativeVelocity; transformConcat(&exitInverse, &gScene.player.lookTransform, &relativeExit); - levelQueueLoad(step->loadLevel.levelIndex, &relativeExit); + quatMultVector(&exitInverse.rotation, &gScene.player.body.velocity, &relativeVelocity); + levelQueueLoad(step->loadLevel.levelIndex, &relativeExit, &relativeVelocity); break; } case CutsceneStepTypeGoto: diff --git a/src/levels/levels.c b/src/levels/levels.c index a48508c..099cf20 100644 --- a/src/levels/levels.c +++ b/src/levels/levels.c @@ -7,6 +7,7 @@ #include "static_render.h" #include "cutscene_runner.h" #include "../graphics/graphics.h" +#include "../player/player.h" #include "../util/rom.h" #include "../util/memory.h" @@ -17,11 +18,13 @@ u64 gTriggeredCutscenes; int gQueuedLevel = NO_QUEUED_LEVEL; struct Transform gRelativeTransform = { - {0.0f, 0.0f, 0.0f}, + {0.0f, PLAYER_HEAD_HEIGHT, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, }; +struct Vector3 gRelativeVelocity; + int levelCount() { return LEVEL_COUNT; } @@ -102,13 +105,14 @@ void levelLoad(int index) { collisionSceneInit(&gCollisionScene, gCurrentLevel->collisionQuads, gCurrentLevel->collisionQuadCount, &gCurrentLevel->world); } -void levelQueueLoad(int index, struct Transform* relativeExitTransform) { +void levelQueueLoad(int index, struct Transform* relativeExitTransform, struct Vector3* relativeVelocity) { if (index == NEXT_LEVEL) { gQueuedLevel = gCurrentLevelIndex + 1; } else { gQueuedLevel = index; } gRelativeTransform = *relativeExitTransform; + gRelativeVelocity = *relativeVelocity; } int levelGetQueued() { @@ -119,6 +123,10 @@ struct Transform* levelRelativeTransform() { return &gRelativeTransform; } +struct Vector3* levelRelativeVelocity() { + return &gRelativeVelocity; +} + int levelMaterialCount() { return STATIC_MATERIAL_COUNT; } diff --git a/src/levels/levels.h b/src/levels/levels.h index 35aa302..3a7dc30 100644 --- a/src/levels/levels.h +++ b/src/levels/levels.h @@ -10,9 +10,10 @@ int levelCount(); void levelLoad(int index); -void levelQueueLoad(int index, struct Transform* relativeExitTransform); +void levelQueueLoad(int index, struct Transform* relativeExitTransform, struct Vector3* relativeVelocity); int levelGetQueued(); struct Transform* levelRelativeTransform(); +struct Vector3* levelRelativeVelocity(); extern struct LevelDefinition* gCurrentLevel; diff --git a/src/physics/collision_object.c b/src/physics/collision_object.c index 48cc7cf..93f07d4 100644 --- a/src/physics/collision_object.c +++ b/src/physics/collision_object.c @@ -14,6 +14,7 @@ void collisionObjectInit(struct CollisionObject* object, struct ColliderTypeData object->collisionLayers = collisionLayers; object->data = 0; object->trigger = 0; + object->manifoldIds = 0; } int collisionObjectIsActive(struct CollisionObject* object) { diff --git a/src/physics/collision_object.h b/src/physics/collision_object.h index 9edfdc0..275bd71 100644 --- a/src/physics/collision_object.h +++ b/src/physics/collision_object.h @@ -25,6 +25,7 @@ struct CollisionObject { short flags; void* data; TriggerCallback trigger; + u32 manifoldIds; }; struct SweptCollisionObject { diff --git a/src/physics/collision_scene.c b/src/physics/collision_scene.c index 347374a..b32f296 100644 --- a/src/physics/collision_scene.c +++ b/src/physics/collision_scene.c @@ -120,6 +120,21 @@ void collisionObjectCollideWithSceneSwept(struct CollisionObject* object, struct } } +void collisionObjectCollideMixed(struct CollisionObject* object, struct Vector3* objectPrevPos, struct Box3D* sweptBB, struct CollisionScene* scene, struct ContactSolver* contactSolver) { + short colliderIndices[MAX_COLLIDERS]; + int quadCount = collisionObjectRoomColliders(&scene->world->rooms[object->body->currentRoom], sweptBB, colliderIndices); + + for (int i = 0; i < quadCount; ++i) { + struct CollisionObject* quad = &scene->quads[colliderIndices[i]]; + if (quad->manifoldIds & object->manifoldIds) { + collisionObjectCollideWithQuad(object, quad, contactSolver); + } else { + collisionObjectCollideWithQuadSwept(object, objectPrevPos, sweptBB, quad, contactSolver); + } + } +} + + int collisionSceneFilterPortalContacts(struct ContactManifold* contact) { int writeIndex = 0; diff --git a/src/physics/collision_scene.h b/src/physics/collision_scene.h index 1500be4..6fa81ec 100644 --- a/src/physics/collision_scene.h +++ b/src/physics/collision_scene.h @@ -27,6 +27,7 @@ extern struct CollisionScene gCollisionScene; void collisionSceneInit(struct CollisionScene* scene, struct CollisionObject* quads, int quadCount, struct World* world); void collisionObjectCollideWithScene(struct CollisionObject* object, struct CollisionScene* scene, struct ContactSolver* contactSolver); void collisionObjectCollideWithSceneSwept(struct CollisionObject* object, struct Vector3* objectPrevPos, struct Box3D* sweptBB, struct CollisionScene* scene, struct ContactSolver* contactSolver); +void collisionObjectCollideMixed(struct CollisionObject* object, struct Vector3* objectPrevPos, struct Box3D* sweptBB, struct CollisionScene* scene, struct ContactSolver* contactSolver); int collisionSceneIsTouchingSinglePortal(struct Vector3* contactPoint, struct Vector3* contactNormal, struct Transform* portalTransform, int portalIndex); int collisionSceneIsTouchingPortal(struct Vector3* contactPoint, struct Vector3* contactNormal); diff --git a/src/physics/contact_insertion.c b/src/physics/contact_insertion.c index fd69ffa..a57b1be 100644 --- a/src/physics/contact_insertion.c +++ b/src/physics/contact_insertion.c @@ -7,6 +7,7 @@ void contactInsert(struct ContactManifold* contactState, struct EpaResult* epaResult) { int shouldReplace = 1; int replacementIndex = 0; + int idMask = 1 << (contactState - gContactSolver.contacts); float smallestOverlap = -10000.0f; int insertIndex; @@ -41,6 +42,9 @@ void contactInsert(struct ContactManifold* contactState, struct EpaResult* epaRe vector3Normalize(&contactState->tangentVectors[0], &contactState->tangentVectors[0]); vector3Cross(&contactState->normal, &contactState->tangentVectors[0], &contactState->tangentVectors[1]); + contactState->shapeA->manifoldIds |= idMask; + contactState->shapeB->manifoldIds |= idMask; + if (insertIndex == MAX_CONTACTS_PER_MANIFOLD) { if (!shouldReplace) { return; diff --git a/src/physics/contact_solver.c b/src/physics/contact_solver.c index 674c626..91c7f7c 100644 --- a/src/physics/contact_solver.c +++ b/src/physics/contact_solver.c @@ -78,6 +78,12 @@ void contactSolverCleanupManifold(struct ContactManifold* manifold) { manifold->contactCount = writeIndex; } +void contactSolverManifoldCleanup(struct ContactSolver* contactSolver, struct ContactManifold* contact) { + int idMask = ~(1 << (contact - contactSolver->contacts)); + contact->shapeA->manifoldIds &= idMask; + contact->shapeB->manifoldIds &= idMask; +} + void contactSolverRemoveUnusedContacts(struct ContactSolver* contactSolver) { struct ContactManifold* curr = contactSolver->activeContacts; struct ContactManifold* prev = NULL; @@ -86,6 +92,8 @@ void contactSolverRemoveUnusedContacts(struct ContactSolver* contactSolver) { contactSolverCleanupManifold(curr); if (curr->contactCount == 0) { + contactSolverManifoldCleanup(contactSolver, curr); + if (prev) { prev->next = curr->next; } else { @@ -136,6 +144,7 @@ void contactSolverCheckPortalContacts(struct ContactSolver* contactSolver, struc } if (curr->contactCount == 0) { + contactSolverManifoldCleanup(contactSolver, curr); if (prev) { prev->next = curr->next; } else { diff --git a/src/physics/contact_solver.h b/src/physics/contact_solver.h index 55dc205..a8c2ed4 100644 --- a/src/physics/contact_solver.h +++ b/src/physics/contact_solver.h @@ -42,7 +42,7 @@ struct ContactManifold { struct ContactManifold* next; }; -#define MAX_CONTACT_COUNT 16 +#define MAX_CONTACT_COUNT 20 struct ContactSolver { struct ContactManifold contacts[MAX_CONTACT_COUNT]; diff --git a/src/physics/mesh_collider.c b/src/physics/mesh_collider.c index d13cb0e..67c6076 100644 --- a/src/physics/mesh_collider.c +++ b/src/physics/mesh_collider.c @@ -81,7 +81,8 @@ void meshColliderCollideObject(struct CollisionObject* meshColliderObject, struc continue; } - // contactA is already in the local space for the mesh object + // contactA should be in world coordinates + transformPoint(&meshColliderObject->body->transform, &result.contactA, &result.contactA); // transform contactB to be in the localspace of the other object transformPoint(&meshColliderObject->body->transform, &result.contactB, &result.contactB); diff --git a/src/physics/rigid_body.h b/src/physics/rigid_body.h index 341057a..edb742b 100644 --- a/src/physics/rigid_body.h +++ b/src/physics/rigid_body.h @@ -23,8 +23,7 @@ enum RigidBodyFlags { RigidBodyIsKinematic = (1 << 10), RigidBodyIsSleeping = (1 << 11), - // for kinematic bodies that should generate - // contacts with other kinematic bodies + // tells the collision system to generate contacts with the player RigidBodyIsPlayer = (1 << 12), RigidBodyFizzled = (1 << 13), diff --git a/src/player/player.c b/src/player/player.c index 8acde5e..5ef1989 100644 --- a/src/player/player.c +++ b/src/player/player.c @@ -46,12 +46,13 @@ struct ColliderTypeData gPlayerColliderData = { &gCollisionCylinderCallbacks, }; -void playerInit(struct Player* player, struct Location* startLocation) { +void playerInit(struct Player* player, struct Location* startLocation, struct Vector3* velocity) { collisionObjectInit(&player->collisionObject, &gPlayerColliderData, &player->body, 1.0f, PLAYER_COLLISION_LAYERS); // rigidBodyMarkKinematic(&player->body); player->body.flags |= RigidBodyIsKinematic | RigidBodyIsPlayer; collisionSceneAddDynamicObject(&player->collisionObject); + player->body.velocity = *velocity; player->grabbingThroughPortal = PLAYER_GRABBING_THROUGH_NOTHING; player->grabbing = NULL; player->pitchVelocity = 0.0f; @@ -65,7 +66,6 @@ void playerInit(struct Player* player, struct Location* startLocation) { transformInitIdentity(&player->lookTransform); player->body.currentRoom = 0; } - player->lookTransform.position.y += PLAYER_HEAD_HEIGHT; player->body.transform = player->lookTransform; collisionObjectUpdateBB(&player->collisionObject); @@ -283,7 +283,7 @@ void playerUpdate(struct Player* player, struct Transform* cameraTransform) { collisionObjectUpdateBB(&player->collisionObject); box3DUnion(&sweptBB, &player->collisionObject.boundingBox, &sweptBB); - collisionObjectCollideWithSceneSwept(&player->collisionObject, &prevPos, &sweptBB, &gCollisionScene, &gContactSolver); + collisionObjectCollideMixed(&player->collisionObject, &prevPos, &sweptBB, &gCollisionScene, &gContactSolver); struct RaycastHit hit; struct Ray ray; diff --git a/src/player/player.h b/src/player/player.h index e3be3ab..a2df2f3 100644 --- a/src/player/player.h +++ b/src/player/player.h @@ -26,7 +26,7 @@ struct Player { enum PlayerFlags flags; }; -void playerInit(struct Player* player, struct Location* startLocation); +void playerInit(struct Player* player, struct Location* startLocation, struct Vector3* velocity); void playerUpdate(struct Player* player, struct Transform* cameraTransform); void playerRender(struct Player* player, struct RenderState* renderState); diff --git a/src/scene/dynamic_scene.c b/src/scene/dynamic_scene.c index 30e8b92..56479c7 100644 --- a/src/scene/dynamic_scene.c +++ b/src/scene/dynamic_scene.c @@ -5,10 +5,9 @@ struct DynamicScene gDynamicScene; -#define FLAG_MASK (DYNAMIC_SCENE_OBJECT_FLAGS_USED | DYNAMIC_SCENE_OBJECT_FLAGS_ACTIVE | DYNAMIC_SCENE_OBJECT_FLAGS_TOUCHING_PORTAL) +#define FLAG_MASK (DYNAMIC_SCENE_OBJECT_FLAGS_USED | DYNAMIC_SCENE_OBJECT_FLAGS_ACTIVE) #define FLAG_VALUE_NOT_TOUCHING_PORTAL (DYNAMIC_SCENE_OBJECT_FLAGS_USED | DYNAMIC_SCENE_OBJECT_FLAGS_ACTIVE) -#define FLAG_VALUE_TOUCHING_PORTAL (DYNAMIC_SCENE_OBJECT_FLAGS_USED | DYNAMIC_SCENE_OBJECT_FLAGS_ACTIVE | DYNAMIC_SCENE_OBJECT_FLAGS_TOUCHING_PORTAL) void dynamicSceneInit() { for (int i = 0; i < MAX_DYNAMIC_SCENE_OBJECTS; ++i) { @@ -16,7 +15,6 @@ void dynamicSceneInit() { } } - void dynamicScenePopulateWithFlags(struct FrustrumCullingInformation* cullingInfo, struct RenderScene* renderScene, int flags) { for (int i = 0; i < MAX_DYNAMIC_SCENE_OBJECTS; ++i) { struct DynamicSceneObject* object = &gDynamicScene.objects[i]; @@ -33,13 +31,6 @@ void dynamicScenePopulateWithFlags(struct FrustrumCullingInformation* cullingInf } } -void dynamicSceneRenderTouchingPortal(struct Transform* cameraTransform, struct FrustrumCullingInformation* cullingInfo, struct RenderState* renderState) { - struct RenderScene* tmpScene = renderSceneNew(cameraTransform, renderState, MAX_DYNAMIC_SCENE_OBJECTS, ~0); - dynamicScenePopulateWithFlags(cullingInfo, tmpScene, FLAG_VALUE_TOUCHING_PORTAL); - renderSceneGenerate(tmpScene, renderState); - renderSceneFree(tmpScene); -} - int dynamicSceneAdd(void* data, DynamicRender renderCallback, struct Transform* transform, float radius) { for (int i = 0; i < MAX_DYNAMIC_SCENE_OBJECTS; ++i) { struct DynamicSceneObject* object = &gDynamicScene.objects[i]; diff --git a/src/scene/dynamic_scene.h b/src/scene/dynamic_scene.h index 2c1b6e1..1153c4e 100644 --- a/src/scene/dynamic_scene.h +++ b/src/scene/dynamic_scene.h @@ -12,7 +12,6 @@ typedef void (*DynamicRender)(void* data, struct RenderScene* renderScene); #define DYNAMIC_SCENE_OBJECT_FLAGS_USED (1 << 0) #define DYNAMIC_SCENE_OBJECT_FLAGS_ACTIVE (1 << 1) -#define DYNAMIC_SCENE_OBJECT_FLAGS_TOUCHING_PORTAL (1 << 2) #define INVALID_DYNAMIC_OBJECT -1 @@ -30,8 +29,6 @@ struct DynamicScene { void dynamicSceneInit(); -void dynamicSceneRenderTouchingPortal(struct Transform* cameraTransform, struct FrustrumCullingInformation* cullingInfo, struct RenderState* renderState); - int dynamicSceneAdd(void* data, DynamicRender renderCallback, struct Transform* transform, float radius); void dynamicSceneRemove(int id); void dynamicSceneSetFlags(int id, int flags); diff --git a/src/scene/portal.c b/src/scene/portal.c index f0173fa..63946a2 100644 --- a/src/scene/portal.c +++ b/src/scene/portal.c @@ -204,8 +204,6 @@ int renderPropsNext(struct RenderProps* current, struct RenderProps* next, struc return 0; } - dynamicSceneRenderTouchingPortal(&next->camera.transform, ¤t->cullingInfo, renderState); - next->currentDepth = current->currentDepth - 1; Vp* viewport = renderPropsBuildViewport(next, renderState); diff --git a/src/scene/scene.c b/src/scene/scene.c index b17819e..91c70d1 100644 --- a/src/scene/scene.c +++ b/src/scene/scene.c @@ -40,14 +40,16 @@ void sceneUpdateListeners(struct Scene* scene); void sceneInit(struct Scene* scene) { signalsInit(1); - cameraInit(&scene->camera, 70.0f, 0.125f * SCENE_SCALE, 40.0f * SCENE_SCALE); + cameraInit(&scene->camera, 70.0f, 0.125f * SCENE_SCALE, 30.0f * SCENE_SCALE); struct Location* startLocation = levelGetLocation(gCurrentLevel->startLocation); struct Location combinedLocation; + struct Vector3 startVelocity; combinedLocation.roomIndex = startLocation->roomIndex; transformConcat(&startLocation->transform, levelRelativeTransform(), &combinedLocation.transform); + quatMultVector(&startLocation->transform.rotation, levelRelativeVelocity(), &startVelocity); - playerInit(&scene->player, &combinedLocation); + playerInit(&scene->player, &combinedLocation, &startVelocity); sceneUpdateListeners(scene); portalInit(&scene->portals[0], 0); @@ -192,8 +194,6 @@ void sceneRender(struct Scene* scene, struct RenderState* renderState, struct Gr gDPSetRenderMode(renderState->dl++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2); - dynamicSceneRenderTouchingPortal(&scene->camera.transform, &renderProperties.cullingInfo, renderState); - sceneRenderWithProperties(scene, &renderProperties, renderState); sceneRenderPortalGun(scene, renderState);