void CDeflectorShield::ProcessProjectile(CProjectile* pProjectile, Vec3 hitPosition, Vec3 hitNormal, Vec3 hitDirection) { if (CanProjectilePassThroughShield(pProjectile)) { return; } const Matrix34& intoWorldTransform = GetEntity()->GetWorldTM(); Matrix34 intoLocalTransform = intoWorldTransform.GetInvertedFast(); SDeflectedEnergy deflectedEnergy; deflectedEnergy.m_delay = 0.0f; deflectedEnergy.m_localPosition = intoLocalTransform.TransformPoint(hitPosition); deflectedEnergy.m_localDirection = intoLocalTransform.TransformVector(-hitDirection); deflectedEnergy.m_damage = CLAMP(pProjectile->GetDamage(), m_minDamage, m_maxDamage); if (deflectedEnergy.m_localDirection.NormalizeSafe() > 0.0f) { m_deflectedEnergies.push_back(deflectedEnergy); GetGameObject()->EnableUpdateSlot(this, 0); } if (m_pDeflectedEffect) { m_pDeflectedEffect->Spawn(IParticleEffect::ParticleLoc(hitPosition, hitNormal)); } pProjectile->Destroy(); }
//------------------------------------------------------------------------ int CScriptBind_Vehicle::GetHelperDir(IFunctionHandler* pH, const char* name, bool isInVehicleSpace) { if (IVehicle *pVehicle = GetVehicle(pH)) { if (IVehicleHelper* pHelper = pVehicle->GetHelper(name)) { Matrix34 tm; if (isInVehicleSpace) pHelper->GetVehicleTM(tm); else tm = pHelper->GetLocalTM(); return pH->EndFunction(tm.TransformVector(FORWARD_DIRECTION)); } } return pH->EndFunction(Vec3(0.0f, 1.0f, 0.0f)); }
//------------------------------------------------------------------------ void CVehicleViewFirstPerson::Update(float frameTimeIn) { // Use the physics frame time, but only if non zero! const float physFrameTime = static_cast<CVehicle*>(m_pVehicle)->GetPhysicsFrameTime(); const float frameTime = (physFrameTime>0.f) ? min(physFrameTime,frameTimeIn) : frameTimeIn; CVehicleViewBase::Update(frameTime); if (m_frameSlot != -1 && m_pHelper) { Matrix34 tm; m_pHelper->GetVehicleTM(tm); tm = tm * m_invFrame; tm.SetTranslation(tm.GetTranslation() + tm.TransformVector(m_frameObjectOffset)); m_pVehicle->GetEntity()->SetSlotLocalTM(m_frameSlot, tm); } m_viewPosition = GetWorldPosGoal(); }
//--------------------------------------------------------------------- //This function is only executed on the server void CC4Projectile::StickToStaticObject(EventPhysCollision *pCollision, IPhysicalEntity *pTarget) { //Calculate new position and orientation Matrix34 mat; Vec3 pos = pCollision->pt+(pCollision->n*0.05f); mat.SetRotation33(Matrix33::CreateOrientation(-pCollision->n,GetEntity()->GetWorldTM().TransformVector(Vec3(0,0,1)),gf_PI)); Vec3 newUpDir = mat.TransformVector(Vec3(0,0,1)); pos += (newUpDir*-0.1f); mat.SetTranslation(pos+(newUpDir*-0.1f)); GetEntity()->SetWorldTM(mat); GetGameObject()->SetAspectProfile(eEA_Physics, ePT_Static); pos = mat.GetTranslation(); Quat rot = GetEntity()->GetWorldRotation(); if(gEnv->bMultiplayer) GetGameObject()->InvokeRMI(CC4Projectile::ClSetPosition(),ProjectileStaticParams(pos,rot),eRMI_ToAllClients); }
bool CCoherentInputEventListener::TraceMouse( int& outX, int& outY, CCoherentViewListener*& pViewListener ) { if ( gCoherentUISystem == nullptr ) { return false; } CCamera& camera = gEnv->pSystem->GetViewCamera(); int vpWidth = gEnv->pRenderer->GetWidth(); int vpHeight = gEnv->pRenderer->GetHeight(); float proj22 = 1.0f / cry_tanf( camera.GetFov() / 2.0f ); float proj11 = proj22 / camera.GetProjRatio(); float viewX = ( ( ( 2.0f * ( float )GetMouseX() ) / vpWidth ) - 1.0f ) / proj11; float viewY = ( ( ( -2.0f * ( float )GetMouseY() ) / vpHeight ) + 1.0f ) / proj22; Matrix34 invView = camera.GetMatrix(); Vec3 dir = invView.TransformVector( Vec3( viewX, 1.0f, viewY ) ); // Z is up Vec3 origin = camera.GetPosition(); return gCoherentUISystem->RaycastClosestViewListenersGeometry( origin, dir, outX, outY, pViewListener ); }
////////////////////////////////////////////////////////////////////////// // 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 ); }
virtual void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo ) { if (!pActInfo->pEntity) return; if (event == eFE_Activate && IsPortActive(pActInfo, IN_ACTIVATE)) { pe_action_impulse action; int ipart = GetPortInt( pActInfo, IN_PARTINDEX ); if (ipart>0) action.ipart = ipart-1; IEntity* pEntity = pActInfo->pEntity; ECoordSys coordSys = (ECoordSys)GetPortInt( pActInfo, IN_COORDSYS ); if (coordSys==CS_PARENT && !pEntity->GetParent()) coordSys = CS_WORLD; // When a "zero point" is set in the node, the value is left undefined and physics assume it is the CM of the object. // but when the entity has a parent (is linked), then we have to use a real world coordinate for the point, because we have to apply the impulse to the highest entity // on the hierarchy and physics will use the position of that entity instead of the position of the entity assigned to the node bool bHaveToUseTransformedZeroPoint = false; Vec3 transformedZeroPoint; Matrix34 transMat; switch (coordSys) { case CS_WORLD: default: { transMat.SetIdentity(); bHaveToUseTransformedZeroPoint = pEntity->GetParent()!=NULL; transformedZeroPoint = pEntity->GetWorldPos(); break; } case CS_PARENT: { transMat = pEntity->GetParent()->GetWorldTM(); bHaveToUseTransformedZeroPoint = pEntity->GetParent()->GetParent()!=NULL; transformedZeroPoint = pEntity->GetParent()->GetWorldPos(); break; } case CS_LOCAL: { transMat = pEntity->GetWorldTM(); bHaveToUseTransformedZeroPoint = pEntity->GetParent()!=NULL; transformedZeroPoint = pEntity->GetWorldPos(); break; } } action.impulse = GetPortVec3( pActInfo, IN_IMPULSE ); action.impulse = transMat.TransformVector( action.impulse ); Vec3 angImpulse = GetPortVec3( pActInfo, IN_ANGIMPULSE ); if (!angImpulse.IsZero()) action.angImpulse = transMat.TransformVector( angImpulse ); Vec3 pointApplication = GetPortVec3( pActInfo, IN_POINT ); if (!pointApplication.IsZero()) action.point = transMat.TransformPoint( pointApplication ); else { if (bHaveToUseTransformedZeroPoint) action.point = transformedZeroPoint; } // the impulse has to be applied to the highest entity in the hierarchy. This comes from how physics manage linked entities. IEntity* pEntityImpulse = pEntity; while (pEntityImpulse->GetParent()) { pEntityImpulse = pEntityImpulse->GetParent(); } IPhysicalEntity * pPhysEntity = pEntityImpulse->GetPhysics(); if (pPhysEntity) pPhysEntity->Action( &action ); } }
virtual void ProcessEvent(EFlowEvent event, SActivationInfo *pActInfo) { switch (event) { case eFE_Initialize:{ pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID,true); } case eFE_Activate: { pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID,true); } break; case eFE_Update: { //if (IsPortActive(pActInfo, PORT_IN_ENABLE)) //{ EntityId id = GetPortEntityId(pActInfo,EIP_EntityID); EntityId parentid = GetPortEntityId(pActInfo,EIP_ParentEntityID); //IGameObject *pGameObj = gEnv->pGameFramework->GetGameObject(id); IEntity * entity = gEnv->pEntitySystem->GetEntity(id); IEntity * parententity = gEnv->pEntitySystem->GetEntity(parentid); if(entity){ Matrix34 trans; // = entity->GetWorldTM(); trans.CreateIdentity(); Matrix34 transparent; transparent.CreateIdentity(); if (parententity){ //quatParent = parententity->GetRotation(); transparent = parententity->GetWorldTM(); //CryLogAlways("Parent : [%f,%f,%f]",transparent.GetColumn0().x,transparent.GetColumn0().y,transparent.GetColumn0().z); //CryLogAlways("Parent : [%f,%f,%f]",transparent.GetColumn1().x,transparent.GetColumn1().y,transparent.GetColumn1().z); //CryLogAlways("Parent : [%f,%f,%f]",transparent.GetColumn2().x,transparent.GetColumn2().y,transparent.GetColumn2().z); //CryLogAlways("Parent : [%f,%f,%f]",transparent.GetColumn3().x,transparent.GetColumn3().y,transparent.GetColumn3().z); } Quat quat = entity->GetRotation(); quat.v = GetPortVec3( pActInfo, EIP_Rotation_XYZ); quat.w = GetPortFloat( pActInfo, EIP_Rotation_W); quat.NormalizeFast(); Vec3 position = GetPortVec3(pActInfo, EIP_Position); trans.Set(Vec3(1,1,1),quat.GetNormalized(),position); //transparent.SetTranslation(Vec3(0,0,0)); Vec3 positionlocal = transparent.TransformVector(position); trans = transparent * trans; //Matrix34 transresult ; //transresult.CreateIdentity(); //transresult.Scale trans.SetTranslation(positionlocal+parententity->GetPos()); if(trans.IsValid()){ entity->SetWorldTM(trans); } else { //CryLogAlways("[%f,%f,%f]",trans.GetColumn0().x,trans.GetColumn0().y,trans.GetColumn0().z); //CryLogAlways("[%f,%f,%f]",trans.GetColumn1().x,trans.GetColumn1().y,trans.GetColumn1().z); //CryLogAlways("[%f,%f,%f]",trans.GetColumn2().x,trans.GetColumn2().y,trans.GetColumn2().z); //CryLogAlways("[%f,%f,%f]",trans.GetColumn3().x,trans.GetColumn3().y,trans.GetColumn3().z); //CryLogAlways("rot : [%f,%f,%f,%f]",trans.GetRo); //CryLogAlways("sca : [%f,%f,%f]",trans.GetColumn0().x,trans.GetColumn0().y,trans.GetColumn0().z); } } //CryLogAlways("%i",id); //} } } }
bool CCoherentInputEventListener::OnInputEvent( const SInputEvent& event ) { // Process special keys that toggle a function if ( event.deviceId == eDI_Keyboard && ( event.state & eIS_Released ) ) { switch ( event.keyId ) { case eKI_NP_0: m_PlayerInputEnabled = !m_PlayerInputEnabled; if (gEnv->pGame && gEnv->pGame->GetIGameFramework()) { gEnv->pGame->GetIGameFramework()->GetIActionMapManager()->EnableActionMap( "player", m_PlayerInputEnabled ); } return false; // NP_1 starts raycasting so don't use it case eKI_NP_2: m_DrawCoherentUI = !m_DrawCoherentUI; return false; case eKI_NP_3: m_DrawCursor = !m_DrawCursor; ::ShowCursor( m_DrawCursor ); if ( gEnv && gEnv->pSystem && gEnv->pSystem->GetIHardwareMouse() ) { if ( m_DrawCursor ) { gEnv->pSystem->GetIHardwareMouse()->IncrementCounter(); } else { gEnv->pSystem->GetIHardwareMouse()->DecrementCounter(); } } return false; case eKI_NP_4: { EntityId id = gEnv->pGame->GetIGameFramework()->GetClientActor()->GetGameObject()->GetWorldQuery()->GetLookAtEntityId(); IEntity* pEntityInFront = gEnv->pEntitySystem->GetEntity( id ); if ( pEntityInFront ) { OutputDebugString( pEntityInFront->GetName() ); } } return false; case eKI_NP_5: { CCamera& cam = gEnv->pSystem->GetViewCamera(); int vpWidth = gEnv->pRenderer->GetWidth(); int vpHeight = gEnv->pRenderer->GetHeight(); float proj22 = 1.0f / cry_tanf( cam.GetFov() / 2.0f ); float proj11 = proj22 / cam.GetProjRatio(); float viewX = ( ( ( 2.0f * ( float )GetMouseX() ) / vpWidth ) - 1.0f ) / proj11; float viewY = ( ( ( -2.0f * ( float )GetMouseY() ) / vpHeight ) + 1.0f ) / proj22; Matrix34 invView = cam.GetMatrix(); Vec3 dir = invView.TransformVector( Vec3( viewX, 1.0f, viewY ) ); // Z is up dir.Normalize(); dir *= 1000.0f; Vec3 origin = cam.GetPosition(); ray_hit hit; IPhysicalEntity* pSkipEnt = nullptr; int hitCount = gEnv->pPhysicalWorld->RayWorldIntersection( origin, dir, ent_sleeping_rigid | ent_rigid, rwi_stop_at_pierceable | rwi_colltype_any, &hit, 1, pSkipEnt ); if ( hitCount > 0 ) { IEntity* pEntity = gEnv->pEntitySystem->GetEntityFromPhysics( hit.pCollider ); OutputDebugString( pEntity->GetName() ); } } return false; case eKI_NP_6: { int dummyx, dummyy; CCoherentViewListener* pViewListener; TraceMouse( dummyx, dummyy, pViewListener ); } return false; } } // Send input to Coherent UI only when the player input is disabled if ( m_PlayerInputEnabled ) { return false; } if ( event.deviceId == eDI_Keyboard ) { if ( !m_pLastFocusedViewListener || !m_pLastFocusedViewListener->GetView() ) { return false; } Coherent::UI::KeyEventData keyEvent; if ( ToKeyEventData( event, keyEvent ) ) { // Enter needs to be sent as KeyDown to work if ( keyEvent.KeyCode == 13 ) { keyEvent.Type = Coherent::UI::KeyEventData::KeyDown; m_pLastFocusedViewListener->GetView()->KeyEvent( keyEvent ); keyEvent.Type = Coherent::UI::KeyEventData::Char; } m_pLastFocusedViewListener->GetView()->KeyEvent( keyEvent ); } } else if ( event.deviceId == eDI_Mouse ) { Coherent::UI::MouseEventData mouseEvent; int mouseX, mouseY; if ( ToMouseEvent( event, mouseEvent ) && TraceMouse( mouseX, mouseY, m_pLastFocusedViewListener ) ) { mouseEvent.X = mouseX; mouseEvent.Y = mouseY; if ( m_pLastFocusedViewListener->GetView() ) { m_pLastFocusedViewListener->GetView()->MouseEvent( mouseEvent ); } } } return false; }
////////////////////////////////////////////////////////////////////////// // 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::UpdateSurfaceEffects(const float deltaTime) { FUNCTION_PROFILER(GetISystem(), PROFILE_GAME); if(0 == g_pGameCVars->v_pa_surface) { ResetParticles(); return; } IEntity *pEntity = m_pVehicle->GetEntity(); const Matrix34 &worldTM = pEntity->GetWorldTM(); float distSq = worldTM.GetTranslation().GetSquaredDistance(gEnv->pRenderer->GetCamera().GetPosition()); if(distSq > sqr(300.f) || (distSq > sqr(50.f) && !m_pVehicle->GetGameObject()->IsProbablyVisible())) return; Matrix34 worldTMInv = worldTM.GetInverted(); const SVehicleStatus &status = m_pVehicle->GetStatus(); float velDot = status.vel * worldTM.GetColumn1(); float powerNorm = min(abs(m_movementAction.power), 1.f); SEnvironmentParticles *envParams = m_pPaParams->GetEnvironmentParticles(); SEnvParticleStatus::TEnvEmitters::iterator end = m_paStats.envStats.emitters.end(); for(SEnvParticleStatus::TEnvEmitters::iterator emitterIt = m_paStats.envStats.emitters.begin(); emitterIt!=end; ++emitterIt) { if(emitterIt->layer < 0) { assert(0); continue; } const SEnvironmentLayer &layer = envParams->GetLayer(emitterIt->layer); SEntitySlotInfo info; info.pParticleEmitter = 0; pEntity->GetSlotInfo(emitterIt->slot, info); float countScale = 1.f; float sizeScale = 1.f; float speedScale = 1.f; float speed = 0.f; // check if helper position is beneath water level Vec3 emitterWorldPos = worldTM * emitterIt->quatT.t; float waterLevel = gEnv->p3DEngine->GetWaterLevel(&emitterWorldPos); int matId = 0; if(emitterWorldPos.z <= waterLevel+0.1f && m_statusDyn.submergedFraction<0.999f) { matId = gEnv->pPhysicalWorld->GetWaterMat(); speed = status.speed; bool spray = !strcmp(layer.GetName(), "spray"); if(spray) { // slip based speed -= abs(velDot); } GetParticleScale(layer, speed, powerNorm, countScale, sizeScale, speedScale); } else { countScale = 0.f; } if(matId && matId != emitterIt->matId) { // change effect IParticleEffect *pEff = 0; const char *effect = GetEffectByIndex(matId, layer.GetName()); if(effect && (pEff = gEnv->pParticleManager->FindEffect(effect))) { #if ENABLE_VEHICLE_DEBUG if(DebugParticles()) CryLog("%s changes water sfx to %s (slot %i)", pEntity->GetName(), effect, emitterIt->slot); #endif if(info.pParticleEmitter) { info.pParticleEmitter->Activate(false); pEntity->FreeSlot(emitterIt->slot); } emitterIt->slot = pEntity->LoadParticleEmitter(emitterIt->slot, pEff); if(emitterIt->slot != -1) pEntity->SetSlotLocalTM(emitterIt->slot, Matrix34(emitterIt->quatT)); info.pParticleEmitter = 0; pEntity->GetSlotInfo(emitterIt->slot, info); } else countScale = 0.f; } if(matId) emitterIt->matId = matId; if(info.pParticleEmitter) { SpawnParams sp; sp.fSizeScale = sizeScale; sp.fCountScale = countScale; sp.fSpeedScale = speedScale; info.pParticleEmitter->SetSpawnParams(sp); if(layer.alignToWater && countScale > 0.f) { Vec3 worldPos(emitterWorldPos.x, emitterWorldPos.y, waterLevel+0.05f); Matrix34 localTM(emitterIt->quatT); localTM.SetTranslation(worldTMInv * worldPos); pEntity->SetSlotLocalTM(emitterIt->slot, localTM); } } #if ENABLE_VEHICLE_DEBUG if(DebugParticles() && m_pVehicle->IsPlayerDriving()) { float color[] = {1,1,1,1}; ColorB red(255,0,0,255); IRenderAuxGeom *pAuxGeom = gEnv->pRenderer->GetIRenderAuxGeom(); const char *effect = info.pParticleEmitter ? info.pParticleEmitter->GetName() : ""; const Matrix34 &slotTM = m_pEntity->GetSlotWorldTM(emitterIt->slot); Vec3 ppos = slotTM.GetTranslation(); pAuxGeom->DrawSphere(ppos, 0.2f, red); pAuxGeom->DrawCone(ppos, slotTM.GetColumn1(), 0.1f, 0.5f, red); gEnv->pRenderer->Draw2dLabel(50.f, (float)(400+10*emitterIt->slot), 1.2f, color, false, "<%s> water fx: slot %i [%s], speed %.1f, sizeScale %.2f, countScale %.2f (pos %.0f,%0.f,%0.f)", pEntity->GetName(), emitterIt->slot, effect, speed, sizeScale, countScale, ppos.x, ppos.y, ppos.z); } #endif } // generate water splashes Vec3 wakePos; if(m_pSplashPos) { wakePos = m_pSplashPos->GetWorldSpaceTranslation(); } else { wakePos = worldTM.GetTranslation(); } float wakeWaterLevel = gEnv->p3DEngine->GetWaterLevel(&wakePos); Vec3 localW = worldTMInv.TransformVector(m_statusDyn.w); if(localW.x >= 0.f) m_diving = false; if(!m_diving && localW.x < -0.03f && status.speed > 10.f && wakePos.z < m_lastWakePos.z && wakeWaterLevel+0.1f >= wakePos.z) { float speedRatio = min(1.f, status.speed/m_maxSpeed); m_diving = true; if(m_pWaveEffect) { if(IParticleEmitter *pEmitter = pEntity->GetParticleEmitter(m_wakeSlot)) { pEmitter->Activate(false); pEntity->FreeSlot(m_wakeSlot); m_wakeSlot = -1; } SpawnParams spawnParams; spawnParams.fSizeScale = spawnParams.fCountScale = 0.5f + 0.25f*speedRatio; spawnParams.fSizeScale += 0.4f*m_waveRandomMult; spawnParams.fCountScale += 0.4f*Random(); m_wakeSlot = pEntity->LoadParticleEmitter(m_wakeSlot, m_pWaveEffect, &spawnParams); } // handle splash sound PlaySound(eSID_Splash, 0.f, Vec3(0,5,1)); SetSoundParam(eSID_Splash, "intensity", 0.2f*speedRatio + 0.5f*m_waveRandomMult); if(m_rpmPitchDir == 0) { m_rpmPitchDir = -1; m_waveSoundPitch = 0.f; m_waveSoundAmount = 0.02f + m_waveRandomMult*0.08f; } } if(m_wakeSlot != -1) { // update emitter local pos to short above waterlevel Matrix34 tm; if(m_pSplashPos) m_pSplashPos->GetVehicleTM(tm); else tm.SetIdentity(); Vec3 pos = tm.GetTranslation(); pos.z = worldTMInv.TransformPoint(Vec3(wakePos.x,wakePos.y,wakeWaterLevel)).z + 0.2f; tm.SetTranslation(pos); pEntity->SetSlotLocalTM(m_wakeSlot, tm); #if ENABLE_VEHICLE_DEBUG if(IsProfilingMovement()) { Vec3 wPos = worldTM * tm.GetTranslation(); ColorB col(128, 128, 0, 200); gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(wPos, 0.4f, col); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(wPos, col, wPos+Vec3(0,0,1.5f), col); } #endif } m_lastWakePos = wakePos; }
//------------------------------------------------------------------------ void CVehicleMovementStdBoat::Update(const float deltaTime) { CVehicleMovementBase::Update(deltaTime); SetAnimationSpeed(eVMA_Engine, abs(m_rpmScaleSgn)); if(m_inWater) { IEntity *pEntity = m_pVehicle->GetEntity(); const Matrix34 &wTM = pEntity->GetWorldTM(); Matrix34 wTMInv = wTM.GetInvertedFast(); Vec3 localVel = wTMInv.TransformVector(m_statusDyn.v); SetSoundParam(eSID_Run, "slip", 0.2f*abs(localVel.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(); Vec3 localVel = wTMInv.TransformVector(m_statusDyn.v); Vec3 localW = wTMInv.TransformVector(m_statusDyn.w); float speed = m_statusDyn.v.len2() > 0.001f ? m_statusDyn.v.len() : 0.f; float speedRatio = min(1.f, speed/m_maxSpeed); 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, m_statusDyn.submergedFraction > fSubmergedMin ? color : colorRed, false, "Submerged: %.2f", m_statusDyn.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 }