//------------------------------------------------------------------------ void CVehicleMovementStdBoat::UpdateRunSound(const float deltaTime) { Vec3 localAccel(ZERO); m_measureSpeedTimer+=deltaTime; if (m_measureSpeedTimer > 0.25f) { const Vec3& v = m_physStatus[k_mainThread].v; Vec3 accel = (v - m_lastMeasuredVel) * (1.f/m_measureSpeedTimer); localAccel = accel * m_physStatus[k_mainThread].q; m_lastMeasuredVel = v; m_measureSpeedTimer = 0.f; } if (m_pVehicle->IsProbablyDistant()) return; // rpm dropdown for waves if (m_rpmPitchDir != 0) { float speed = (m_rpmPitchDir > 0) ? 0.1f : -0.8f; // quick down, slow up m_waveSoundPitch += deltaTime * speed; if (m_waveSoundPitch < -m_waveSoundAmount) // dropdown amount { m_waveSoundPitch = -m_waveSoundAmount; m_rpmPitchDir = 1; } else if (m_waveSoundPitch > 0.f) { m_waveSoundPitch = 0.f; m_rpmPitchDir = 0; } } if (m_rpmPitchSpeed>0.f) { const float maxPedal = (!m_inWater) ? 1.f : Boosting() ? 0.8f : 0.7f; // pitch rpm with pedal float pedal = GetEnginePedal(); pedal = sgnnz(pedal)*max(ms_engineSoundIdleRatio, min(maxPedal, abs(pedal))); // clamp "pedal" to [0.2..0.7] range float delta = pedal - m_rpmScaleSgn; m_rpmScaleSgn = max(-1.f, min(1.f, m_rpmScaleSgn + sgn(delta)*min(abs(delta), m_rpmPitchSpeed*deltaTime))); // skip transition around 0 when on pedal (sounds more realistic) if (abs(GetEnginePedal()) > 0.001f && abs(delta) > 0.001f && sgn(m_rpmScaleSgn) != sgn(delta) && abs(m_rpmScaleSgn) <= 0.3f) m_rpmScaleSgn = sgn(delta)*0.3f; // for normal driving, rpm is clamped at max defined by sound dept m_rpmScale = abs(m_rpmScaleSgn); m_rpmScale = min(1.f, max(ms_engineSoundIdleRatio, m_rpmScale + m_waveSoundPitch)); m_pIEntityAudioProxy->SetRtpcValue(m_audioControlIDs[eSID_VehicleRPM], m_rpmScale); } }
////////////////////////////////////////////////////////////////////////// // NOTE: This function must be thread-safe. Before adding stuff contact MarcoC. void CVehicleMovementTank::ProcessMovement(const float deltaTime) { FUNCTION_PROFILER( gEnv->pSystem, PROFILE_GAME ); m_netActionSync.UpdateObject(this); CryAutoCriticalSection lk(m_lock); CVehicleMovementBase::ProcessMovement(deltaTime); if (!(m_actorId && m_isEnginePowered)) { IPhysicalEntity* pPhysics = GetPhysics(); if (m_latFriction != 1.3f) SetLatFriction(1.3f); if (m_axleFriction != m_axleFrictionMax) UpdateAxleFriction(0.f, false, deltaTime); m_action.bHandBrake = 1; m_action.pedal = 0; m_action.steer = 0; pPhysics->Action(&m_action, 1); return; } IPhysicalEntity* pPhysics = GetPhysics(); MARK_UNUSED m_action.clutch; Matrix34 worldTM( m_PhysPos.q ); worldTM.AddTranslation( m_PhysPos.pos ); const Matrix34 invWTM = worldTM.GetInvertedFast(); Vec3 localVel = invWTM.TransformVector(m_PhysDyn.v); Vec3 localW = invWTM.TransformVector(m_PhysDyn.w); float speed = m_PhysDyn.v.len(); float speedRatio = min(1.f, speed/m_maxSpeed); float actionPedal = abs(m_movementAction.power) > 0.001f ? m_movementAction.power : 0.f; // tank specific: // avoid steering input around 0.5 (ask Anton) float actionSteer = m_movementAction.rotateYaw; float absSteer = abs(actionSteer); float steerSpeed = (absSteer < 0.01f && abs(m_currSteer) > 0.01f) ? m_steerSpeedRelax : m_steerSpeed; if (steerSpeed == 0.f) { m_currSteer = (float)sgn(actionSteer); } else { if (m_movementAction.isAI) { m_currSteer = actionSteer; } else { m_currSteer += min(abs(actionSteer-m_currSteer), deltaTime*steerSpeed) * sgn(actionSteer-m_currSteer); } } Limit(m_currSteer, -m_steerLimit, m_steerLimit); if (abs(m_currSteer) > 0.0001f) { // if steering, apply full throttle to have enough turn power actionPedal = (float)sgn(actionPedal); if (actionPedal == 0.f) { // allow steering-on-teh-spot only above maxReverseSpeed (to avoid sudden reverse of controls) const float maxReverseSpeed = -1.5f; actionPedal = max(0.f, min(1.f, 1.f-(localVel.y/maxReverseSpeed))); // todo float steerLim = 0.8f; Limit(m_currSteer, -steerLim*m_steerLimit, steerLim*m_steerLimit); } } if (!pPhysics->GetStatus(&m_vehicleStatus)) return; int currGear = m_vehicleStatus.iCurGear - 1; // indexing for convenience: -1,0,1,2,.. UpdateAxleFriction(m_movementAction.power, true, deltaTime); UpdateSuspension(deltaTime); float absPedal = abs(actionPedal); // pedal ramping if (m_pedalSpeed == 0.f) m_currPedal = actionPedal; else { m_currPedal += deltaTime * m_pedalSpeed * sgn(actionPedal - m_currPedal); m_currPedal = clamp_tpl(m_currPedal, -absPedal, absPedal); } // only apply pedal after threshold is exceeded if (currGear == 0 && fabs_tpl(m_currPedal) < m_pedalThreshold) m_action.pedal = 0; else m_action.pedal = m_currPedal; // change pedal amount based on damages float damageMul = 0.0f; { if (m_movementAction.isAI) { damageMul = 1.0f - 0.30f * m_damage; m_action.pedal *= damageMul; } else { // request from Sten: damage shouldn't affect reversing so much. float effectiveDamage = m_damage; if(m_action.pedal < -0.1f) effectiveDamage = 0.4f * m_damage; m_action.pedal *= GetWheelCondition(); damageMul = 1.0f - 0.7f*effectiveDamage; m_action.pedal *= damageMul; } } // reverse steering value for backward driving float effSteer = m_currSteer * sgn(actionPedal); // update lateral friction float latSlipMinGoal = 0.f; float latFricMinGoal = m_latFricMin; if (abs(effSteer) > 0.01f && !m_movementAction.brake) { latSlipMinGoal = m_latSlipMin; // use steering friction, but not when countersteering if (sgn(effSteer) != sgn(localW.z)) latFricMinGoal = m_latFricMinSteer; } Interpolate(m_currentSlipMin, latSlipMinGoal, 3.f, deltaTime); if (latFricMinGoal < m_currentFricMin) m_currentFricMin = latFricMinGoal; else Interpolate(m_currentFricMin, latFricMinGoal, 3.f, deltaTime); float fractionSpeed = min(1.f, max(0.f, m_avgLateralSlip-m_currentSlipMin) / (m_latSlipMax-m_currentSlipMin)); float latFric = fractionSpeed * (m_latFricMax-m_currentFricMin) + m_currentFricMin; if ( m_movementAction.brake && m_movementAction.isAI ) { // it is natural for ai, apply differnt friction value while handbreaking latFric = m_latFricMax; } if (latFric != m_latFriction) { SetLatFriction(latFric); } const static float maxSteer = gf_PI/4.f; // fix maxsteer, shouldn't change m_action.steer = m_currSteer * maxSteer; if (m_steeringImpulseMin > 0.f && m_wheelContactsLeft != 0 && m_wheelContactsRight != 0) { const float maxW = 0.3f*gf_PI; float steer = abs(m_currSteer)>0.001f ? m_currSteer : 0.f; float desired = steer * maxW; float curr = -localW.z; float err = desired - curr; // err>0 means correction to right Limit(err, -maxW, maxW); if (abs(err) > 0.01f) { float amount = m_steeringImpulseMin + speedRatio*(m_steeringImpulseMax-m_steeringImpulseMin); // bigger correction for relaxing if (desired == 0.f || (desired*curr>0 && abs(desired)<abs(curr))) amount = m_steeringImpulseRelaxMin + speedRatio*(m_steeringImpulseRelaxMax-m_steeringImpulseRelaxMin); float corr = -err * amount * m_PhysDyn.mass * deltaTime; pe_action_impulse imp; imp.iApplyTime = 0; imp.angImpulse = worldTM.GetColumn2() * corr; pPhysics->Action(&imp, THREAD_SAFE); } } m_action.bHandBrake = (m_movementAction.brake) ? 1 : 0; if (currGear > 0 && m_vehicleStatus.iCurGear < m_currentGear) { // when shifted down, disengage clutch immediately to avoid power/speed dropdown m_action.clutch = 1.f; } pPhysics->Action(&m_action, 1); if (Boosting()) ApplyBoost(speed, 1.2f*m_maxSpeed*GetWheelCondition()*damageMul, m_boostStrength, deltaTime); if (m_wheelContacts <= 1 && speed > 5.f) { ApplyAirDamp(DEG2RAD(20.f), DEG2RAD(10.f), deltaTime, THREAD_SAFE); UpdateGravity(-9.81f * 1.4f); } if (m_netActionSync.PublishActions( CNetworkMovementStdWheeled(this) )) CHANGED_NETWORK_STATE(m_pVehicle, eEA_GameClientDynamic ); }
//------------------------------------------------------------------------ void CVehicleMovementHelicopter::ProcessActionsLift(float deltaTime) { Ang3 angles = m_pEntity->GetWorldAngles(); // const float& currentPitch = angles.x; const float ¤tRoll = angles.y; float boost = Boosting() ? m_boostMult : 1.0f; float workingUpDirZ = max(0.90f, min(1.0f, m_workingUpDir.z)); float pitchRatio = (workingUpDirZ - 0.95f) / 0.05f; float liftAction = 0.0f; if(pitchRatio > -0.0001f) liftAction = m_liftAction * sinf(pitchRatio * gf_PI * 0.5f); float fwdAction = 0.0f; if(pitchRatio < -0.0001f) fwdAction = m_liftAction * cosf(pitchRatio * gf_PI * 0.5f); if(m_liftAction < 0.0f && pitchRatio < 0.0f) { liftAction = m_liftAction * (-max(-1.0f, pitchRatio) * 0.5f); fwdAction = 0.0f; } m_noHoveringTimer = 0.0f; float gravity; pe_simulation_params paramsSim; if(GetPhysics()->GetParams(¶msSim)) gravity = abs(paramsSim.gravity.z); else gravity = 9.8f; // hovering force m_control.impulse += Vec3(m_workingUpDir.x, m_workingUpDir.y, min(1.0f, m_workingUpDir.z)) * gravity * (boost); m_control.impulse += m_workingUpDir * m_enginePower * gravity * liftAction * boost; // forward force m_control.impulse += Vec3(m_workingUpDir.x, m_workingUpDir.y, 0.0f) * gravity * m_enginePower * fwdAction; if(abs(m_desiredRoll) > 0.0001) { float side = 1.0f; if(currentRoll < 0.0f) side = -1.0f; else if(currentRoll > 0.0f) side = 1.0f; else side = 0.0f; m_control.impulse += m_currentLeftDir * m_enginePower * (m_liftAction + (m_desiredRoll * side)) * boost; } // force damping float horizDamp = m_velDamp; float vertDamp = 1.0f; if(Boosting()) horizDamp *= 0.25f; float turbulenceMult = 1.0f - min(m_turbulenceMultMax, m_turbulence); m_control.impulse.x -= m_PhysDyn.v.x * horizDamp; m_control.impulse.y -= m_PhysDyn.v.y * horizDamp; m_control.impulse.z -= m_PhysDyn.v.z * vertDamp; #if ENABLE_VEHICLE_DEBUG IActor *pActor = m_pActorSystem->GetActor(m_actorId); int profile = g_pGameCVars->v_profileMovement; if((profile == 1 && pActor && pActor->IsClient()) || profile == 2) { IRenderer *pRenderer = gEnv->pRenderer; float color[4] = {1,1,1,1}; Ang3 localAngles = m_pEntity->GetWorldAngles(); pRenderer->Draw2dLabel(5.0f, 350.0f, 1.5f, color, false, "pitchRatio: %f, liftAction: %f, fwdAction: %f", pitchRatio, liftAction, fwdAction); } #endif }
//------------------------------------------------------------------------ void CVehicleMovementHelicopter::ProcessActions(const float deltaTime) { FUNCTION_PROFILER(GetISystem(), PROFILE_GAME); UpdateDamages(deltaTime); UpdateEngine(deltaTime); m_velDamp = 0.0f; m_playerControls.ProcessActions(deltaTime); Limit(m_forwardAction, -1.0f, 1.0f); Limit(m_strafeAction, -1.0f, 1.0f); m_actionYaw = 0.0f; Matrix33 tm(m_PhysPos.q); Ang3 angles = Ang3::GetAnglesXYZ(tm); Vec3 worldPos = m_PhysPos.pos; // +ve pitch means nose up const float ¤tPitch = angles.x; // +ve roll means to the left const float ¤tRoll = angles.y; // +ve direction mean rotation anti-clockwise about the z axis - 0 means along y float currentDir = angles.z; float pitchDeg = RAD2DEG(currentPitch); if(m_maxPitchAngleMov != 0.0f && pitchDeg >= (m_maxPitchAngleMov * 0.5f)) { float mult = pitchDeg / (m_maxPitchAngleMov); if(mult > 1.0f && m_desiredPitch < 0.0f) { m_desiredPitch *= 0.0f; m_actionPitch *= 0.0f; m_desiredPitch += 0.5f * mult; } else if(m_desiredPitch < 0.0f) { m_desiredPitch *= (1.0f - mult); m_desiredPitch += 0.05f; } } else if(m_maxPitchAngleMov != 0.0f && pitchDeg <= (-m_maxPitchAngleMov * 0.5f)) { float mult = abs(pitchDeg) / (m_maxPitchAngleMov); if(mult > 1.0f && m_desiredPitch > 0.0f) { m_desiredPitch *= 0.0f; m_actionPitch *= 0.0f; m_desiredPitch += 0.5f * mult; } else if(m_desiredPitch > 0.0f) { m_desiredPitch *= (1.0f - mult); m_desiredPitch -= 0.05f; } } if(m_pInvertPitchVar->GetIVal() == 0) m_desiredPitch *= -1.0f; Vec3 currentVel = m_PhysDyn.v; Vec3 currentVel2D = currentVel; currentVel2D.z = 0.0f; if(currentRoll >= DEG2RAD(m_maxRollAngle * 0.5f) && m_desiredRoll > 0.001f) { float r = currentRoll / DEG2RAD(m_maxRollAngle); r = min(1.0f, r * 1.0f); r = 1.0f - r; m_desiredRoll *= r; m_desiredRoll = min(1.0f, m_desiredRoll); } else if(currentRoll <= DEG2RAD(-m_maxRollAngle * 0.5f) && m_desiredRoll < 0.001f) { float r = abs(currentRoll) / DEG2RAD(m_maxRollAngle); r = min(1.0f, r * 1.0f); r = 1.0f - r; m_desiredRoll *= r; m_desiredRoll = max(-1.0f, m_desiredRoll); } Vec3 currentFwdDir2D = m_currentFwdDir; currentFwdDir2D.z = 0.0f; currentFwdDir2D.NormalizeSafe(); Vec3 currentLeftDir2D(-currentFwdDir2D.y, currentFwdDir2D.x, 0.0f); currentVel2D.z = 0.0f; float currentHeight = worldPos.z; float currentFwdSpeed = currentVel.Dot(currentFwdDir2D); ProcessActions_AdjustActions(deltaTime); float inputMult = m_basicSpeedFraction; // desired things float turnDecreaseScale = m_yawDecreaseWithSpeed / (m_yawDecreaseWithSpeed + fabs(currentFwdSpeed)); Vec3 desired_vel2D = currentFwdDir2D * m_forwardAction * m_maxFwdSpeed * inputMult + currentLeftDir2D * m_strafeAction * m_maxLeftSpeed * inputMult; // calculate the angle changes Vec3 desiredVelChange2D = desired_vel2D - currentVel2D; float desiredTiltAngle = m_tiltPerVelDifference * desiredVelChange2D.GetLength(); Limit(desiredTiltAngle, -m_maxTiltAngle, m_maxTiltAngle); float goal = abs(m_desiredPitch) + abs(m_desiredRoll); goal *= 1.5f; Interpolate(m_playerAcceleration, goal, 0.25f, deltaTime); Limit(m_playerAcceleration, 0.0f, 5.0f); if(!iszero(m_desiredPitch)) { m_actionPitch -= m_desiredPitch * m_pitchInputConst; Limit(m_actionPitch, -m_maxYawRate, m_maxYawRate); } m_actionRoll += m_pitchActionPerTilt * m_desiredRoll * (m_playerAcceleration + 1.0f); Limit(m_actionRoll, -10.0f, 10.0f); Limit(m_actionPitch, -10.0f, 10.0f); float relaxRollTolerance = 0.0f; if(!iszero(m_turnAction) || abs(m_PhysDyn.w.z) > DEG2RAD(10.0f)) { m_actionYaw += -m_turnAction * m_yawInputConst * GetDamageMult(); float side = 0.0f; if(abs(m_turnAction) > 0.01f) side = min(1.0f, max(-1.0f, m_turnAction)); float roll = DEG2RAD(m_extraRollForTurn * side) - (currentRoll); m_actionRoll += max(0.0f, abs(roll)) * side * m_rollForTurnForce; float pitchComp = abs(currentPitch) / DEG2RAD(2.50f); if(pitchComp > 1.0f) roll *= pitchComp; roll *= max(1.0f, abs(m_PhysDyn.w.z)); m_actionRoll += roll; Limit(m_actionYaw, -m_maxYawRate, m_maxYawRate); } m_desiredDir = currentDir; m_lastDir = currentDir; float boost = Boosting() ? m_boostMult : 1.0f; if(m_pAltitudeLimitVar) { float altitudeLimit = m_pAltitudeLimitVar->GetFVal(); if(!iszero(altitudeLimit)) { float altitudeLowerOffset; if(m_pAltitudeLimitLowerOffsetVar) { float r = 1.0f - min(1.0f, max(0.0f, m_pAltitudeLimitLowerOffsetVar->GetFVal())); altitudeLowerOffset = r * altitudeLimit; } else altitudeLowerOffset = altitudeLimit; float mult = 1.0f; if(currentHeight >= altitudeLimit) { if(m_liftAction > 0.f) { mult = 0.0f; } } else if(currentHeight >= altitudeLowerOffset) { float zone = altitudeLimit - altitudeLowerOffset; mult = (altitudeLimit - currentHeight) / (zone); } m_liftAction *= mult; if(currentPitch > DEG2RAD(0.0f)) { if(m_forwardAction > 0.0f) m_forwardAction *= mult; if(m_actionPitch > 0.0f) { m_actionPitch *= mult; m_actionPitch += -currentPitch; } } m_desiredHeight = min(altitudeLowerOffset, currentHeight); } } else { m_desiredHeight = currentHeight; } ProcessActionsLift(deltaTime); if(m_pStabilizeVTOL) { float stabilizeTime = m_pStabilizeVTOL->GetFVal(); if(stabilizeTime > 0.0f) { if(m_relaxTimer < 6.0f) m_relaxTimer += deltaTime; else { float r = currentRoll - relaxRollTolerance; r = min(1.0f, max(-1.0f, r)); m_actionRoll += -r * m_relaxForce * (m_relaxTimer / 6.0f); } } } if(m_netActionSync.PublishActions(CNetworkMovementHelicopter(this))) CHANGED_NETWORK_STATE(m_pVehicle, eEA_GameClientDynamic); }
//------------------------------------------------------------------------ void CVehicleMovementHelicopter::Update(const float deltaTime) { FUNCTION_PROFILER(GetISystem(), PROFILE_GAME); CVehicleMovementBase::Update(deltaTime); CryAutoCriticalSection lk(m_lock); if(m_isTouchingGround) { m_timeOnTheGround += deltaTime; m_isTouchingGround = false; } else { m_timeOnTheGround = 0.0f; } // ai specific sound matter if(m_soundMasterVolume != m_vehicleVolume) { float vol = m_soundMasterVolume; if(m_vehicleVolume == 0) CVehicleMovementBase::SetSoundMasterVolume(m_vehicleVolume); else if(vol < m_vehicleVolume) { vol +=deltaTime; if(vol > m_vehicleVolume) vol = m_vehicleVolume; CVehicleMovementBase::SetSoundMasterVolume(vol); } else if(vol > m_vehicleVolume) { vol -=deltaTime; if(vol < m_vehicleVolume) vol = m_vehicleVolume; CVehicleMovementBase::SetSoundMasterVolume(vol); } } // update animation if(m_isEngineGoingOff) { if(m_enginePower > 0.0f) { UpdateEngine(deltaTime); } else { m_enginePower = 0.0f; } } SetAnimationSpeed(eVMA_Engine, (m_enginePower / m_enginePowerMax)); #if ENABLE_VEHICLE_DEBUG IActor *pActor = m_pActorSystem->GetActor(m_actorId); int profile = g_pGameCVars->v_profileMovement; if((profile == 1 && pActor && pActor->IsClient()) || profile == 2) { IRenderer *pRenderer = gEnv->pRenderer; float color[4] = {1,1,1,1}; Ang3 localAngles = m_pEntity->GetWorldAngles(); m_mass = m_statusDyn.mass; Vec3 &velocity = m_statusDyn.v; Vec3 &angVelocity = m_statusDyn.w; pRenderer->Draw2dLabel(5.0f, 0.0f, 2.0f, color, false, "Helicopter movement"); Vec3 i; i = m_control.impulse.GetNormalizedSafe(); pRenderer->Draw2dLabel(5.0f, 85.0f, 1.5f, color, false, "impulse: %f, %f, %f (%f, %f, %f)", m_control.impulse.x, m_control.impulse.y, m_control.impulse.z, i.x, i.y, i.z); pRenderer->Draw2dLabel(5.0f, 100.0f, 1.5f, color, false, "angImpulse: %f, %f, %f", m_control.angImpulse.x, m_control.angImpulse.y, m_control.angImpulse.z); i = velocity.GetNormalizedSafe(); pRenderer->Draw2dLabel(5.0f, 115.0f, 1.5f, color, false, "velocity: %f, %f, %f (%f) (%f, %f, %f)", velocity.x, velocity.y, velocity.z, velocity.GetLength(), i.x, i.y, i.z); pRenderer->Draw2dLabel(5.0f, 130.0f, 1.5f, color, false, "angular velocity: %f, %f, %f", RAD2DEG(angVelocity.x), RAD2DEG(angVelocity.y), RAD2DEG(angVelocity.z)); pRenderer->Draw2dLabel(5.0f, 160.0f, 1.5f, color, false, "angles: %f, %f, %f (%f, %f, %f)", RAD2DEG(localAngles.x), localAngles.y, localAngles.z, RAD2DEG(localAngles.x), RAD2DEG(localAngles.y), RAD2DEG(localAngles.z)); pRenderer->Draw2dLabel(5.0f, 175.0f, 1.5f, color, false, "m_rpmScale: %f, damage: %f, damageActual: %f, turbulence: %f", m_rpmScale, m_damage, m_damageActual, m_turbulence); pRenderer->Draw2dLabel(5.0f, 190.0f, 1.5f, color, false, "m_turnAction: %f, actionYaw: %f, targetRotation: %f, %f, %f", m_turnAction, m_actionYaw, RAD2DEG(m_rotateTarget.x), RAD2DEG(m_rotateTarget.y), RAD2DEG(m_rotateTarget.z)); pRenderer->Draw2dLabel(5.0f, 220.0f, 1.5f, color, false, "lift: %f, engineForce: %f, hoveringPower: %f, desiredHeight: %f, boost: %d, fwdAction: %f", m_liftAction, m_engineForce, m_hoveringPower, m_desiredHeight, Boosting(), m_forwardAction); pRenderer->Draw2dLabel(5.0f, 235.0f, 1.5f, color, false, "pitchAction: %f, rollAction: %f", m_actionPitch, m_actionRoll); pRenderer->Draw2dLabel(5.0f, 250.0f, 1.5f, color, false, "desiredPitch: %f, desiredRoll: %f", m_desiredPitch, m_desiredRoll); Vec3 direction = m_pEntity->GetWorldTM().GetColumn(1); pRenderer->Draw2dLabel(5.0f, 270.0f, 1.5f, color, false, "fwd direction: %.2f, %.2f, %.2f", direction.x, direction.y, direction.z); pRenderer->Draw2dLabel(5.0f, 285.0f, 1.5f, color, false, "workingUpDir: %.2f, %.2f, %.2f", m_workingUpDir.x, m_workingUpDir.y, m_workingUpDir.z); pRenderer->Draw2dLabel(5.0f, 300.0f, 1.5f, color, false, "accel: %f", m_playerAcceleration); } #endif }
//------------------------------------------------------------------------ void CVehicleMovementVTOL::ProcessActions(const float deltaTime) { FUNCTION_PROFILER( GetISystem(), PROFILE_GAME ); UpdateDamages(deltaTime); UpdateEngine(deltaTime); m_velDamp = 0.25f; m_playerControls.ProcessActions(deltaTime); Limit(m_forwardAction, -1.0f, 1.0f); Limit(m_strafeAction, -1.0f, 1.0f); m_actionYaw = 0.0f; Vec3 worldPos = m_pEntity->GetWorldPos(); IPhysicalEntity* pPhysics = GetPhysics(); // get the current state // roll pitch + yaw Matrix34 worldTM = m_pRotorPart ? m_pRotorPart->GetWorldTM() : m_pEntity->GetWorldTM(); // if (m_pRotorPart) // worldTM = m_pRotorPart->GetWorldTM(); // else // worldTM = m_pEntity->GetWorldTM(); Vec3 specialPos = worldTM.GetTranslation(); Ang3 angles = Ang3::GetAnglesXYZ(Matrix33(worldTM)); Matrix33 tm; tm.SetRotationXYZ((angles)); // +ve pitch means nose up const float& currentPitch = angles.x; // +ve roll means to the left const float& currentRoll = angles.y; // +ve direction mean rotation anti-clockwise about the z axis - 0 means along y float currentDir = angles.z; const float maxPitchAngle = 60.0f; float pitchDeg = RAD2DEG(currentPitch); if (pitchDeg >= (maxPitchAngle * 0.75f)) { float mult = pitchDeg / (maxPitchAngle); if (mult > 1.0f && m_desiredPitch < 0.0f) { m_desiredPitch *= 0.0f; m_actionPitch *= 0.0f; m_desiredPitch += 0.2f * mult; } else if (m_desiredPitch < 0.0f) { m_desiredPitch *= (1.0f - mult); m_desiredPitch += 0.05f; } } else if (pitchDeg <= (-maxPitchAngle * 0.75f)) { float mult = abs(pitchDeg) / (maxPitchAngle); if (mult > 1.0f && m_desiredPitch > 0.0f) { m_desiredPitch *= 0.0f; m_actionPitch *= 0.0f; m_desiredPitch += 0.2f * mult; } else if (m_desiredPitch > 0.0f) { m_desiredPitch *= (1.0f - mult); m_desiredPitch -= 0.05f; } } if (currentRoll >= DEG2RAD(m_maxRollAngle * 0.7f) && m_desiredRoll > 0.001f) { float r = currentRoll / DEG2RAD(m_maxRollAngle); r = min(1.0f, r * 1.0f); r = 1.0f - r; m_desiredRoll *= r; m_desiredRoll = min(1.0f, m_desiredRoll); } else if (currentRoll <= DEG2RAD(-m_maxRollAngle * 0.7f) && m_desiredRoll < 0.001f) { float r = abs(currentRoll) / DEG2RAD(m_maxRollAngle); r = min(1.0f, r * 1.0f); r = 1.0f - r; m_desiredRoll *= r; m_desiredRoll = max(-1.0f, m_desiredRoll); } Vec3 currentFwdDir2D = m_currentFwdDir; currentFwdDir2D.z = 0.0f; currentFwdDir2D.NormalizeSafe(); Vec3 currentLeftDir2D(-currentFwdDir2D.y, currentFwdDir2D.x, 0.0f); Vec3 currentVel = m_PhysDyn.v; Vec3 currentVel2D = currentVel; currentVel2D.z = 0.0f; float currentHeight = worldPos.z; float currentFwdSpeed = currentVel.Dot(currentFwdDir2D); ProcessActions_AdjustActions(deltaTime); float inputMult = m_basicSpeedFraction; // desired things float turnDecreaseScale = m_yawDecreaseWithSpeed / (m_yawDecreaseWithSpeed + fabs(currentFwdSpeed)); Vec3 desired_vel2D = currentFwdDir2D * m_forwardAction * m_maxFwdSpeed * inputMult + currentLeftDir2D * m_strafeAction * m_maxLeftSpeed * inputMult; // calculate the angle changes Vec3 desiredVelChange2D = desired_vel2D - currentVel2D; float desiredTiltAngle = m_tiltPerVelDifference * desiredVelChange2D.GetLength(); Limit(desiredTiltAngle, -m_maxTiltAngle, m_maxTiltAngle); float goal = abs(m_desiredPitch) + abs(m_desiredRoll); goal *= 1.5f; Interpolate(m_playerAcceleration, goal, 0.25f, deltaTime); Limit(m_playerAcceleration, 0.0f, 5.0f); //static float g_angleLift = 4.0f; if (abs(m_liftAction) > 0.001f && abs(m_forwardAction) < 0.001) { // float pitch = RAD2DEG(currentPitch); if (m_liftPitchAngle < 0.0f && m_liftAction > 0.0f) m_liftPitchAngle = 0.0f; else if (m_liftPitchAngle > 0.0f && m_liftAction < 0.0f) m_liftPitchAngle = 0.0f; Interpolate(m_liftPitchAngle, 1.25f * m_liftAction, 0.75f, deltaTime); if (m_liftPitchAngle < 1.0f && m_liftPitchAngle > -1.0f) m_desiredPitch += 0.05f * m_liftAction; } else if (m_liftAction < 0.001f && abs(m_liftPitchAngle) > 0.001) { Interpolate(m_liftPitchAngle, 0.0f, 1.0f, deltaTime); m_desiredPitch += 0.05f * -m_liftPitchAngle; } /* todo else if (m_liftAction < -0.001f) { m_desiredPitch += min(0.0f, (DEG2RAD(-5.0f) - currentPitch)) * 0.5f * m_liftAction; }*/ if (!iszero(m_desiredPitch)) { m_actionPitch -= m_desiredPitch * m_pitchInputConst; Limit(m_actionPitch, -m_maxYawRate, m_maxYawRate); } float rollAccel = 1.0f; if (abs(currentRoll + m_desiredRoll) < abs(currentRoll)) rollAccel *= 1.25f; m_actionRoll += m_pitchActionPerTilt * m_desiredRoll * rollAccel * (m_playerAcceleration + 1.0f); Limit(m_actionRoll, -10.0f, 10.0f); Limit(m_actionPitch, -10.0f, 10.0f); // roll as we turn if (!m_strafeAction) { m_actionYaw += m_yawPerRoll * currentRoll; } if (abs(m_strafeAction) > 0.001f) { float side = 0.0f; side = min(1.0f, max(-1.0f, m_strafeAction)); float roll = DEG2RAD(m_extraRollForTurn * 0.25f * side) - (currentRoll); m_actionRoll += max(0.0f, abs(roll)) * side * 1.0f; } float relaxRollTolerance = 0.0f; if (abs(m_turnAction) > 0.01f || abs(m_PhysDyn.w.z) > DEG2RAD(3.0f)) { m_actionYaw += -m_turnAction * m_yawInputConst * GetDamageMult(); float side = 0.0f; if (abs(m_turnAction) > 0.01f) side = min(1.0f, max(-1.0f, m_turnAction)); float roll = DEG2RAD(m_extraRollForTurn * side) - (currentRoll); m_actionRoll += max(0.0f, abs(roll)) * side * m_rollForTurnForce; roll *= max(1.0f, abs(m_PhysDyn.w.z)); m_actionRoll += roll; Limit(m_actionYaw, -m_maxYawRate, m_maxYawRate); } m_desiredDir = currentDir; m_lastDir = currentDir; float boost = Boosting() ? m_boostMult : 1.0f; float liftActionMax = 1.0f; if (m_pAltitudeLimitVar) { float altitudeLimit = m_pAltitudeLimitVar->GetFVal(); if (!iszero(altitudeLimit)) { float altitudeLowerOffset; if (m_pAltitudeLimitLowerOffsetVar) { float r = 1.0f - min(1.0f, max(0.0f, m_pAltitudeLimitLowerOffsetVar->GetFVal())); altitudeLowerOffset = r * altitudeLimit; } else altitudeLowerOffset = altitudeLimit; float mult = 1.0f; if (currentHeight >= altitudeLimit) { if (m_liftAction > 0.f) { mult = 0.0f; } } else if (currentHeight >= altitudeLowerOffset) { float zone = altitudeLimit - altitudeLowerOffset; mult = (altitudeLimit - currentHeight) / (zone); } m_liftAction *= mult; if (currentPitch > DEG2RAD(0.0f)) { if (m_forwardAction > 0.0f) m_forwardAction *= mult; if (m_actionPitch > 0.0f) { m_actionPitch *= mult; m_actionPitch += -currentPitch; } } m_desiredHeight = min(altitudeLowerOffset, currentHeight); } } else { m_desiredHeight = currentHeight; } if (abs(m_liftAction) > 0.001f) { m_liftAction = min(liftActionMax, max(-0.2f, m_liftAction)); m_hoveringPower = (m_powerInputConst * m_liftAction) * boost; m_noHoveringTimer = 0.0f; } else if (!m_isTouchingGround) { if (m_noHoveringTimer <= 0.0f) { float gravity; pe_simulation_params paramsSim; if (pPhysics->GetParams(¶msSim)) gravity = abs(paramsSim.gravity.z); else gravity = 9.2f; float upDirZ = m_workingUpDir.z; if (abs(m_forwardAction) > 0.01 && upDirZ > 0.0f) upDirZ = 1.0f; else if (upDirZ > 0.8f) upDirZ = 1.0f; float upPower = upDirZ; upPower -= min(1.0f, abs(m_forwardAction) * abs(angles.x)); float turbulenceMult = 1.0f - min(m_turbulenceMultMax, m_turbulence); Vec3& impulse = m_control.impulse; impulse += Vec3(0.0f, 0.0f, upPower) * gravity * turbulenceMult * GetDamageMult(); impulse.z -= m_PhysDyn.v.z * turbulenceMult; } else { m_noHoveringTimer -= deltaTime; } } if (m_pStabilizeVTOL) { float stabilizeTime = m_pStabilizeVTOL->GetFVal(); if (stabilizeTime > 0.0f) { if (m_relaxTimer < 6.0f) m_relaxTimer += deltaTime; else { float r = currentRoll - relaxRollTolerance; r = min(1.0f, max(-1.0f, r)); m_actionRoll += -r * m_relaxForce * (m_relaxTimer / 6.0f); } } } if (m_netActionSync.PublishActions( CNetworkMovementHelicopter(this) )) m_pVehicle->GetGameObject()->ChangedNetworkState(eEA_GameClientDynamic); }
//------------------------------------------------------------------------ void CVehicleMovementVTOL::PreProcessMovement(const float deltaTime) { CVehicleMovementHelicopter::PreProcessMovement(deltaTime); if (abs(m_forwardAction) > 0.0f && m_timeOnTheGround <= 0.01f) SetHorizontalMode(1.0f); else SetHorizontalMode(0.0f); if (m_forwardAction > 0.0f && m_timeOnTheGround <= 0.01f) { m_wingsTimer += deltaTime; m_wingsTimer = min(m_wingsTimer, m_timeUntilWingsRotate); } else { m_wingsTimer -= deltaTime * 0.65f; m_wingsTimer = max(m_wingsTimer, 0.0f); } Interpolate(m_wingsAnimTime, 1.0f - (m_wingsTimer / m_timeUntilWingsRotate), m_wingsSpeed, deltaTime); if (!m_isVTOLMovement) return; IPhysicalEntity* pPhysics = GetPhysics(); assert(pPhysics); float gravity; pe_simulation_params paramsSim; if (pPhysics->GetParams(¶msSim)) gravity = abs(paramsSim.gravity.z); else gravity = 9.2f; float vertical = 1.0f - m_horizontal; //m_engineForce = max(1.0f, gravity * vertical) * m_enginePower * max(0.25f, vertical); m_engineForce = 0.0f; m_engineForce += gravity * vertical * m_enginePower; m_engineForce += m_horizontal * m_enginePower; Matrix33 tm( m_PhysPos.q); Ang3 angles = Ang3::GetAnglesXYZ(tm); m_workingUpDir = m_engineUpDir; //Vec3(0.0f, 0.0f, 1.0f); m_workingUpDir += (vertical * m_rotorDiskTiltScale * Vec3(angles.y, -angles.x, 0.0f)); m_workingUpDir += (m_horizontal * m_rotorDiskTiltScale * Vec3(0.0f, 0.0f, angles.z)); m_workingUpDir = tm * m_workingUpDir; m_workingUpDir.z += 0.25f; m_workingUpDir.NormalizeSafe(); float strafe = m_strafeAction * m_strafeForce; if (m_noHoveringTimer <= 0.0f) { Vec3 forwardImpulse; float turbulenceMult = 1.0f - min(m_turbulenceMultMax, m_turbulence); forwardImpulse = m_currentFwdDir * m_enginePower * m_horizFwdForce * m_horizontal * (m_forwardAction + (Boosting() * m_boostForce)) * GetDamageMult() * turbulenceMult; if (m_forwardAction < 0.0f) forwardImpulse *= m_forwardInverseMult; forwardImpulse += m_currentUpDir * m_liftAction * m_enginePower * gravity; Vec3 fakeLeftDir = tm * Vec3(-1.0f, 0.0f, 0.0f); fakeLeftDir.z = 0.0f; forwardImpulse += fakeLeftDir * -strafe * m_enginePower * m_horizLeftForce * turbulenceMult; float horizDamp = 0.25f; static float vertDamp = 0.0f; if ( m_movementAction.isAI ) horizDamp *= abs(m_turnAction * 4.0f) + 1.0f; else horizDamp = m_velDamp; m_control.impulse += forwardImpulse; m_control.impulse.x -= m_PhysDyn.v.x * horizDamp * turbulenceMult; m_control.impulse.y -= m_PhysDyn.v.y * horizDamp * turbulenceMult; m_control.impulse.z -= m_PhysDyn.v.z * vertDamp * turbulenceMult; } m_workingUpDir.z += 0.45f * m_liftAction; m_workingUpDir.NormalizeSafe(); return; }
////////////////////////////////////////////////////////////////////////// // NOTE: This function must be thread-safe. Before adding stuff contact MarcoC. void CVehicleMovementStdBoat::ProcessMovement(const float deltaTime) { FUNCTION_PROFILER( GetISystem(), PROFILE_GAME ); static const float fWaterLevelMaxDiff = 0.15f; // max allowed height difference between propeller center and water level static const float fSubmergedMin = 0.01f; static const float fMinSpeedForTurn = 0.5f; // min speed so that turning becomes possible if (m_bNetSync) m_netActionSync.UpdateObject(this); CryAutoCriticalSection lk(m_lock); CVehicleMovementBase::ProcessMovement(deltaTime); IEntity* pEntity = m_pVehicle->GetEntity(); IPhysicalEntity* pPhysics = pEntity->GetPhysics(); SVehiclePhysicsStatus* physStatus = &m_physStatus[k_physicsThread]; assert(pPhysics); float frameTime = min(deltaTime, 0.1f); if (abs(m_movementAction.power) < 0.001f) m_movementAction.power = 0.f; if (abs(m_movementAction.rotateYaw) < 0.001f) m_movementAction.rotateYaw = 0.f; Matrix34 wTM( physStatus->q ); wTM.AddTranslation( physStatus->pos ); Matrix34 wTMInv = wTM.GetInvertedFast(); Vec3 localVel = wTMInv.TransformVector( physStatus->v ); Vec3 localW = wTMInv.TransformVector( physStatus->w ); const Vec3 xAxis = wTM.GetColumn0(); const Vec3 yAxis = wTM.GetColumn1(); const Vec3 zAxis = wTM.GetColumn2(); // check if propeller is in water Vec3 worldPropPos = wTM * m_pushOffset; float waterLevelWorld = gEnv->p3DEngine->GetWaterLevel( &worldPropPos ); float fWaterLevelDiff = worldPropPos.z - waterLevelWorld; bool submerged = physStatus->submergedFraction > fSubmergedMin; m_inWater = submerged && fWaterLevelDiff < fWaterLevelMaxDiff; float speed = physStatus->v.len2() > 0.001f ? physStatus->v.len() : 0.f; float speedRatio = min(1.f, speed/(m_maxSpeed*m_factorMaxSpeed)); float absPedal = abs(m_movementAction.power); float absSteer = abs(m_movementAction.rotateYaw); // wave stuff float waveFreq = 1.f; waveFreq += 3.f*speedRatio; float waveTimerPrev = m_waveTimer; m_waveTimer += frameTime*waveFreq; // new randomized amount for this oscillation if (m_waveTimer >= gf_PI && waveTimerPrev < gf_PI) m_waveRandomMult = cry_random(0.0f, 1.0f); if (m_waveTimer >= 2*gf_PI) m_waveTimer -= 2*gf_PI; float kx = m_waveIdleStrength.x*(m_waveRandomMult+0.3f) * (1.f-speedRatio + m_waveSpeedMult*speedRatio); float ky = m_waveIdleStrength.y * (1.f - 0.5f*absPedal - 0.5f*absSteer); Vec3 waveLoc = m_massOffset; waveLoc.y += speedRatio*min(0.f, m_pushOffset.y-m_massOffset.y); waveLoc = wTM * waveLoc; bool visible = m_pVehicle->GetGameObject()->IsProbablyVisible(); bool doWave = visible && submerged && physStatus->submergedFraction < 0.99f; if (doWave && !m_isEnginePowered) m_pVehicle->NeedsUpdate(IVehicle::eVUF_AwakePhysics); if (m_isEnginePowered || (visible && !m_pVehicle->IsProbablyDistant())) { if (doWave && (m_isEnginePowered || g_pGameCVars->v_rockBoats)) { pe_action_impulse waveImp; waveImp.angImpulse.x = Boosting() ? 0.f : sinf(m_waveTimer) * frameTime * m_Inertia.x * kx; if (isneg(waveImp.angImpulse.x)) waveImp.angImpulse.x *= (1.f - min(1.f, 2.f*speedRatio)); // less amplitude for negative impulse waveImp.angImpulse.y = sinf(m_waveTimer-0.5f*gf_PI) * frameTime * m_Inertia.y * ky; waveImp.angImpulse.z = 0.f; waveImp.angImpulse = wTM.TransformVector(waveImp.angImpulse); waveImp.point = waveLoc; if (!m_movementAction.isAI) pPhysics->Action(&waveImp, 1); } } // ~wave stuff if (!m_isEnginePowered) return; pe_action_impulse linearImp, angularImp, dampImp, liftImp; float turnAccel = 0, turnAccelNorm = 0; if (m_inWater) { // Lateral damping if (m_lateralDamping>0.f) { pe_action_impulse impulse; impulse.impulse = - physStatus->mass * xAxis * (localVel.x * (frameTime * m_lateralDamping)/(1.f + frameTime*m_lateralDamping)); pPhysics->Action(&impulse, 1); } // optional lifting (catamarans) if (m_velLift > 0.f) { if (localVel.y > m_velLift && !IsLifted()) Lift(true); else if (localVel.y < m_velLift && IsLifted()) Lift(false); } if (Boosting() && IsLifted()) { // additional lift force liftImp.impulse = Vec3(0,0,physStatus->mass*frameTime*(localVel.y/(m_velMax*m_factorMaxSpeed))*3.f); liftImp.point = wTM * m_massOffset; pPhysics->Action(&liftImp, 1); } // apply driving force float a = m_movementAction.power; if (sgn(a)*sgn(localVel.y) > 0) { // reduce acceleration with increasing speed float ratio = (localVel.y > 0.f) ? localVel.y/(m_velMax*m_factorMaxSpeed) : -localVel.y/(m_velMaxReverse*m_factorMaxSpeed); a = (ratio>1.f) ? 0.f : sgn(a)*min(abs(a), 1.f-((1.f-m_accelVelMax)*sqr(ratio))); } if (a != 0) { if (sgn(a) * sgn(localVel.y) < 0) // "braking" a *= m_accelCoeff; else a = max(a, -m_pedalLimitReverse); Vec3 pushDir(FORWARD_DIRECTION); // apply force downwards a bit for more realistic response if (a > 0) pushDir = Quat_tpl<float>::CreateRotationAA( DEG2RAD(m_pushTilt), Vec3(-1,0,0) ) * pushDir; pushDir = wTM.TransformVector( pushDir ); linearImp.impulse = pushDir * physStatus->mass * a * m_accel * m_factorAccel * frameTime; linearImp.point = m_pushOffset; linearImp.point.x = m_massOffset.x; linearImp.point = wTM * linearImp.point; pPhysics->Action(&linearImp, 1); } float roll = (float)__fsel(zAxis.z - 0.2f, xAxis.z / (frameTime + frameTime*frameTime), 0.f); // Roll damping (with a exp. time constant of 1 sec) // apply steering if (m_movementAction.rotateYaw != 0) { if (abs(localVel.y) < fMinSpeedForTurn){ // if forward speed too small, no turning possible turnAccel = 0; } else { int iDir = m_movementAction.power != 0.f ? sgn(m_movementAction.power) : sgn(localVel.y); turnAccelNorm = m_movementAction.rotateYaw * iDir * max(1.f, m_turnVelocityMult * speedRatio); // steering and current w in same direction? int sgnSteerW = sgn(m_movementAction.rotateYaw) * iDir * sgn(-localW.z); if (sgnSteerW < 0) { // "braking" turnAccelNorm *= m_turnAccelCoeff; } else { // reduce turn vel towards max float maxRatio = 1.f - 0.15f*min(1.f, abs(localW.z)/m_turnRateMax); turnAccelNorm = sgn(turnAccelNorm) * min(abs(turnAccelNorm), maxRatio); } turnAccel = turnAccelNorm * m_turnAccel; //roll = 0.2f * turnAccel; // slight roll } } // Use the centripetal acceleration to determine the amount of roll float centripetalAccel = clamp_tpl(speed * localW.z, -10.f, +10.f); roll -= (1.f - 2.f*fabsf(xAxis.z)) * m_rollAccel * centripetalAccel; // Always damp rotation! turnAccel += localW.z * m_turnDamping; if (turnAccel != 0) { Vec3& angImp = angularImp.angImpulse; angImp.x = 0.f; angImp.y = roll * frameTime * m_Inertia.y; angImp.z = -turnAccel * frameTime * m_Inertia.z; angImp = wTM.TransformVector( angImp ); pPhysics->Action(&angularImp, 1); } if (abs(localVel.x) > 0.01f) { // lateral force Vec3& cornerForce = dampImp.impulse; cornerForce.x = -localVel.x * m_cornerForceCoeff * physStatus->mass * frameTime; cornerForce.y = 0.f; cornerForce.z = 0.f; if (m_cornerTilt != 0) cornerForce = Quat_tpl<float>::CreateRotationAA( sgn(localVel.x)*DEG2RAD(m_cornerTilt), Vec3(0,1,0) ) * cornerForce; dampImp.impulse = wTM.TransformVector(cornerForce); dampImp.point = m_cornerOffset; dampImp.point.x = m_massOffset.x; dampImp.point = wTM.TransformPoint( dampImp.point ); pPhysics->Action(&dampImp, 1); } } EjectionTest(deltaTime); if (!m_pVehicle->GetStatus().doingNetPrediction) { if (m_bNetSync && m_netActionSync.PublishActions( CNetworkMovementStdBoat(this) )) CHANGED_NETWORK_STATE(m_pVehicle, CNetworkMovementStdBoat::CONTROLLED_ASPECT ); } }
//------------------------------------------------------------------------ void CVehicleMovementStdBoat::Update(const float deltaTime) { CVehicleMovementBase::Update(deltaTime); SetAnimationSpeed(eVMA_Engine, abs(m_rpmScaleSgn)); if (m_inWater) { SetSoundParam(eSID_Run, "slip", 0.2f*abs(m_localSpeed.x)); } #if ENABLE_VEHICLE_DEBUG if (IsProfilingMovement() && g_pGameCVars->v_profileMovement != 2) { IEntity* pEntity = m_pVehicle->GetEntity(); const Matrix34& wTM = pEntity->GetWorldTM(); Matrix34 wTMInv = wTM.GetInvertedFast(); const SVehiclePhysicsStatus* physStatus = &m_physStatus[k_mainThread]; Vec3 localW = physStatus->q * physStatus->w; float speed = physStatus->v.len2() > 0.001f ? physStatus->v.len() : 0.f; float speedRatio = min(1.f, speed/(m_maxSpeed*m_factorMaxSpeed)); float absPedal = abs(m_movementAction.power); float absSteer = abs(m_movementAction.rotateYaw); static const float fSubmergedMin = 0.01f; static const float fWaterLevelMaxDiff = 0.15f; // max allowed height difference between propeller center and water level Vec3 worldPropPos = wTM * m_pushOffset; float waterLevelWorld = gEnv->p3DEngine->GetWaterLevel( &worldPropPos ); float fWaterLevelDiff = worldPropPos.z - waterLevelWorld; // wave stuff float waveFreq = 1.f; waveFreq += 3.f*speedRatio; float kx = m_waveIdleStrength.x*(m_waveRandomMult+0.3f) * (1.f-speedRatio + m_waveSpeedMult*speedRatio); float ky = m_waveIdleStrength.y * (1.f - 0.5f*absPedal - 0.5f*absSteer); Vec3 waveLoc = m_massOffset; waveLoc.y += speedRatio*min(0.f, m_pushOffset.y-m_massOffset.y); waveLoc = wTM * waveLoc; IRenderer* pRenderer = gEnv->pRenderer; static float color[4] = {1,1,1,1}; float colorRed[4] = {1,0,0,1}; float colorGreen[4] = {0,1,0,1}; float y=50.f, step1=15.f, step2=20.f, size1=1.3f, size2=1.5f; pRenderer->Draw2dLabel(5.0f, y, size2, color, false, "Boat movement"); pRenderer->Draw2dLabel(5.0f, y+=step2, size1, color, false, "Speed: %.1f (%.1f km/h)", speed, speed*3.6f); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "LocalW.z norm: %.2f", abs(localW.z)/m_turnRateMax); if (m_velLift > 0.f) { pRenderer->Draw2dLabel(5.0f, y+=step2, size1, m_lifted ? colorGreen : color, false, m_lifted ? "Lifted" : "not lifted"); //pRenderer->Draw2dLabel(5.0f, y+=step2, size1, color, false, "Impulse lift: %.0f", liftImp.impulse.len()); } pRenderer->Draw2dLabel(5.0f, y+=step1, size1, physStatus->submergedFraction > fSubmergedMin ? color : colorRed, false, "Submerged: %.2f", physStatus->submergedFraction); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, fWaterLevelDiff < fWaterLevelMaxDiff ? color : colorRed, false, "WaterLevel: %.2f (max: %.2f)", fWaterLevelDiff, fWaterLevelMaxDiff); pRenderer->Draw2dLabel(5.0f, y+=step2, size2, color, false, "Driver input"); pRenderer->Draw2dLabel(5.0f, y+=step2, size1, color, false, "power: %.2f", m_movementAction.power); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "steer: %.2f", m_movementAction.rotateYaw); pRenderer->Draw2dLabel(5.0f, y+=step2, size2, color, false, "Propelling"); //pRenderer->Draw2dLabel(5.0f, y+=step2, size1, color, false, "turnAccel (norm/real): %.2f / %.2f", turnAccelNorm, turnAccel); //pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "Impulse acc: %.0f", linearImp.impulse.len()); //pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "Impulse steer/damp: %.0f", angularImp.angImpulse.len()); //pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "Impulse corner: %.0f", dampImp.impulse.len()); pRenderer->Draw2dLabel(5.0f, y+=step2, size2, color, false, "Waves"); pRenderer->Draw2dLabel(5.0f, y+=step2, size1, color, false, "timer: %.1f", m_waveTimer); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "frequency: %.2f", waveFreq); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "random: %.2f", m_waveRandomMult); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "kX: %.2f", kx); pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "kY: %.2f", ky); if (Boosting()) pRenderer->Draw2dLabel(5.0f, y+=step1, size1, color, false, "Boost: %.2f", m_boostCounter); IRenderAuxGeom* pGeom = pRenderer->GetIRenderAuxGeom(); ColorB colorB(0,255,0,255); pRenderer->DrawLabel(worldPropPos, 1.3f, "WL: %.2f", waterLevelWorld); pGeom->DrawSphere(worldPropPos, 0.15f, colorB); pGeom->DrawSphere(waveLoc, 0.25f, colorB); pGeom->DrawLine(waveLoc, colorB, waveLoc+Vec3(0,0,2), colorB); // impulses //DrawImpulse(linearImp, Vec3(0,0,1), 3.f/deltaTime, ColorB(255,0,0,255)); //DrawImpulse(angularImp, Vec3(0,0,1), 2.f/deltaTime, ColorB(128,0,0,255)); //DrawImpulse(liftImp, Vec3(0,0,6), 2.f/deltaTime, ColorB(0,0,255,255)); } #endif }
//------------------------------------------------------------------------ void CVehicleMovementStdBoat::UpdateRunSound(const float deltaTime) { Vec3 localAccel(ZERO); m_measureSpeedTimer+=deltaTime; if(m_measureSpeedTimer > 0.25f) { Vec3 accel = (m_statusDyn.v - m_lastMeasuredVel) * (1.f/m_measureSpeedTimer); Matrix33 worldTM(!m_statusPos.q); localAccel = worldTM * accel; m_lastMeasuredVel = m_statusDyn.v; m_measureSpeedTimer = 0.f; } if(m_pVehicle->IsProbablyDistant()) return; float soundSpeedRatio = ms_engineSoundIdleRatio + (1.f-ms_engineSoundIdleRatio) * m_speedRatio; SetSoundParam(eSID_Run, "speed", soundSpeedRatio); SetSoundParam(eSID_Ambience, "speed", soundSpeedRatio); //SetSoundParam(eSID_Run, "boost", Boosting() ? 1.f : 0.f); float acceleration = min(1.f, abs(localAccel.y) / m_accel*max(1.f, m_accelCoeff)); if(acceleration > 0.5f) { if(ISound *pSound = GetOrPlaySound(eSID_Acceleration, 2.f)) SetSoundParam(pSound, "acceleration", acceleration); } float damage = GetSoundDamage(); if(damage > 0.1f) { if(ISound *pSound = GetOrPlaySound(eSID_Damage, 5.f, m_enginePos)) SetSoundParam(pSound, "damage", damage); } // rpm dropdown for waves if(m_rpmPitchDir != 0) { float speed = (m_rpmPitchDir > 0) ? 0.1f : -0.8f; // quick down, slow up m_waveSoundPitch += deltaTime * speed; if(m_waveSoundPitch < -m_waveSoundAmount) // dropdown amount { m_waveSoundPitch = -m_waveSoundAmount; m_rpmPitchDir = 1; } else if(m_waveSoundPitch > 0.f) { m_waveSoundPitch = 0.f; m_rpmPitchDir = 0; } } if(m_rpmPitchSpeed>0.f) { const float maxPedal = (!m_inWater) ? 1.f : Boosting() ? 0.8f : 0.7f; // pitch rpm with pedal float pedal = GetEnginePedal(); pedal = sgnnz(pedal)*max(ms_engineSoundIdleRatio, min(maxPedal, abs(pedal))); // clamp "pedal" to [0.2..0.7] range float delta = pedal - m_rpmScaleSgn; m_rpmScaleSgn = max(-1.f, min(1.f, m_rpmScaleSgn + sgn(delta)*min(abs(delta), m_rpmPitchSpeed*deltaTime))); // skip transition around 0 when on pedal (sounds more realistic) if(abs(GetEnginePedal()) > 0.001f && abs(delta) > 0.001f && sgn(m_rpmScaleSgn) != sgn(delta) && abs(m_rpmScaleSgn) <= 0.3f) m_rpmScaleSgn = sgn(delta)*0.3f; // for normal driving, rpm is clamped at max defined by sound dept m_rpmScale = abs(m_rpmScaleSgn); m_rpmScale = min(1.f, max(ms_engineSoundIdleRatio, m_rpmScale + m_waveSoundPitch)); SetSoundParam(eSID_Run, "rpm_scale", m_rpmScale); SetSoundParam(eSID_Ambience, "rpm_scale", m_rpmScale); } }