From 0b66517ccaa45c708bbc19454be61bd1a5970637 Mon Sep 17 00:00:00 2001 From: James Lambert Date: Sat, 21 May 2022 21:34:33 -0600 Subject: [PATCH] Logic for crossing between rooms --- Dockerfile | 4 +- .../test_chamber_00/test_chamber_00_0.blend | Bin 2065356 -> 2065356 bytes skelatool64/main.cpp | 2 +- .../CollisionGenerator.cpp | 7 +++- .../definition_generator/CollisionGenerator.h | 4 +- .../definition_generator/RoomGenerator.cpp | 22 +++++----- .../src/definition_generator/RoomGenerator.h | 3 +- .../definition_generator/TriggerGenerator.cpp | 6 ++- src/levels/levels.c | 38 ++++++++++++++++++ src/levels/levels.h | 4 ++ src/physics/collision_scene.h | 11 +++++ src/player/player.c | 5 +++ 12 files changed, 89 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1fa8498..d737c04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,7 +40,8 @@ RUN apt install -y binutils-mips-n64 \ cmake \ build-essential \ wget \ - unzip + unzip \ + sox COPY skelatool64/src skelatool64/src COPY skelatool64/main.cpp skelatool64/main.cpp @@ -62,6 +63,7 @@ RUN pip install vpk COPY Makefile Makefile COPY tools/export_fbx.py tools/export_fbx.py COPY tools/generate_level_list.js tools/generate_level_list.js +COPY tools/generate_sound_ids.js tools/generate_sound_ids.js COPY asm asm COPY assets assets COPY src src diff --git a/assets/test_chambers/test_chamber_00/test_chamber_00_0.blend b/assets/test_chambers/test_chamber_00/test_chamber_00_0.blend index e351d1fdc3b48ff12f9fcdc784c55c9dbe5d3f16..a7b1e543e1df02ebab093c0ebf907e22020a618a 100644 GIT binary patch delta 52723 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 K=Sa)l^Z$Qmnxm5d delta 53988 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 diff --git a/skelatool64/main.cpp b/skelatool64/main.cpp index 22dbb9c..75b9581 100644 --- a/skelatool64/main.cpp +++ b/skelatool64/main.cpp @@ -153,7 +153,7 @@ int main(int argc, char *argv[]) { roomGenerator.GenerateDefinitions(scene, fileDef); std::cout << "Generating collider definitions" << std::endl; - CollisionGenerator colliderGenerator(settings); + CollisionGenerator colliderGenerator(settings, roomGenerator.GetOutput()); colliderGenerator.TraverseScene(scene); colliderGenerator.GenerateDefinitions(scene, fileDef); diff --git a/skelatool64/src/definition_generator/CollisionGenerator.cpp b/skelatool64/src/definition_generator/CollisionGenerator.cpp index ffba371..fbb195d 100644 --- a/skelatool64/src/definition_generator/CollisionGenerator.cpp +++ b/skelatool64/src/definition_generator/CollisionGenerator.cpp @@ -4,9 +4,10 @@ #include -CollisionGenerator::CollisionGenerator(const DisplayListSettings& settings) : +CollisionGenerator::CollisionGenerator(const DisplayListSettings& settings, const RoomGeneratorOutput& roomOutput) : DefinitionGenerator(), - mSettings(settings) {} + mSettings(settings), + mRoomOutput(roomOutput) {} bool CollisionGenerator::ShouldIncludeNode(aiNode* node) { @@ -26,6 +27,8 @@ void CollisionGenerator::GenerateDefinitions(const aiScene* scene, CFileDefiniti std::string colliderTypesName = fileDefinition.GetUniqueName("collider_types"); std::string collisionObjectsName = fileDefinition.GetUniqueName("collision_objects"); + sortNodesByRoom(mIncludedNodes, mRoomOutput); + for (auto node = mIncludedNodes.begin(); node != mIncludedNodes.end(); ++node) { for (unsigned i = 0; i < (*node)->mNumMeshes; ++i) { aiMesh* mesh = scene->mMeshes[(*node)->mMeshes[i]]; diff --git a/skelatool64/src/definition_generator/CollisionGenerator.h b/skelatool64/src/definition_generator/CollisionGenerator.h index 020aadb..f498169 100644 --- a/skelatool64/src/definition_generator/CollisionGenerator.h +++ b/skelatool64/src/definition_generator/CollisionGenerator.h @@ -4,6 +4,7 @@ #include "DefinitionGenerator.h" #include "../DisplayListSettings.h" #include "CollisionQuad.h" +#include "RoomGenerator.h" struct CollisionGeneratorOutput { std::string quadsName; @@ -12,7 +13,7 @@ struct CollisionGeneratorOutput { class CollisionGenerator : public DefinitionGenerator { public: - CollisionGenerator(const DisplayListSettings& settings); + CollisionGenerator(const DisplayListSettings& settings, const RoomGeneratorOutput& roomOutput); virtual bool ShouldIncludeNode(aiNode* node); virtual void GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition); @@ -20,6 +21,7 @@ public: const CollisionGeneratorOutput& GetOutput() const; private: DisplayListSettings mSettings; + RoomGeneratorOutput mRoomOutput; CollisionGeneratorOutput mOutput; }; diff --git a/skelatool64/src/definition_generator/RoomGenerator.cpp b/skelatool64/src/definition_generator/RoomGenerator.cpp index 58b36c5..596c18b 100644 --- a/skelatool64/src/definition_generator/RoomGenerator.cpp +++ b/skelatool64/src/definition_generator/RoomGenerator.cpp @@ -20,19 +20,23 @@ RoomGenerator::RoomGenerator(const DisplayListSettings& settings): DefinitionGen short RoomGeneratorOutput::FindLocationRoom(const std::string& name) const { for (auto& location : namedLocations) { if (location.name == name) { - auto room = roomIndexMapping.find(location.node); - - if (room == roomIndexMapping.end()) { - return -1; - } - - return room->second; + return RoomForNode(location.node); } } return -1; } +int RoomGeneratorOutput::RoomForNode(const aiNode* node) const { + auto room = roomIndexMapping.find(node); + + if (room == roomIndexMapping.end()) { + return -1; + } + + return room->second; +} + struct RoomBlock { aiAABB boundingBox; int roomIndex; @@ -42,9 +46,9 @@ bool RoomGenerator::ShouldIncludeNode(aiNode* node) { return true; } -void sortNodesByRoom(std::vector& nodes, RoomGeneratorOutput& roomOutput) { +void sortNodesByRoom(std::vector& nodes, const RoomGeneratorOutput& roomOutput) { std::sort(nodes.begin(), nodes.end(), [&](const aiNode* a, const aiNode* b) -> bool { - return roomOutput.roomIndexMapping[a] < roomOutput.roomIndexMapping[b]; + return roomOutput.RoomForNode(a) < roomOutput.RoomForNode(b); }); } diff --git a/skelatool64/src/definition_generator/RoomGenerator.h b/skelatool64/src/definition_generator/RoomGenerator.h index 740e467..f4f929a 100644 --- a/skelatool64/src/definition_generator/RoomGenerator.h +++ b/skelatool64/src/definition_generator/RoomGenerator.h @@ -28,9 +28,10 @@ struct RoomGeneratorOutput { int roomCount; short FindLocationRoom(const std::string& name) const; + int RoomForNode(const aiNode* node) const; }; -void sortNodesByRoom(std::vector& nodes, RoomGeneratorOutput& roomOutput); +void sortNodesByRoom(std::vector& nodes, const RoomGeneratorOutput& roomOutput); class RoomGenerator : public DefinitionGenerator { public: diff --git a/skelatool64/src/definition_generator/TriggerGenerator.cpp b/skelatool64/src/definition_generator/TriggerGenerator.cpp index b849e90..3139902 100644 --- a/skelatool64/src/definition_generator/TriggerGenerator.cpp +++ b/skelatool64/src/definition_generator/TriggerGenerator.cpp @@ -20,14 +20,16 @@ CutsceneStep::CutsceneStep( bool doesBelongToCutscene(const CutsceneStep& startStep, const CutsceneStep& otherStep) { aiVector3D offset = otherStep.location - startStep.location; - aiVector3D relativeOffset = startStep.direction.Rotate(offset); + aiQuaternion directionCopy = startStep.direction; + aiVector3D relativeOffset = directionCopy.Rotate(offset); return relativeOffset.y >= 0.0f && relativeOffset.x * relativeOffset.x + relativeOffset.z * relativeOffset.z < 0.1f; } float distanceFromStart(const CutsceneStep& startStep, const CutsceneStep& otherStep) { aiVector3D offset = otherStep.location - startStep.location; - aiVector3D relativeOffset = startStep.direction.Rotate(offset); + aiQuaternion directionCopy = startStep.direction; + aiVector3D relativeOffset = directionCopy.Rotate(offset); return relativeOffset.y; } diff --git a/src/levels/levels.c b/src/levels/levels.c index a2ffd56..2d2a4dc 100644 --- a/src/levels/levels.c +++ b/src/levels/levels.c @@ -77,4 +77,42 @@ struct Location* levelGetLocation(short index) { } return &gCurrentLevel->locations[index]; +} + +int levelCheckDoorwaySides(struct Vector3* position, int currentRoom) { + struct Room* room = &gCurrentLevel->rooms[currentRoom]; + + int sideMask = 0; + + for (int i = 0; i < room->doorwayCount; ++i) { + if (planePointDistance(&gCurrentLevel->doorways[room->doorwayIndices[i]].quad.plane, position) > 0) { + sideMask |= 1 << 1; + } + } + + return sideMask; +} + +int levelCheckDoorwayCrossings(struct Vector3* position, int currentRoom, int sideMask) { + struct Room* room = &gCurrentLevel->rooms[currentRoom]; + + for (int i = 0; i < room->doorwayCount; ++i) { + struct Doorway* doorway = &gCurrentLevel->doorways[room->doorwayIndices[i]]; + + int prevSide = (sideMask & (1 << 1)) != 0; + int currSide = planePointDistance(&doorway->quad.plane, position) > 0; + + if (prevSide != currSide) { + struct Vector3 posOnFace; + planeProjectPoint(&doorway->quad.plane, position, &posOnFace); + + if (collisionQuadDetermineEdges(&posOnFace, &doorway->quad)) { + continue; + } + + return doorway->roomA == currentRoom ? doorway->roomB : doorway->roomA; + } + } + + return currentRoom; } \ No newline at end of file diff --git a/src/levels/levels.h b/src/levels/levels.h index 0e5d9f6..961ab5f 100644 --- a/src/levels/levels.h +++ b/src/levels/levels.h @@ -17,6 +17,10 @@ Gfx* levelMaterialRevert(int index); int levelQuadIndex(struct CollisionObject* pointer); +int levelCheckDoorwaySides(struct Vector3* position, int currentRoom); + +int levelCheckDoorwayCrossings(struct Vector3* position, int currentRoom, int sideMask); + void levelCheckTriggers(struct Vector3* playerPos); struct Location* levelGetLocation(short index); diff --git a/src/physics/collision_scene.h b/src/physics/collision_scene.h index 866e051..a0973d0 100644 --- a/src/physics/collision_scene.h +++ b/src/physics/collision_scene.h @@ -10,6 +10,17 @@ #define MAX_DYNAMIC_OBJECTS 16 +struct BroadphaseEdge { + float xValue; + short quadIndex; + short isStartEdge; +}; + +struct BroadphaseIndex { + struct BroadphaseEdge* edges; + short edgesCount; +}; + struct CollisionScene { struct CollisionObject* quads; struct Transform* portalTransforms[2]; diff --git a/src/player/player.c b/src/player/player.c index 6418be6..8ceb78d 100644 --- a/src/player/player.c +++ b/src/player/player.c @@ -8,6 +8,7 @@ #include "physics/collision_sphere.h" #include "physics/collision_scene.h" #include "physics/config.h" +#include "../levels/levels.h" #define GRAB_RAYCAST_DISTANCE 3.5f @@ -147,6 +148,8 @@ void playerUpdate(struct Player* player, struct Transform* cameraTransform) { struct Vector3 forward; struct Vector3 right; + int doorwayMask = levelCheckDoorwaySides(&player->body.transform.position, player->currentRoom); + struct Transform* transform = &player->body.transform; quatMultVector(&transform->rotation, &gForward, &forward); @@ -276,4 +279,6 @@ void playerUpdate(struct Player* player, struct Transform* cameraTransform) { cameraTransform->rotation = player->body.transform.rotation; transformPoint(transform, &gCameraOffset, &cameraTransform->position); playerUpdateGrabbedObject(player); + + player->currentRoom = levelCheckDoorwayCrossings(&player->body.transform.position, player->currentRoom, doorwayMask); }