Skip to content

Commit

Permalink
Added SENSOR layer, handling onGround() better.
Browse files Browse the repository at this point in the history
Not sure why they're treated as ground for the player but programmatically handling that now.
  • Loading branch information
afritz1 committed Jan 12, 2025
1 parent 78edecb commit 79b28ec
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 25 deletions.
6 changes: 4 additions & 2 deletions OpenTESArena/src/Collision/CollisionChunkManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ namespace
static_cast<float>(boxWorldVoxelPos.z + 0.50));
const RadiansF boxYRotation = static_cast<RadiansF>(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)
{
Expand All @@ -59,7 +62,6 @@ namespace
return false;
}

boxBody->SetIsSensor(isSensor);
*outBodyID = boxBody->GetID();
return true;
}
Expand Down
11 changes: 9 additions & 2 deletions OpenTESArena/src/Collision/PhysicsLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand All @@ -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
Expand All @@ -43,6 +46,8 @@ const char *PhysicsBroadPhaseLayerInterface::GetBroadPhaseLayerName(JPH::BroadPh
return "NON_MOVING";
case static_cast<JPH::BroadPhaseLayer::Type>(PhysicsBroadPhaseLayers::MOVING):
return "MOVING";
case static_cast<JPH::BroadPhaseLayer::Type>(PhysicsBroadPhaseLayers::SENSOR):
return "SENSOR";
default:
DebugNotImplementedMsg(std::to_string(layerType));
return nullptr;
Expand All @@ -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));
}
Expand Down
6 changes: 4 additions & 2 deletions OpenTESArena/src/Collision/PhysicsLayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion OpenTESArena/src/Entities/EntityChunkManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ namespace
static_cast<float>(ceilingScale + capsuleHalfTotalHeight),
static_cast<float>(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);
Expand Down
35 changes: 28 additions & 7 deletions OpenTESArena/src/Player/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions OpenTESArena/src/Player/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
19 changes: 10 additions & 9 deletions OpenTESArena/src/Player/PlayerLogicController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const Rect> nativeCursorRegions)
{
// Classic interface mode.
Expand Down Expand Up @@ -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());
}
Expand Down Expand Up @@ -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());
}
Expand All @@ -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.
Expand Down Expand Up @@ -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());
}
Expand Down Expand Up @@ -460,22 +460,23 @@ Double2 PlayerLogicController::makeTurningAngularValues(Game &game, double dt, B
void PlayerLogicController::handlePlayerMovement(Game &game, double dt, BufferView<const Rect> 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);
}
}

Expand Down

0 comments on commit 79b28ec

Please sign in to comment.