void CPlayerRotation::ProcessForcedLookDirection( const Quat& lastViewQuat, float frameTime ) { const float forceLookLenSqr(m_forceLookVector.len2()); if (forceLookLenSqr < 0.001f) return; const float forceLookLen(sqrt_tpl(forceLookLenSqr)); Vec3 forceLook(m_forceLookVector); forceLook *= (float)__fres(forceLookLen); forceLook = lastViewQuat.GetInverted() * forceLook; const float smoothSpeed(6.6f * forceLookLen); float blendAmount = min(1.0f,frameTime*smoothSpeed); if(!m_bForcedLookAtBlendingEnabled) { blendAmount = 1.0f; } m_deltaAngles.x += asinf(forceLook.z) * blendAmount; m_deltaAngles.z += atan2_tpl(-forceLook.x,forceLook.y) * blendAmount; PR_CHECKQNAN_VEC(m_deltaAngles); }
void CNetPlayerInput::DoSetState(const SSerializedPlayerInput& input ) { m_newInterpolation |= (input.position != m_curInput.position) || (input.deltaMovement != m_curInput.deltaMovement); const bool wasSprinting = m_curInput.sprint; m_curInput = input; CHANGED_NETWORK_STATE(m_pPlayer, CPlayer::ASPECT_INPUT_CLIENT ); if(wasSprinting != input.sprint) { SInputEventData inputEventData( SInputEventData::EInputEvent_Sprint, m_pPlayer->GetEntityId(), CCryName("sprint"), input.sprint ? eAAM_OnPress : eAAM_OnRelease, 0.f ); m_pPlayer->StateMachineHandleEventMovement( SStateEventPlayerInput( &inputEventData ) ); } // not having these set seems to stop a remote avatars rotation being reflected m_curInput.aiming = true; m_curInput.allowStrafing = true; m_curInput.usinglookik = true; IAIActor* pAIActor = CastToIAIActorSafe(m_pPlayer->GetEntity()->GetAI()); if (pAIActor) pAIActor->GetState().bodystate=input.bodystate; CMovementRequest moveRequest; moveRequest.SetStance( (EStance)m_curInput.stance ); if(IsDemoPlayback()) { Vec3 localVDir(m_pPlayer->GetViewQuatFinal().GetInverted() * m_curInput.lookDirection); Ang3 deltaAngles(asinf(localVDir.z),0,atan2_tpl(-localVDir.x,localVDir.y)); moveRequest.AddDeltaRotation(deltaAngles*gEnv->pTimer->GetFrameTime()); } moveRequest.SetPseudoSpeed(CalculatePseudoSpeed()); moveRequest.SetAllowStrafing(input.allowStrafing); m_pPlayer->GetMovementController()->RequestMovement(moveRequest); #if !defined(_RELEASE) // debug.. if (g_pGameCVars->g_debugNetPlayerInput & 1) { IPersistantDebug * pPD = gEnv->pGame->GetIGameFramework()->GetIPersistantDebug(); pPD->Begin( string("net_player_input_") + m_pPlayer->GetEntity()->GetName(), true ); pPD->AddSphere( moveRequest.GetLookTarget(), 0.5f, ColorF(1,0,1,1), 1.0f ); // pPD->AddSphere( moveRequest.GetMoveTarget(), 0.5f, ColorF(1,1,0,1), 1.0f ); Vec3 wp(m_pPlayer->GetEntity()->GetWorldPos() + Vec3(0,0,2)); pPD->AddDirection( wp, 1.5f, m_curInput.deltaMovement, ColorF(1,0,0,1), 1.0f ); pPD->AddDirection( wp, 1.5f, m_curInput.lookDirection, ColorF(0,1,0,1), 1.0f ); } #endif }
//------------------------------------------------------------------------------------ // Adjust the aim dir before we pass it to the torsoAim pose modifier, this allows us // to have the weapon deviate from the camera in certain circumstances // Should only be called once per frame as it time-steps internal vars //------------------------------------------------------------------------------------ void CLocalPlayerComponent::AdjustTorsoAimDir(float fFrameTime, Vec3 &aimDir) { const f32 HALF_PI = gf_PI * 0.5f; float newElevLimit = HALF_PI; const f32 MIN_FLATTEN_LEVEL = -0.3f; const f32 MAX_FLATTEN_LEVEL = -0.1f; const f32 TARGET_FLATTEN_ELEV = -0.2f; const f32 LIMIT_CHANGE_RATE = HALF_PI; if (g_pGameCVars->pl_swimAlignArmsToSurface && m_rPlayer.IsSwimming() && (m_rPlayer.m_playerStateSwim_WaterTestProxy.GetRelativeWaterLevel() > MIN_FLATTEN_LEVEL)) { newElevLimit = (m_rPlayer.m_playerStateSwim_WaterTestProxy.GetRelativeWaterLevel() - MIN_FLATTEN_LEVEL) / (MAX_FLATTEN_LEVEL - MIN_FLATTEN_LEVEL); newElevLimit = LERP(gf_PI * 0.5f, TARGET_FLATTEN_ELEV, clamp_tpl(newElevLimit, 0.0f, 1.0f)); } float limitDelta = LIMIT_CHANGE_RATE * fFrameTime; float limitDiff = newElevLimit - m_stapElevLimit; float smoothedLimit = (float) fsel(fabs_tpl(limitDiff) - limitDelta, m_stapElevLimit + (fsgnf(limitDiff) * limitDelta), newElevLimit); m_stapElevLimit = smoothedLimit; if (smoothedLimit < HALF_PI) { //--- Need to limit, convert to yaw & elev, limit & then convert back float yaw, elev; float xy = aimDir.GetLengthSquared2D(); if (xy > 0.001f) { yaw = atan2_tpl(aimDir.y,aimDir.x); elev = asin_tpl(clamp_tpl(aimDir.z, -1.f, +1.f)); } else { yaw = 0.f; elev = (float)fsel(aimDir.z, +1.f, -1.f) * (gf_PI*0.5f); } elev = min(elev, smoothedLimit); float sinYaw, cosYaw; float sinElev, cosElev; sincos_tpl(yaw, &sinYaw, &cosYaw); sincos_tpl(elev, &sinElev, &cosElev); aimDir.x = cosYaw * cosElev; aimDir.y = sinYaw * cosElev; aimDir.z = sinElev; } }
//------------------------------------------------------------------------ void CVehicleViewSteer::Update(float dt) { IEntity* pEntity = m_pVehicle->GetEntity(); assert(pEntity); IVehicleMovement* pVehicleMovement = m_pVehicle->GetMovement(); if (pVehicleMovement == NULL) return; IPhysicalEntity* pPhysEntity = pEntity->GetPhysics(); if (!pPhysEntity) return; pe_status_dynamics dynStatus; pPhysEntity->GetStatus(&dynStatus); SMovementState movementState; pVehicleMovement->GetMovementState(movementState); const float pedal = pVehicleMovement->GetEnginePedal(); const float maxSpeed = movementState.maxSpeed; const Matrix34 &pose = m_pAimPart ? m_pAimPart->GetWorldTM() : pEntity->GetWorldTM(); const Vec3 entityPos = pose.GetColumn3(); const Vec3 xAxis = pose.GetColumn0(); const Vec3 yAxis = pose.GetColumn1(); const Vec3 zAxis = pose.GetColumn2(); const float forwardSpeed = dynStatus.v.dot(yAxis); const float speedNorm = clamp_tpl(forwardSpeed / maxSpeed, 0.0f, 1.0f); const Vec3 maxRotation = m_maxRotation + speedNorm * (m_maxRotation2 - m_maxRotation); CalcLookAt(pose); if (m_lookAt.IsValid()) { if (!m_lastOffset.IsValid()) { m_position = pose * m_localSpaceCameraOffset; m_lastOffset = m_position - m_lookAt; m_lastOffsetBeforeElev = m_lastOffset; } Vec3 offset = m_lastOffsetBeforeElev; if (pedal < 0.1f && forwardSpeed < 1.0f) { // Going Backwards m_flags &= ~(eVCam_goingForwards | m_forwardFlags); m_flags |= m_backwardsFlags; } if (offset.dot(yAxis) < 0.8f && forwardSpeed > 1.f) { // Going Forwards m_flags &= ~m_backwardsFlags; m_flags |= eVCam_goingForwards | m_forwardFlags; } float sensitivity = (1.f - speedNorm) * m_stickSensitivity.z + speedNorm * m_stickSensitivity2.z; float rotate = -m_rotatingAction.z * sensitivity; rotate = rotate * dt; if (zAxis.z > 0.1f) { // Safe to update curYaw Vec3 projectedX = xAxis; projectedX.z = 0.f; Vec3 projectedY = yAxis; projectedY.z = 0.f; const float newYaw = atan2_tpl(offset.dot(projectedX), -(offset.dot(projectedY))); const float maxChange = DEG2RAD(270.f) * dt; const float delta = clamp_tpl(newYaw - m_curYaw, -maxChange, +maxChange); m_curYaw += delta; } // Rotation Action { if (m_flags & eVCam_rotationClamp) { float newYaw = clamp_tpl(m_curYaw + rotate, -maxRotation.z, +maxRotation.z); rotate = newYaw - m_curYaw; rotate = clamp_tpl(newYaw - m_curYaw, -fabsf(rotate), +fabsf(rotate)); m_rotation.z += rotate; } else { m_rotation.z = 0.f; } if (speedNorm > 0.1f) { float reduce = dt * 1.f; m_rotation.z = m_rotation.z - reduce * m_rotation.z / (fabsf(m_rotation.z) + reduce); } } // Ang Spring { float angSpeedCorrection = dt * dt * m_angSpeedCorrection / (dt * m_angSpeedCorrection + 1.f) * dynStatus.w.z; if ((m_flags & eVCam_rotationSpring) == 0) { m_angReturnSpeed = 0.f; angSpeedCorrection = 0.f; } float difference = m_rotation.z - m_curYaw; float relax = difference * (m_angReturnSpeed * dt) / ((m_angReturnSpeed * dt) + 1.f); const float delta = +relax + angSpeedCorrection + rotate; m_curYaw += delta; Matrix33 rot = Matrix33::CreateRotationZ(delta); offset = rot * offset; // Lerp the spring speed float angSpeedTarget = m_angReturnSpeed1 + speedNorm * (m_angReturnSpeed2 - m_angReturnSpeed1); m_angReturnSpeed += (angSpeedTarget - m_angReturnSpeed) * (dt / (dt + 0.3f)); m_angSpeedCorrection += (m_angSpeedCorrection0 - m_angSpeedCorrection) * (dt / (dt + 0.3f)); } if (!offset.IsValid()) offset = m_lastOffset; // Velocity influence Vec3 displacement = -((2.f - speedNorm) * dt) * dynStatus.v;// - yAxis*(0.0f*speedNorm*(yAxis.dot(dynStatus.v)))); float dot = offset.dot(displacement); if (dot < 0.f) { displacement = displacement + offset * -0.1f * (offset.dot(displacement) / offset.GetLengthSquared()); } offset = offset + displacement; const float radius0 = fabsf(m_localSpaceCameraOffset.y); const float minRadius = radius0 * m_radiusMin; const float maxRadius = radius0 * m_radiusMax; float radiusXY = sqrtf(sqr(offset.x) + sqr(offset.y)); Vec3 offsetXY = offset; offsetXY.z = 0.f; Vec3 accelerationV = (dynStatus.v - m_lastVehVel); float acceleration = offsetXY.dot(accelerationV) / radiusXY; m_lastVehVel = dynStatus.v; m_radiusVel -= acceleration; m_radius += m_radiusVel * dt - dt * m_radiusVelInfluence * offsetXY.dot(dynStatus.v) / radiusXY; m_radiusVel *= expf(-dt * m_radiusDampRate); m_radius += (radius0 - m_radius) * (dt * m_radiusRelaxRate) / (dt * m_radiusRelaxRate + 1.f); m_radius = clamp_tpl(m_radius, minRadius, maxRadius); offset = offset * (m_radius / radiusXY); // Vertical motion float targetOffsetHeight = m_localSpaceCameraOffset.z * (m_radius / radius0); float oldOffsetHeight = offset.z; offset.z += (targetOffsetHeight - offset.z) * (dt / (dt + 0.3f)); Limit(offset.z, targetOffsetHeight - 2.f, targetOffsetHeight + 2.f); float verticalChange = offset.z - oldOffsetHeight; m_lastOffsetBeforeElev = offset; // Add up and down camera tilt { offset.z -= verticalChange; m_rotation.x += dt * m_stickSensitivity.x * m_rotatingAction.x; m_rotation.x = clamp_tpl(m_rotation.x, -maxRotation.x, +maxRotation.x); float elevAngleVehicle = m_inheritedElev * yAxis.z; // yAxis.z == approx elevation angle float elevationAngle = m_rotation.x - elevAngleVehicle; float sinElev, cosElev; sincos_tpl(elevationAngle, &sinElev, &cosElev); float horizLen = sqrtf(offset.GetLengthSquared2D()); float horizLenNew = horizLen * cosElev - sinElev * offset.z; if (horizLen > 1e-4f) { horizLenNew /= horizLen; offset.x *= horizLenNew; offset.y *= horizLenNew; offset.z = offset.z * cosElev + sinElev * horizLen; } offset.z += verticalChange; } if (!offset.IsValid()) offset = m_lastOffset; m_position = m_lookAt + offset; // Perform world intersection test. { // Initialise sphere and direction. primitives::sphere sphere; sphere.center = m_lookAt; sphere.r = g_SteerCameraRadius; Vec3 direction = m_position - m_lookAt; // Calculate camera bounds. AABB localBounds; m_pVehicle->GetEntity()->GetLocalBounds(localBounds); const float cameraBoundsScale = 0.75f; localBounds.min *= cameraBoundsScale; localBounds.max *= cameraBoundsScale; OBB cameraBounds; Matrix34 worldTM = m_pVehicle->GetEntity()->GetWorldTM(); cameraBounds.SetOBBfromAABB(Matrix33(worldTM), localBounds); // Try to find point on edge of camera bounds to begin swept sphere intersection test. Vec3 rayBoxIntersect; if (Intersect::Ray_OBB(Ray(m_position, -direction), worldTM.GetTranslation(), cameraBounds, rayBoxIntersect) > 0) { Vec3 temp = m_position - rayBoxIntersect; if (direction.Dot(temp) > 0.0f) { sphere.center = rayBoxIntersect; direction = temp; } } // Perform swept sphere intersection test against world. geom_contact* pContact = NULL; IPhysicalEntity* pSkipEntities[10]; float distance = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphere.type, &sphere, direction, ent_static | ent_terrain | ent_rigid | ent_sleeping_rigid, &pContact, 0, (geom_colltype_player << rwi_colltype_bit) | rwi_stop_at_pierceable, 0, 0, 0, pSkipEntities, m_pVehicle->GetSkipEntities(pSkipEntities, 10)); if (distance > 0.0f) { // Sweep intersects world so calculate new offset. offset = (sphere.center + (direction.GetNormalizedSafe() * distance)) - m_lookAt; } } Interpolate(m_lastOffset, offset, 10.f, dt); m_position = m_lookAt + m_lastOffset; } else { CRY_ASSERT_MESSAGE(0, "camera will fail because lookat position is invalid"); } m_rotatingAction.zero(); }
void CPlayerStateGround::OnPrePhysicsUpdate( CPlayer& player, const SActorFrameMovementParams &movement, float frameTime, const bool isHeavyWeapon, const bool isPlayer ) { const Matrix34A baseMtx = Matrix34A(player.GetBaseQuat()); Matrix34A baseMtxZ(baseMtx * Matrix33::CreateScale(Vec3Constants<float>::fVec3_OneZ)); baseMtxZ.SetTranslation(Vec3Constants<float>::fVec3_Zero); const CAutoAimManager& autoAimManager = g_pGame->GetAutoAimManager(); const EntityId closeCombatTargetId = autoAimManager.GetCloseCombatSnapTarget(); const IActor* pCloseCombatTarget = isPlayer && closeCombatTargetId && player.IsClient() ? g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(closeCombatTargetId) : NULL; if (pCloseCombatTarget) { ProcessAlignToTarget(autoAimManager, player, pCloseCombatTarget); } else { // This is to restore inertia if the ProcessAlignToTarget set it previously. if( m_inertiaIsZero ) { CPlayerStateUtil::RestorePlayerPhysics( player ); m_inertiaIsZero = false; } //process movement const bool isRemote = isPlayer && !player.IsClient(); Vec3 move(ZERO); CPlayerStateUtil::CalculateGroundOrJumpMovement( player, movement, isHeavyWeapon, move ); player.GetMoveRequest().type = eCMT_Normal; //apply movement Vec3 desiredVel(ZERO); Vec3 entityPos = player.GetEntity()->GetWorldPos(); Vec3 entityRight(player.GetBaseQuat().GetColumn0()); hwvec3 xmDesiredVel = HWV3Zero(); hwmtx33 xmBaseMtxZ; HWMtx33LoadAligned(xmBaseMtxZ, baseMtxZ); hwmtx33 xmBaseMtxZOpt = HWMtx33GetOptimized(xmBaseMtxZ); hwvec3 xmMove = HWVLoadVecUnaligned(&move); simdf fGroundNormalZ; #ifdef STATE_DEBUG bool debugJumping = (g_pGameCVars->pl_debug_jumping != 0); #endif const SPlayerStats& stats = *player.GetActorStats(); { xmDesiredVel = xmMove; Vec3 groundNormal = player.GetActorPhysics().groundNormal; if(!gEnv->bMultiplayer) { if (player.IsAIControlled()) fGroundNormalZ = SIMDFLoadFloat(square(groundNormal.z)); else fGroundNormalZ = SIMDFLoadFloat(groundNormal.z); } else { //If the hill steepness is greater than our minimum threshold if(groundNormal.z > 1.f - cosf(g_pGameCVars->pl_movement.mp_slope_speed_multiplier_minHill)) { //Check if we are trying to move up or downhill groundNormal.z = 0.f; groundNormal.Normalize(); Vec3 moveDir = move; moveDir.z = 0.f; moveDir.Normalize(); float normalDotMove = groundNormal.Dot(moveDir); //Apply speed multiplier based on moving up/down hill and hill steepness float multiplier = normalDotMove < 0.f ? g_pGameCVars->pl_movement.mp_slope_speed_multiplier_uphill : g_pGameCVars->pl_movement.mp_slope_speed_multiplier_downhill; fGroundNormalZ = SIMDFLoadFloat(1.f - (1.f - player.GetActorPhysics().groundNormal.z) * multiplier); } else { fGroundNormalZ = SIMDFLoadFloat(1.0f); } } const float depthHi = g_pGameCVars->cl_shallowWaterDepthHi; const float depthLo = g_pGameCVars->cl_shallowWaterDepthLo; const float relativeBottomDepth = player.m_playerStateSwim_WaterTestProxy.GetRelativeBottomDepth(); if( relativeBottomDepth > depthLo ) { // Shallow water speed slowdown float shallowWaterMultiplier = 1.0f; shallowWaterMultiplier = isPlayer ? g_pGameCVars->cl_shallowWaterSpeedMulPlayer : g_pGameCVars->cl_shallowWaterSpeedMulAI; shallowWaterMultiplier = max(shallowWaterMultiplier, 0.1f); assert(shallowWaterMultiplier <= 1.0f); float shallowWaterDepthSpan = (depthHi - depthLo); shallowWaterDepthSpan = max(0.1f, shallowWaterDepthSpan); float slowdownFraction = (relativeBottomDepth - depthLo) / shallowWaterDepthSpan; slowdownFraction = clamp_tpl(slowdownFraction, 0.0f, 1.0f); shallowWaterMultiplier = LERP(1.0f, shallowWaterMultiplier, slowdownFraction); //avoid branch if m_stats.relativeBottomDepth <= 0.0f; shallowWaterMultiplier = (float)__fsel(-relativeBottomDepth, 1.0f, shallowWaterMultiplier); simdf vfShallowWaterMultiplier = SIMDFLoadFloat(shallowWaterMultiplier); xmDesiredVel = HWVMultiplySIMDF(xmDesiredVel, vfShallowWaterMultiplier); } } // Slow down on sloped terrain, simply proportional to the slope. xmDesiredVel = HWVMultiplySIMDF(xmDesiredVel, fGroundNormalZ); //be sure desired velocity is flat to the ground hwvec3 vDesiredVelVert = HWMtx33RotateVecOpt(xmBaseMtxZOpt, xmDesiredVel); xmDesiredVel = HWVSub(xmDesiredVel, vDesiredVelVert); HWVSaveVecUnaligned(&desiredVel, xmDesiredVel); if (isPlayer) { Vec3 modifiedSlopeNormal = player.GetActorPhysics().groundNormal; float h = Vec2(modifiedSlopeNormal.x, modifiedSlopeNormal.y).GetLength(); // TODO: OPT: sqrt(x*x+y*y) float v = modifiedSlopeNormal.z; float slopeAngleCur = RAD2DEG(atan2_tpl(h, v)); const float divisorH = (float)__fsel(-h, 1.0f, h); const float divisorV = (float)__fsel(-v, 1.0f, v); const float invV = __fres(divisorV); const float invH = __fres(divisorH); const float slopeAngleHor = 10.0f; const float slopeAngleVer = 50.0f; float slopeAngleFraction = clamp_tpl((slopeAngleCur - slopeAngleHor) * __fres(slopeAngleVer - slopeAngleHor), 0.0f, 1.0f); slopeAngleFraction = slopeAngleFraction * slopeAngleFraction * slopeAngleFraction; float slopeAngleMod = LERP(0.0f, 90.0f, slopeAngleFraction); float s, c; sincos_tpl(DEG2RAD(slopeAngleMod), &s, &c); const float hMultiplier = (float)__fsel(-h, 1.0f, s * invH); const float vMultiplier = (float)__fsel(-v, 1.0f, c * invV); modifiedSlopeNormal.x *= hMultiplier; modifiedSlopeNormal.y *= hMultiplier; modifiedSlopeNormal.z *= vMultiplier; //Normalize the slope normal if possible const float fSlopeNormalLength = modifiedSlopeNormal.len(); const float fSlopeNormalLengthSafe = (float)__fsel(fSlopeNormalLength - 0.000001f, fSlopeNormalLength, 1.0f); modifiedSlopeNormal = modifiedSlopeNormal * __fres(fSlopeNormalLengthSafe); float alignment = min(modifiedSlopeNormal * desiredVel, 0.0f); // Also affect air control (but not as much), to prevent jumping up against steep slopes. alignment *= (float)__fsel(-fabsf(stats.onGround), LERP(0.7f, 1.0f, 1.0f - clamp_tpl(modifiedSlopeNormal.z * 100.0f, 0.0f, 1.0f)), 1.0f); modifiedSlopeNormal.z = modifiedSlopeNormal.z; desiredVel -= modifiedSlopeNormal * alignment; #ifdef STATE_DEBUG if (debugJumping) { player.DebugGraph_AddValue("GroundSlope", slopeAngleCur); player.DebugGraph_AddValue("GroundSlopeMod", slopeAngleMod); } #endif } Vec3 newVelocity = desiredVel; const float fNewSpeed = newVelocity.len(); const float fVelocityMultiplier = (float)__fsel(fNewSpeed - 22.0f, __fres(fNewSpeed+FLT_EPSILON) * 22.0f, 1.0f); // TODO: Maybe we should tell physics about this new velocity ? Or maybe SPlayerStats::velocity ? (stephenn). player.GetMoveRequest().velocity = newVelocity * (stats.flashBangStunMult * fVelocityMultiplier); #ifdef STATE_DEBUG if(g_pGameCVars->pl_debug_movement > 0) { const char* filter = g_pGameCVars->pl_debug_filter->GetString(); const char* name = player.GetEntity()->GetName(); if ((strcmp(filter, "0") == 0) || (strcmp(filter, name) == 0)) { float white[] = {1.0f,1.0f,1.0f,1.0f}; gEnv->pRenderer->Draw2dLabel(20, 450, 2.0f, white, false, "Speed: %.3f m/s", player.GetMoveRequest().velocity.len()); if(g_pGameCVars->pl_debug_movement > 1) { gEnv->pRenderer->Draw2dLabel(35, 470, 1.8f, white, false, "Stance Speed: %.3f m/s - (%sSprinting)", player.GetStanceMaxSpeed(player.GetStance()), player.IsSprinting() ? "" : "Not "); } } } #endif } if( isPlayer ) { CheckForVaultTrigger(player, frameTime); } }
void CPlayerRotation::ClampAngles( float minAngle, float maxAngle ) { //Cap up/down looking { const float currentViewPitch = GetLocalPitch(); const float newPitch = clamp_tpl(currentViewPitch + m_deltaAngles.x, minAngle, maxAngle); m_deltaAngles.x = newPitch - currentViewPitch; } //Further limit the view if necessary { const SViewLimitParams& viewLimits = m_player.m_params.viewLimits; const Vec3 limitDir = viewLimits.GetViewLimitDir(); if (limitDir.len2() < 0.1f) return; const float limitV = viewLimits.GetViewLimitRangeV(); const float limitH = viewLimits.GetViewLimitRangeH(); const float limitVUp = viewLimits.GetViewLimitRangeVUp(); const float limitVDown = viewLimits.GetViewLimitRangeVDown(); if ((limitH+limitV+fabsf(limitVUp)+fabsf(limitVDown)) > 0.0f) { //A matrix is built around the view limit, and then the player view angles are checked with it. //Later, if necessary the upVector could be made customizable. const Vec3 forward(limitDir); const Vec3 up(m_baseQuat.GetColumn2()); const Vec3 right((-(up % forward)).GetNormalized()); Matrix33 limitMtx; limitMtx.SetFromVectors(right,forward,right%forward); limitMtx.Invert(); const Vec3 localDir(limitMtx * m_viewQuat.GetColumn1()); Ang3 limit; if (limitV) { limit.x = asinf(localDir.z) + m_deltaAngles.x; const float deltaX(limitV - fabs(limit.x)); m_deltaAngles.x = m_deltaAngles.x + (float)__fsel(deltaX, 0.0f, deltaX * (float)__fsel(limit.x, 1.0f, -1.0f)); } if (limitVUp || limitVDown) { limit.x = asinf(localDir.z) + m_deltaAngles.x; const float deltaXUp(limitVUp - limit.x); float fNewDeltaX = m_deltaAngles.x; const float fDeltaXUpIncrement = (float)__fsel( deltaXUp, 0.0f, deltaXUp); fNewDeltaX = fNewDeltaX + (float)__fsel(-fabsf(limitVUp), 0.0f, fDeltaXUpIncrement); const float deltaXDown(limitVDown - limit.x); const float fDeltaXDownIncrement = (float)__fsel( deltaXDown, deltaXDown, 0.0f); fNewDeltaX = fNewDeltaX + (float)__fsel(-fabsf(limitVDown), 0.0f, fDeltaXDownIncrement); m_deltaAngles.x = fNewDeltaX; } if (limitH) { limit.z = atan2_tpl(-localDir.x,localDir.y) + m_deltaAngles.z; const float deltaZ(limitH - fabs(limit.z)); m_deltaAngles.z = m_deltaAngles.z + (float)__fsel(deltaZ, 0.0f, deltaZ * (float)__fsel(limit.z, 1.0f, -1.0f)); } } } }
void CNetPlayerInput::UpdateMoveRequest() { CMovementRequest moveRequest; SMovementState moveState; m_pPlayer->GetMovementController()->GetMovementState(moveState); Quat worldRot = m_pPlayer->GetBaseQuat(); // m_pPlayer->GetEntity()->GetWorldRotation(); Vec3 deltaMovement = worldRot.GetInverted().GetNormalized() * m_curInput.deltaMovement; // absolutely ensure length is correct deltaMovement = deltaMovement.GetNormalizedSafe(ZERO) * m_curInput.deltaMovement.GetLength(); moveRequest.AddDeltaMovement( deltaMovement ); if( IsDemoPlayback() ) { Vec3 localVDir(m_pPlayer->GetViewQuatFinal().GetInverted() * m_curInput.lookDirection); Ang3 deltaAngles(asinf(localVDir.z),0,atan2_tpl(-localVDir.x,localVDir.y)); moveRequest.AddDeltaRotation(deltaAngles*gEnv->pTimer->GetFrameTime()); } const float fNetAimLerpFactor = g_pGameCVars->pl_netAimLerpFactor; //Vector slerp produces artifacts here, using a per-component lerp instead Vec3 vCurrentRight = m_lookDir.cross(Vec3Constants<float>::fVec3_OneZ); Vec3 vCurrentProjected = -(vCurrentRight.cross(Vec3Constants<float>::fVec3_OneZ)); vCurrentRight.Normalize(); vCurrentProjected.Normalize(); Vec3 vNewRight = m_curInput.lookDirection.cross(Vec3Constants<float>::fVec3_OneZ); Vec3 vNewProjected = -(vNewRight.cross(Vec3Constants<float>::fVec3_OneZ)); vNewProjected.Normalize(); float fRotZDirDot = vNewProjected.dot(vCurrentRight); float fRotZDot = vNewProjected.dot(vCurrentProjected); float fRotZ = acos_tpl(fRotZDot); fRotZ = AngleWrap_PI(fRotZ); float fRotZFinal = -fsgnf(fRotZDirDot) * fRotZ * fNetAimLerpFactor; float fCurrentAngle = acos_tpl(Vec3Constants<float>::fVec3_OneZ.dot(m_lookDir)); float fNewAngle = acos_tpl(Vec3Constants<float>::fVec3_OneZ.dot(m_curInput.lookDirection)); float fRotXFinal = (fNewAngle - fCurrentAngle) * -fNetAimLerpFactor; //Rotate around X first, as we have already generated the right vector Vec3 vNewLookDir = m_lookDir.GetRotated(vCurrentRight, fRotXFinal); m_lookDir = vNewLookDir.GetRotated(Vec3Constants<float>::fVec3_OneZ, fRotZFinal); Vec3 distantTarget = moveState.eyePosition + 1000.0f * m_lookDir; Vec3 lookTarget = distantTarget; if (m_curInput.usinglookik) moveRequest.SetLookTarget( lookTarget ); else moveRequest.ClearLookTarget(); if (m_curInput.aiming) moveRequest.SetAimTarget( lookTarget ); else moveRequest.ClearAimTarget(); if (m_curInput.deltaMovement.GetLengthSquared() > sqr(0.02f)) // 0.2f is almost stopped moveRequest.SetBodyTarget( distantTarget ); else moveRequest.ClearBodyTarget(); moveRequest.SetAllowStrafing(m_curInput.allowStrafing); moveRequest.SetPseudoSpeed(CalculatePseudoSpeed()); moveRequest.SetStance( (EStance)m_curInput.stance ); m_pPlayer->GetMovementController()->RequestMovement(moveRequest); if (m_curInput.sprint) m_pPlayer->m_actions |= ACTION_SPRINT; else m_pPlayer->m_actions &= ~ACTION_SPRINT; #if 0 if (m_pPlayer->m_netSetPosition) { SPredictedCharacterStates charStates; charStates.states[0].position = m_pPlayer->GetEntity()->GetPos(); charStates.states[0].orientation = m_pPlayer->GetEntity()->GetRotation(); charStates.states[0].deltatime = 0.0f; charStates.states[1].position = m_pPlayer->m_netCurrentLocation; charStates.states[1].orientation = m_pPlayer->GetEntity()->GetRotation(); charStates.states[1].deltatime = gEnv->pTimer->GetFrameTime(); charStates.states[2].position = m_pPlayer->m_netDesiredLocation; charStates.states[2].orientation = m_pPlayer->GetEntity()->GetRotation(); charStates.states[2].deltatime = m_pPlayer->m_netLerpTime; charStates.nStates = 3; moveRequest.SetPrediction(charStates); } #endif //0 #if !defined(_RELEASE) // debug.. if (g_pGameCVars->g_debugNetPlayerInput & 2) { IPersistantDebug * pPD = gEnv->pGame->GetIGameFramework()->GetIPersistantDebug(); pPD->Begin( string("update_player_input_") + m_pPlayer->GetEntity()->GetName(), true ); Vec3 wp = m_pPlayer->GetEntity()->GetWorldPos(); wp.z += 2.0f; pPD->AddSphere( moveRequest.GetLookTarget(), 0.5f, ColorF(1,0,1,0.3f), 1.0f ); // pPD->AddSphere( moveRequest.GetMoveTarget(), 0.5f, ColorF(1,1,0,0.3f), 1.0f ); pPD->AddDirection( m_pPlayer->GetEntity()->GetWorldPos() + Vec3(0,0,2), 1, m_curInput.deltaMovement, ColorF(1,0,0,0.3f), 1.0f ); } #endif //m_curInput.deltaMovement.zero(); }