//------------------------------------------------------------------------------------ // 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 CIronSight::UpdateDepthOfField(CActor *pActor, float frameTime, float t) { if(pActor) { CPlayer *pPlayer = static_cast<CPlayer *>(pActor); if(IMovementController *pMV = pActor->GetMovementController()) { SMovementState ms; pMV->GetMovementState(ms); Vec3 start = ms.eyePosition; Vec3 dir = ms.eyeDirection; static ray_hit hit; IPhysicalEntity *pSkipEntities[10]; int nSkip = CSingle::GetSkipEntities(m_pWeapon, pSkipEntities, 10); // jitter the direction (non-uniform disk sampling ... we want to bias the center in this case) f32 cosTheta, sinTheta; f32 theta = Random() * gf_PI2; f32 spreadAngle = DEG2RAD(g_pGameCVars->g_dof_sampleAngle)/2.0f; f32 scale = tan_tpl(spreadAngle); f32 radiusSqrt = scale * Random(); sincos_tpl(theta, &cosTheta, &sinTheta); f32 x = radiusSqrt * cosTheta; f32 y = radiusSqrt * sinTheta; Matrix33 viewRotation(pPlayer->GetViewQuatFinal()); Vec3 xOff = x * viewRotation.GetColumn0(); Vec3 yOff = y * viewRotation.GetColumn2(); // jitter if(true) { dir += xOff + yOff; dir.Normalize(); } const float maxRelaxSpeed = 1.0f; f32 delta; if(gEnv->pPhysicalWorld->RayWorldIntersection(start, 1000.0f*dir, ent_all, rwi_pierceability(10)|rwi_ignore_back_faces, &hit, 1, pSkipEntities, nSkip)) { delta = g_pGameCVars->g_dof_minHitScale*hit.dist - m_minDoF; Limit(delta, -g_pGameCVars->g_dof_minAdjustSpeed, g_pGameCVars->g_dof_minAdjustSpeed); //delta *= fabs(delta/minAdjustSpeed); m_minDoF += delta * frameTime; delta = g_pGameCVars->g_dof_maxHitScale*hit.dist - m_maxDoF; Limit(delta, -g_pGameCVars->g_dof_maxAdjustSpeed, g_pGameCVars->g_dof_maxAdjustSpeed); //delta *= fabs(delta/maxAdjustSpeed); m_maxDoF += delta * frameTime; } if(m_maxDoF - g_pGameCVars->g_dof_distAppart < m_minDoF) { m_maxDoF = m_minDoF + g_pGameCVars->g_dof_distAppart; } else { // relax max to min delta = m_minDoF - m_maxDoF; Limit(delta, -maxRelaxSpeed, maxRelaxSpeed); //delta *= fabs(delta/maxRelaxSpeed); m_maxDoF += delta * frameTime; } // the average is relaxed to the center between min and max m_averageDoF = (m_maxDoF - m_minDoF)/2.0f; Limit(delta, -g_pGameCVars->g_dof_averageAdjustSpeed, g_pGameCVars->g_dof_averageAdjustSpeed); //delta *= fabs(delta/averageAdjustSpeed); m_averageDoF += delta * frameTime; } } }
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); } }