From 79b28eca5b8c9d638eb338677d5ea0df528c2578 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sun, 12 Jan 2025 11:06:32 -0800 Subject: [PATCH] Added SENSOR layer, handling onGround() better. Not sure why they're treated as ground for the player but programmatically handling that now. --- .../src/Collision/CollisionChunkManager.cpp | 6 ++-- OpenTESArena/src/Collision/PhysicsLayer.cpp | 11 ++++-- OpenTESArena/src/Collision/PhysicsLayer.h | 6 ++-- .../src/Entities/EntityChunkManager.cpp | 3 +- OpenTESArena/src/Player/Player.cpp | 35 +++++++++++++++---- OpenTESArena/src/Player/Player.h | 4 +-- .../src/Player/PlayerLogicController.cpp | 19 +++++----- 7 files changed, 59 insertions(+), 25 deletions(-) diff --git a/OpenTESArena/src/Collision/CollisionChunkManager.cpp b/OpenTESArena/src/Collision/CollisionChunkManager.cpp index 9e0adb383..64073eaf9 100644 --- a/OpenTESArena/src/Collision/CollisionChunkManager.cpp +++ b/OpenTESArena/src/Collision/CollisionChunkManager.cpp @@ -50,7 +50,10 @@ namespace static_cast(boxWorldVoxelPos.z + 0.50)); const RadiansF boxYRotation = static_cast(boxShapeDef.yRotation); const JPH::Quat boxJoltQuat = JPH::Quat::sRotation(JPH::Vec3Arg::sAxisY(), boxYRotation); - const JPH::BodyCreationSettings boxSettings(boxShape, boxJoltPos, boxJoltQuat, JPH::EMotionType::Static, PhysicsLayers::NON_MOVING); + const JPH::ObjectLayer boxObjectLayer = isSensor ? PhysicsLayers::SENSOR : PhysicsLayers::NON_MOVING; + JPH::BodyCreationSettings boxSettings(boxShape, boxJoltPos, boxJoltQuat, JPH::EMotionType::Static, boxObjectLayer); + boxSettings.mIsSensor = isSensor; + JPH::Body *boxBody = bodyInterface.CreateBody(boxSettings); if (boxBody == nullptr) { @@ -59,7 +62,6 @@ namespace return false; } - boxBody->SetIsSensor(isSensor); *outBodyID = boxBody->GetID(); return true; } diff --git a/OpenTESArena/src/Collision/PhysicsLayer.cpp b/OpenTESArena/src/Collision/PhysicsLayer.cpp index f30ad52f8..f8acfe17c 100644 --- a/OpenTESArena/src/Collision/PhysicsLayer.cpp +++ b/OpenTESArena/src/Collision/PhysicsLayer.cpp @@ -9,7 +9,9 @@ bool PhysicsObjectLayerPairFilter::ShouldCollide(JPH::ObjectLayer object1, JPH:: case PhysicsLayers::NON_MOVING: return object2 == PhysicsLayers::MOVING; case PhysicsLayers::MOVING: - return true; + return (object2 == PhysicsLayers::NON_MOVING) || (object2 == PhysicsLayers::MOVING) || (object2 == PhysicsLayers::SENSOR); + case PhysicsLayers::SENSOR: + return (object2 == PhysicsLayers::MOVING); default: DebugUnhandledReturnMsg(bool, std::to_string(object1)); } @@ -19,6 +21,7 @@ PhysicsBroadPhaseLayerInterface::PhysicsBroadPhaseLayerInterface() { this->objectToBroadPhase[PhysicsLayers::NON_MOVING] = PhysicsBroadPhaseLayers::NON_MOVING; this->objectToBroadPhase[PhysicsLayers::MOVING] = PhysicsBroadPhaseLayers::MOVING; + this->objectToBroadPhase[PhysicsLayers::SENSOR] = PhysicsBroadPhaseLayers::SENSOR; } uint32_t PhysicsBroadPhaseLayerInterface::GetNumBroadPhaseLayers() const @@ -43,6 +46,8 @@ const char *PhysicsBroadPhaseLayerInterface::GetBroadPhaseLayerName(JPH::BroadPh return "NON_MOVING"; case static_cast(PhysicsBroadPhaseLayers::MOVING): return "MOVING"; + case static_cast(PhysicsBroadPhaseLayers::SENSOR): + return "SENSOR"; default: DebugNotImplementedMsg(std::to_string(layerType)); return nullptr; @@ -57,7 +62,9 @@ bool PhysicsObjectVsBroadPhaseLayerFilter::ShouldCollide(JPH::ObjectLayer layer1 case PhysicsLayers::NON_MOVING: return layer2 == PhysicsBroadPhaseLayers::MOVING; case PhysicsLayers::MOVING: - return true; + return (layer2 == PhysicsBroadPhaseLayers::NON_MOVING) || (layer2 == PhysicsBroadPhaseLayers::MOVING) || (layer2 == PhysicsBroadPhaseLayers::SENSOR); + case PhysicsLayers::SENSOR: + return layer2 == PhysicsBroadPhaseLayers::MOVING; default: DebugUnhandledReturnMsg(bool, std::to_string(layer1)); } diff --git a/OpenTESArena/src/Collision/PhysicsLayer.h b/OpenTESArena/src/Collision/PhysicsLayer.h index 472f020b3..ba593a27e 100644 --- a/OpenTESArena/src/Collision/PhysicsLayer.h +++ b/OpenTESArena/src/Collision/PhysicsLayer.h @@ -11,14 +11,16 @@ namespace PhysicsLayers { static constexpr JPH::ObjectLayer NON_MOVING = 0; static constexpr JPH::ObjectLayer MOVING = 1; - static constexpr JPH::ObjectLayer NUM_LAYERS = 2; + static constexpr JPH::ObjectLayer SENSOR = 2; + static constexpr JPH::ObjectLayer NUM_LAYERS = 3; }; namespace PhysicsBroadPhaseLayers { static constexpr JPH::BroadPhaseLayer NON_MOVING(0); static constexpr JPH::BroadPhaseLayer MOVING(1); - static constexpr uint32_t NUM_LAYERS = 2; + static constexpr JPH::BroadPhaseLayer SENSOR(2); + static constexpr uint32_t NUM_LAYERS = 3; }; class PhysicsObjectLayerPairFilter : public JPH::ObjectLayerPairFilter diff --git a/OpenTESArena/src/Entities/EntityChunkManager.cpp b/OpenTESArena/src/Entities/EntityChunkManager.cpp index d1e80cb8d..a297a1f17 100644 --- a/OpenTESArena/src/Entities/EntityChunkManager.cpp +++ b/OpenTESArena/src/Entities/EntityChunkManager.cpp @@ -85,7 +85,8 @@ namespace static_cast(ceilingScale + capsuleHalfTotalHeight), static_cast(capsuleWorldPointXZ.y)); const JPH::Quat capsuleJoltQuat = JPH::Quat::sRotation(JPH::Vec3Arg::sAxisY(), 0.0f); - JPH::BodyCreationSettings capsuleSettings(capsuleShape, capsuleJoltPos, capsuleJoltQuat, JPH::EMotionType::Kinematic, PhysicsLayers::MOVING); + const JPH::ObjectLayer capsuleObjectLayer = isSensor ? PhysicsLayers::SENSOR : PhysicsLayers::MOVING; + JPH::BodyCreationSettings capsuleSettings(capsuleShape, capsuleJoltPos, capsuleJoltQuat, JPH::EMotionType::Kinematic, capsuleObjectLayer); capsuleSettings.mIsSensor = isSensor; const JPH::Body *capsule = bodyInterface.CreateBody(capsuleSettings); diff --git a/OpenTESArena/src/Player/Player.cpp b/OpenTESArena/src/Player/Player.cpp index 559dce97e..7777ae707 100644 --- a/OpenTESArena/src/Player/Player.cpp +++ b/OpenTESArena/src/Player/Player.cpp @@ -357,11 +357,32 @@ double Player::getJumpMagnitude() const return PlayerConstants::JUMP_VELOCITY; } -bool Player::onGround() const +bool Player::onGround(const JPH::PhysicsSystem &physicsSystem) const { - // @todo: not sure we should ever be on steep ground in this engine. "maxSlopeAngle" affects that, and 0 and 90 don't seem perfect. const JPH::CharacterBase::EGroundState groundState = this->physicsCharacter->GetGroundState(); - return (groundState == JPH::CharacterBase::EGroundState::OnGround) || (groundState == JPH::CharacterBase::EGroundState::OnSteepGround); + + // @todo: not sure we should ever be on steep ground in this engine. "maxSlopeAngle" affects that, and 0 and 90 don't seem perfect. + if ((groundState != JPH::CharacterBase::EGroundState::OnGround) && (groundState != JPH::CharacterBase::EGroundState::OnSteepGround)) + { + return false; + } + + const JPH::BodyID groundBodyID = this->physicsCharacter->GetGroundBodyID(); + if (groundBodyID.IsInvalid()) + { + return false; + } + + const JPH::BodyLockInterface &bodyInterface = physicsSystem.GetBodyLockInterface(); + const JPH::BodyLockRead groundBodyLock(bodyInterface, groundBodyID); + if (!groundBodyLock.Succeeded()) + { + return false; + } + + // @todo: not sure this is the best way. why are sensor colliders being considered ground? + const JPH::Body &groundBody = groundBodyLock.GetBody(); + return !groundBody.IsSensor(); } bool Player::isMoving() const @@ -370,11 +391,11 @@ bool Player::isMoving() const return physicsVelocity.LengthSq() >= ConstantsF::Epsilon; } -bool Player::canJump() const +bool Player::canJump(const JPH::PhysicsSystem &physicsSystem) const { const JPH::RVec3 physicsVelocity = this->physicsCharacter->GetLinearVelocity(); constexpr float tinyEpsilon = 1e-8f; - return this->onGround() && (std::abs(physicsVelocity.GetY()) <= tinyEpsilon); + return this->onGround(physicsSystem) && (std::abs(physicsVelocity.GetY()) <= tinyEpsilon); } void Player::rotateX(Degrees deltaX) @@ -471,14 +492,14 @@ void Player::prePhysicsStep(double dt, Game &game) return; } + const JPH::PhysicsSystem &physicsSystem = game.physicsSystem; const Double3 oldVelocity = this->getPhysicsVelocity(); - if (!this->onGround()) + if (!this->onGround(physicsSystem)) { // Need to apply gravity to Character as its gravity factor is 0 when with CharacterVirtual. this->accelerate(-Double3::UnitY, Physics::GRAVITY, dt); } - JPH::PhysicsSystem &physicsSystem = game.physicsSystem; const JPH::Vec3Arg physicsGravity = -this->physicsCharacter->GetUp() * physicsSystem.GetGravity().Length(); JPH::CharacterVirtual::ExtendedUpdateSettings extendedUpdateSettings; // @todo: for stepping up/down stairs const JPH::BroadPhaseLayerFilter &broadPhaseLayerFilter = physicsSystem.GetDefaultBroadPhaseLayerFilter(PhysicsLayers::MOVING); diff --git a/OpenTESArena/src/Player/Player.h b/OpenTESArena/src/Player/Player.h index 619673709..24a7ccb1f 100644 --- a/OpenTESArena/src/Player/Player.h +++ b/OpenTESArena/src/Player/Player.h @@ -90,9 +90,9 @@ struct Player // Gets the strength of the player's jump (i.e., instantaneous change in Y velocity). double getJumpMagnitude() const; - bool onGround() const; + bool onGround(const JPH::PhysicsSystem &physicsSystem) const; bool isMoving() const; - bool canJump() const; + bool canJump(const JPH::PhysicsSystem &physicsSystem) const; // Pitches and yaws relative to global up vector. void rotateX(Degrees deltaX); diff --git a/OpenTESArena/src/Player/PlayerLogicController.cpp b/OpenTESArena/src/Player/PlayerLogicController.cpp index ffbea6f13..45119fc67 100644 --- a/OpenTESArena/src/Player/PlayerLogicController.cpp +++ b/OpenTESArena/src/Player/PlayerLogicController.cpp @@ -22,7 +22,7 @@ namespace PlayerLogicController { - void handlePlayerMovementClassic(Player &player, double dt, double walkSpeed, bool isOnGround, bool isGhostModeEnabled, + void handlePlayerMovementClassic(Player &player, double dt, double walkSpeed, bool isOnGround, bool canJump, bool isGhostModeEnabled, const InputManager &inputManager, BufferView nativeCursorRegions) { // Classic interface mode. @@ -119,7 +119,7 @@ namespace PlayerLogicController const bool rightClick = inputManager.mouseButtonIsDown(SDL_BUTTON_RIGHT); if (rightClick) { - if (player.canJump()) + if (canJump) { player.accelerateInstant(Double3::UnitY, player.getJumpMagnitude()); } @@ -166,7 +166,7 @@ namespace PlayerLogicController // Check for jumping first (so the player can't slide jump on the first frame). if (space) { - if (player.canJump()) + if (canJump) { player.accelerateInstant(Double3::UnitY, player.getJumpMagnitude()); } @@ -183,7 +183,7 @@ namespace PlayerLogicController } } - void handlePlayerMovementModern(Player &player, double dt, double walkSpeed, bool isOnGround, bool isGhostModeEnabled, + void handlePlayerMovementModern(Player &player, double dt, double walkSpeed, bool isOnGround, bool canJump, bool isGhostModeEnabled, const InputManager &inputManager) { // Modern interface. Listen for WASD. @@ -211,7 +211,7 @@ namespace PlayerLogicController // Check for jumping first so the player can't slide jump on the first frame. if (jump) { - if (player.canJump()) + if (canJump) { player.accelerateInstant(Double3::UnitY, player.getJumpMagnitude()); } @@ -460,22 +460,23 @@ Double2 PlayerLogicController::makeTurningAngularValues(Game &game, double dt, B void PlayerLogicController::handlePlayerMovement(Game &game, double dt, BufferView nativeCursorRegions) { const InputManager &inputManager = game.inputManager; + const JPH::PhysicsSystem &physicsSystem = game.physicsSystem; Player &player = game.player; const double maxWalkSpeed = player.maxWalkSpeed; - const bool isOnGround = player.onGround(); + const bool isOnGround = player.onGround(physicsSystem); + const bool canJump = player.canJump(physicsSystem); const Options &options = game.options; const bool isGhostModeEnabled = options.getMisc_GhostMode(); const bool modernInterface = options.getGraphics_ModernInterface(); if (!modernInterface) { - PlayerLogicController::handlePlayerMovementClassic(player, dt, maxWalkSpeed, isOnGround, isGhostModeEnabled, - inputManager, nativeCursorRegions); + PlayerLogicController::handlePlayerMovementClassic(player, dt, maxWalkSpeed, isOnGround, canJump, isGhostModeEnabled, inputManager, nativeCursorRegions); } else { - PlayerLogicController::handlePlayerMovementModern(player, dt, maxWalkSpeed, isOnGround, isGhostModeEnabled, inputManager); + PlayerLogicController::handlePlayerMovementModern(player, dt, maxWalkSpeed, isOnGround, canJump, isGhostModeEnabled, inputManager); } }