void CPlayerStateJump::GetDesiredVelocity( const Vec3 & move, const CPlayer &player, Vec3* pDesiredVel ) const { // Generate jump velocity. SIMDFConstant(xmfMaxMove, 1.0f); simdf fGroundNormalZ = xmfMaxMove; hwvec3 xmMove = HWVLoadVecUnaligned(&move); if( move.len2() > 0.01f ) { const Matrix34A baseMtx = Matrix34A(player.GetBaseQuat()); Matrix34A baseMtxZ(baseMtx * Matrix33::CreateScale(Vec3(0,0,1))); baseMtxZ.SetTranslation(Vec3(0,0,0)); hwmtx33 xmBaseMtxZ; HWMtx33LoadAligned(xmBaseMtxZ, baseMtxZ); hwmtx33 xmBaseMtxZOpt = HWMtx33GetOptimized(xmBaseMtxZ); hwvec3 xmDesiredVel = HWV3Zero(); if (player.IsRemote()) { xmDesiredVel = xmMove; } else { hwvec3 xmVelocity = HWVLoadVecUnaligned(&player.GetActorPhysics().velocity); SIMDFConstant(xmfZero, 0.0f); SIMDFConstant(xmfDiffMultiplier, 0.3f); SIMDFConstant(xmfMaxDiff, 0.1f); SIMDFConstant(xmfMinMove, 0.5f); SIMDFConstant(xmfOnePointFive, 1.5f); hwvec3 xmMoveFlat = HWVSub(xmMove, HWMtx33RotateVecOpt(xmBaseMtxZOpt, xmMove)); hwvec3 xmCurrVelFlat = HWVSub(xmVelocity, HWMtx33RotateVecOpt(xmBaseMtxZOpt, xmVelocity)); simdf xmfCurrVelSizeSq = HWV3LengthSq(xmCurrVelFlat); hwvec3 xmMoveFlatNormalized = HWV3Normalize(xmMoveFlat); hwvec3 xmCurDirFlatTemp = HWV3Normalize(xmCurrVelFlat); hwvec3 xmCurVelFlatNormalized = HWVSelectSIMDF(xmCurDirFlatTemp, HWV3Zero(), SIMDFLessThanEqual(xmfCurrVelSizeSq, xmfZero)); simdf fDot = HWV3Dot(xmMoveFlatNormalized, xmCurVelFlatNormalized); hwvec3 xmScaledMoveFlat = HWVMultiplySIMDF(xmMoveFlat, SIMDFClamp(fDot, xmfMinMove, xmfMaxMove)); simdf fMoveMult = SIMDFMax(SIMDFMult(SIMDFAbs(fDot), xmfDiffMultiplier), xmfMaxDiff); hwvec3 xmReducedMove = HWVMultiplySIMDF(HWVSub(xmMoveFlat, xmCurrVelFlat), fMoveMult); xmDesiredVel = HWVSelectSIMDF( xmScaledMoveFlat, xmReducedMove, SIMDFLessThan( fDot, xmfZero )); simdf xmfDesiredVelSizeSq = HWV3LengthSq(xmDesiredVel); hwvec3 xmDesiredVelNorm = HWV3Normalize(xmDesiredVel); hwvec3 xmClampedVel = HWVMultiplySIMDF(xmDesiredVelNorm, SIMDFMax( xmfOnePointFive, SIMDFSqrt(xmfCurrVelSizeSq))); xmDesiredVel = HWVSelectSIMDF( xmClampedVel, xmDesiredVel, SIMDFLessThan( xmfDesiredVelSizeSq, xmfCurrVelSizeSq)); } HWVSaveVecUnaligned(pDesiredVel, xmDesiredVel); } }
bool CPlayerStateSwim::OnPrePhysicsUpdate( CPlayer& player, const SActorFrameMovementParams& movement, float frameTime ) { const CPlayerStateSwim_WaterTestProxy& waterProxy = player.m_playerStateSwim_WaterTestProxy; CPlayerStateUtil::PhySetFly( player ); const SPlayerStats& stats = player.m_stats; #ifdef STATE_DEBUG const bool debug = (g_pGameCVars->cl_debugSwimming != 0); #endif const Vec3 entityPos = player.GetEntity()->GetWorldPos(); const Quat baseQuat = player.GetBaseQuat(); const Vec3 vRight(baseQuat.GetColumn0()); Vec3 velocity = player.GetActorPhysics().velocity; // Underwater timer, sounds update and surface wave speed. if (waterProxy.IsHeadUnderWater()) { m_headUnderWaterTimer += frameTime; if (m_headUnderWaterTimer <= -0.0f && !m_onSurface ) { player.PlaySound(CPlayer::ESound_DiveIn, true, "speed", velocity.len()); m_headUnderWaterTimer = 0.0f; } player.PlaySound(CPlayer::ESound_Underwater, true); } else { m_headUnderWaterTimer -= frameTime; if (m_headUnderWaterTimer >= 0.0f && (waterProxy.IsHeadComingOutOfWater() || m_onSurface)) { player.PlaySound(CPlayer::ESound_DiveOut, true, "speed", velocity.len()); m_headUnderWaterTimer = 0.0f; } player.PlaySound(CPlayer::ESound_Underwater, false); } m_headUnderWaterTimer = clamp_tpl( m_headUnderWaterTimer, -0.2f, 0.2f ); // Apply water flow velocity to the player Vec3 gravity; pe_params_buoyancy buoyancy[s_maxWaterVolumesToConsider]; if (int count = gEnv->pPhysicalWorld->CheckAreas(entityPos, gravity, &buoyancy[0], s_maxWaterVolumesToConsider)) { for(int i = 0; i < count && i < s_maxWaterVolumesToConsider; ++i) { // 0 is water if( buoyancy[i].iMedium == 0 ) { velocity += (buoyancy[i].waterFlow * frameTime); } } } // Calculate desired acceleration (user input) Vec3 desiredWorldVelocity(ZERO); Vec3 acceleration(ZERO); { Vec3 desiredLocalNormalizedVelocity(ZERO); Vec3 desiredLocalVelocity(ZERO); const Quat viewQuat = player.GetViewQuat(); const float backwardMultiplier = (float)__fsel(movement.desiredVelocity.y, 1.0f, g_pGameCVars->pl_swimBackSpeedMul); desiredLocalNormalizedVelocity.x = movement.desiredVelocity.x * g_pGameCVars->pl_swimSideSpeedMul; desiredLocalNormalizedVelocity.y = movement.desiredVelocity.y * backwardMultiplier; float sprintMultiplier = 1.0f; if ((player.IsSprinting()) && !player.IsCinematicFlagActive(SPlayerStats::eCinematicFlag_RestrictMovement)) { sprintMultiplier = GetSwimParams().m_swimSprintSpeedMul; // Higher speed multiplier when sprinting while looking up const float upFraction = clamp_tpl(viewQuat.GetFwdZ(), 0.0f, 1.0f); sprintMultiplier *= LERP(1.0f, GetSwimParams().m_swimUpSprintSpeedMul, upFraction); } const float baseSpeed = player.GetStanceMaxSpeed(STANCE_SWIM); desiredLocalVelocity.x = desiredLocalNormalizedVelocity.x * sprintMultiplier * baseSpeed; desiredLocalVelocity.y = desiredLocalNormalizedVelocity.y * sprintMultiplier * baseSpeed; desiredLocalVelocity.z = desiredLocalNormalizedVelocity.z * g_pGameCVars->pl_swimVertSpeedMul * baseSpeed; // The desired movement is applied in view-space, not in entity-space, since entity does not necessarily pitch while swimming. desiredWorldVelocity += viewQuat.GetColumn0() * desiredLocalVelocity.x; desiredWorldVelocity += viewQuat.GetColumn1() * desiredLocalVelocity.y; // though, apply up/down in world space. desiredWorldVelocity.z += desiredLocalVelocity.z; #ifdef STATE_DEBUG if (debug) { gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.8f), 1.5f, "BaseSpeed %1.3f", baseSpeed); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,1.0f), 1.5f, "SprintMul %1.2f", sprintMultiplier); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.6f), 1.5f, "MoveN[%1.3f, %1.3f, %1.3f]", desiredLocalNormalizedVelocity.x, desiredLocalNormalizedVelocity.y, desiredLocalNormalizedVelocity.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.5f), 1.5f, "VeloL[%1.3f, %1.3f, %1.3f]", desiredLocalVelocity.x, desiredLocalVelocity.y, desiredLocalVelocity.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.4f), 1.5f, "VeloW[%1.3f, %1.3f, %1.3f]", desiredWorldVelocity.x, desiredWorldVelocity.y, desiredWorldVelocity.z); } #endif //Remove a bit of control when entering the water const float userControlFraction = (float)__fsel(0.3f - waterProxy.GetSwimmingTimer(), 0.2f, 1.0f); acceleration += desiredWorldVelocity * userControlFraction; } // Apply acceleration (frame-rate independent) const float accelerateDelayInv = 3.333f; velocity += acceleration * (frameTime * accelerateDelayInv); #ifdef STATE_DEBUG if( debug ) { gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.2f), 1.5f, " Axx[%1.3f, %1.3f, %1.3f]", acceleration.x, acceleration.y, acceleration.z); } #endif //-------------------- const float relativeWaterLevel = waterProxy.GetRelativeWaterLevel() + 0.1f; const float surfaceDistanceFraction = clamp_tpl(fabsf(relativeWaterLevel), 0.0f, 1.0f); float surfaceProximityInfluence = 1.0f - surfaceDistanceFraction; const float verticalVelocityFraction = clamp_tpl((fabsf(desiredWorldVelocity.z) - 0.3f) * 2.5f, 0.0f, 1.0f); surfaceProximityInfluence = surfaceProximityInfluence * (1.0f - verticalVelocityFraction); // Apply velocity dampening (frame-rate independent) Vec3 damping(ZERO); const float zSpeedPreDamping = velocity.z; { damping.x = fabsf(velocity.x); damping.y = fabsf(velocity.y); // Vertical damping is special, to allow jumping out of water with higher speed, // and also not sink too deep when falling down ito the water after jump or such. float zDamp = 1.0f + (6.0f * clamp_tpl((-velocity.z - 1.0f) * 0.333f, 0.0f, 1.0f)); zDamp *= 1.0f - surfaceProximityInfluence; damping.z = fabsf(velocity.z) * zDamp; const float stopDelayInv = 3.333f; damping *= (frameTime * stopDelayInv); velocity.x = (float)__fsel((fabsf(velocity.x) - damping.x), (velocity.x - fsgnf(velocity.x) * damping.x), 0.0f); velocity.y = (float)__fsel((fabsf(velocity.y) - damping.y), (velocity.y - fsgnf(velocity.y) * damping.y), 0.0f); velocity.z = (float)__fsel((fabsf(velocity.z) - damping.z), (velocity.z - fsgnf(velocity.z) * damping.z), 0.0f); //Make sure you can not swim above the surface if ((relativeWaterLevel >= 0.0f) && (velocity.z > 0.0f)) { velocity.z = 0.0f; } } // Decide if we're on the surface and therefore need to be kept there.. if( relativeWaterLevel > -0.2f && relativeWaterLevel < 1.0f && fabs_tpl( zSpeedPreDamping ) < 0.5f ) { if( !waterProxy.IsHeadUnderWater() ) { m_onSurface = true; } } else { // we only leave the surface if the player moves, otherwise we try and keep the // player on the surface, even if they currently arent m_onSurface = false; } // Calculate and apply surface movement to the player. float speedDelta = 0.0f; if( m_onSurface ) { const float newWaterLevel = waterProxy.GetWaterLevel(); const float waterLevelDelta = clamp_tpl(newWaterLevel - m_lastWaterLevel, -1.0f, 1.0f ); const float newCheckedTime = waterProxy.GetWaterLevelTimeUpdated(); const float timeDelta = newCheckedTime - m_lastWaterLevelTime; if( timeDelta > FLT_EPSILON ) { speedDelta = waterLevelDelta/timeDelta; velocity.z += speedDelta; } m_lastWaterLevel = newWaterLevel; m_lastWaterLevelTime = newCheckedTime; } // Set request type and velocity player.GetMoveRequest().type = eCMT_Fly; player.GetMoveRequest().velocity = velocity; #ifdef STATE_DEBUG // DEBUG VELOCITY if (debug) { gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.0f), 1.5f, "Velo[%1.3f, %1.3f, %1.3f]", velocity.x, velocity.y, velocity.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.4f), 1.5f, "Damp[%1.3f, %1.3f, %1.3f]", damping.x, damping.y, damping.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.6f), 1.5f, "FrameTime %1.4f", frameTime); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.3f), 1.5f, "DeltaSpeed[%1.3f]", speedDelta ); //if (bNewSwimJumping) //gEnv->pRenderer->DrawLabel(entityPos - vRight * 0.15f + Vec3(0,0,0.6f), 2.0f, "JUMP"); } #endif return true; }