inline bool i_QuickSphereTest2(const LTVector &pVector, const float fRadius) { // Find the closest point to the line. // Here's the equation for t: // P = point1 // V = point2 - point1 (ie: direction vector) // S = point you're testing // t = parametric (P + Vt) // t = -(-VS + VP) / VV //dirVec = *pPoint2 - *pPoint1; //t = pServerObj->m_Pos.Dot(dirVec) - dirVec.Dot(*pPoint1); //t /= dirVec.Dot(dirVec); float t = g_VTimesInvVV.Dot(pVector) - g_VPTimesInvVV; if (t < -fRadius || t > (g_LineLen + fRadius)) { return false; } // Now see if it's within range. LTVector vecTo = g_pCurQuery->m_From + g_V * t - pVector; return vecTo.MagSqr() < (fRadius * fRadius); }
void Body::FacePos(const LTVector& vTargetPos) { if (!g_pLTServer || !m_hObject) return; LTVector vPos; g_pLTServer->GetObjectPos(m_hObject, &vPos); LTVector vDir; VEC_SUB(vDir, vTargetPos, vPos); if ( vDir.MagSqr() == 0.0f ) { // Facing the same position... this would be a divide by zero case // when we normalize. So just return. return; } vDir.y = 0.0f; // Don't look up/down VEC_NORM(vDir); LTRotation rRot; LTVector temp(0,1,0); g_pMathLT->AlignRotation(rRot, vDir, temp); g_pLTServer->SetObjectRotation(m_hObject, &rRot); }
inline bool i_QuickSphereTest(const LTObject *pServerObj) { // Find the closest point to the line. // Here's the equation for t: // P = point1 // V = point2 - point1 (ie: direction vector) // S = point you're testing // t = parametric (P + Vt) // t = -(-VS + VP) / VV //dirVec = *pPoint2 - *pPoint1; //t = pServerObj->m_Pos.Dot(dirVec) - dirVec.Dot(*pPoint1); //t /= dirVec.Dot(dirVec); float t = g_VTimesInvVV.Dot(pServerObj->GetPos()) - g_VPTimesInvVV; //cache this radius since it is a virtual function call and can be somewhat expensive on some //object types float fRadius = pServerObj->GetRadius(); if (t < -fRadius || t > (g_LineLen + fRadius)) { return false; } // Now see if it's within range. LTVector vecTo = g_pCurQuery->m_From + g_V * t - pServerObj->GetPos(); return vecTo.MagSqr() < pServerObj->GetRadiusSquared(); }
bool ObjectDetector::TestParamsCylinder( ObjectDetectorLink* pLink, LTVector& vPos, LTVector& vDims, float& fMetRR ) { // Make sure we've got some valid parameters if( ( m_vParamsCylinder.x <= 0.0f ) || ( m_vParamsCylinder.y <= m_vParamsCylinder.x ) || ( m_vParamsCylinder.z <= 0.0f ) ) { fMetRR = 1.0f; return false; } // Get the object position in the space of the source LTVector vRelPos; vRelPos = ( vPos - m_tTransform.m_vPos ); vRelPos = ( ~m_tTransform.m_rRot ).RotateVector( vRelPos ); // Calculate the requirement range values float fHeightDelta = fabs( vRelPos.y ); vRelPos.y = 0.0f; float fMagSqr = vRelPos.MagSqr(); float fMagRR = ( ( fMagSqr - m_vParamsCylinder.x ) / ( m_vParamsCylinder.y - m_vParamsCylinder.x ) ); float fHeightRR = ( fHeightDelta / m_vParamsCylinder.z ); // Weigh the magnitude higher than the angles fMetRR = ( fMagRR * 0.5f ) + ( fHeightRR * 0.5f ); // Return true if it was contained by the cylinder return ( ( fMagSqr >= m_vParamsCylinder.x ) && ( fMagSqr <= m_vParamsCylinder.y ) && ( fHeightDelta <= m_vParamsCylinder.z ) ); }
void CWeaponFX::PlayBulletFlyBySound() { if (!m_pWeapon || !m_pAmmo) return; if (m_pAmmo->eType != VECTOR) return; // Camera pos HOBJECT hCamera = g_pGameClientShell->GetCamera(); LTVector vPos; g_pLTClient->GetObjectPos(hCamera, &vPos); // We only play the flyby sound if we won't hear an impact or // fire sound... LTVector vDist = m_vFirePos - vPos; if (vDist.Mag() < m_pWeapon->nFireSoundRadius) return; if (m_pAmmo->pImpactFX) { vDist = m_vPos - vPos; if (vDist.Mag() < m_pAmmo->pImpactFX->nSoundRadius) return; } // See if the camera is close enough to the bullet path to hear the // bullet... LTFLOAT fRadius = g_cvarFlyByRadius.GetFloat(); LTVector vDir = m_vDir; const LTVector vRelativePos = vPos - m_vFirePos; const LTFLOAT fRayDist = vDir.Dot(vRelativePos); LTVector vBulletDir = (vDir*fRayDist - vRelativePos); const LTFLOAT fDistSqr = vBulletDir.MagSqr(); if (fDistSqr < fRadius*fRadius) { vPos += vBulletDir; g_pClientSoundMgr->PlaySoundFromPos(vPos, "Guns\\Snd\\flyby.wav", g_cvarFlyBySoundRadius.GetFloat(), SOUNDPRIORITY_MISC_LOW); } }
//called when a sphere shape is encountered. This will provide the center of the sphere relative //to the transform heirarchy and the radius of that sphere virtual void HandleSphere(const LTVector& vCenter, float fRadius, float fMassKg, float fDensityG) { m_fTotalMassKg += fMassKg; float fVolume, fSurfaceArea; if(ApplySphereBuoyancy(vCenter, fRadius, m_SurfacePlane, fVolume, fSurfaceArea)) { m_fSurfaceArea += fSurfaceArea; float fWaterDensity = CalcSphereWaterDensity(m_fDensity, fRadius, fMassKg, fDensityG); LTVector vForce = CalcBuoyancyForceVector(m_vGravity, fWaterDensity, fVolume); if( LTIsNaN( vForce ) || vForce.MagSqr() > 1000000.0f * 1000000.0f ) { LTERROR( "Invalid force detected." ); vForce.Init( 0.0f, 10.0f, 0.0f ); } g_pLTBase->PhysicsSim()->ApplyRigidBodyForceWorldSpace(m_hRigidBody, vCenter, vForce); } }
bool ObjectDetector::TestParamsSphere( ObjectDetectorLink* pLink, LTVector& vPos, LTVector& vDims, float& fMetRR ) { // Make sure we've got some valid parameters if( ( m_vParamsSphere.x <= 0.0f ) || ( m_vParamsSphere.y <= m_vParamsSphere.x ) ) { fMetRR = 1.0f; return false; } // Get the object position in the space of the source LTVector vRelPos; vRelPos = ( vPos - m_tTransform.m_vPos ); // Calculate the magnitude and requirement range float fMagSqr = vRelPos.MagSqr(); fMetRR = ( ( fMagSqr - m_vParamsSphere.x ) / ( m_vParamsSphere.y - m_vParamsSphere.x ) ); return ( ( fMagSqr >= m_vParamsSphere.x ) && ( fMagSqr <= m_vParamsSphere.y ) ); }
//called when an OBB shape is encountered. This will provide the transform of the OBB relative to //the transform heirarchy and the half dimensions of the OBB virtual void HandleOBB(const LTRigidTransform& tTransform, const LTVector& vHalfDims, float fMassKg, float fDensityG) { m_fTotalMassKg += fMassKg; float fVolume, fSurfaceArea; LTVector vApplyAt; if(ApplyOBBBuoyancy(tTransform, vHalfDims, m_SurfacePlane, fVolume, vApplyAt, fSurfaceArea)) { m_fSurfaceArea += fSurfaceArea; float fWaterDensity = CalcOBBWaterDensity(m_fDensity, vHalfDims, fMassKg, fDensityG); LTVector vForce = CalcBuoyancyForceVector(m_vGravity, fWaterDensity, fVolume); if( LTIsNaN( vForce ) || vForce.MagSqr() > 1000000.0f * 1000000.0f ) { LTERROR( "Invalid force detected." ); vForce.Init( 0.0f, 10.0f, 0.0f ); } g_pLTBase->PhysicsSim()->ApplyRigidBodyForceWorldSpace(m_hRigidBody, vApplyAt, vForce); } }
void CClientMgr::MoveObject(LTObject *pObject, const LTVector *pNewPos, bool bForce) { LTVector vDiff; if (!bForce) { vDiff = pObject->GetPos() - *pNewPos; if (vDiff.MagSqr() < 0.001f) return; } pObject->SetPos(*pNewPos); // Do special stuff if it's a WorldModel. if (pObject->HasWorldModel()) { RetransformWorldModel(pObject->ToWorldModel()); } world_bsp_client->ClientTree()->InsertObject(pObject); }
bool ObjectDetector::TestParamsFOV( ObjectDetectorLink* pLink, LTVector& vPos, LTVector& vDims, float& fMetRR ) { // Make sure we've got some valid parameters if( ( m_vParamsFOV.z < 0.0f ) || ( m_vParamsFOV.w <= m_vParamsFOV.z ) ) { fMetRR = 1.0f; return false; } // Get the object position in the space of the source LTVector vRelPos; vRelPos = ( vPos - m_tTransform.m_vPos ); vRelPos = ( ~m_tTransform.m_rRot ).RotateVector( vRelPos ); // Get the normalized axis vectors flat along the FOV LTVector vForward( 0.0f, 0.0f, 1.0f ); LTVector vFlatX( vRelPos.x, 0.0f, vRelPos.z ); LTVector vFlatY( 0.0f, vRelPos.y, vRelPos.z ); vFlatX.Normalize(); vFlatY.Normalize(); // Check the range float fMagSqr = vRelPos.MagSqr(); float fXCos = vForward.Dot( vFlatX ); float fYCos = vForward.Dot( vFlatY ); // Calculate the requirement range values float fMagRR = ( ( fMagSqr - m_vParamsFOV.z ) / ( m_vParamsFOV.w - m_vParamsFOV.z ) ); float fFOVXRR = ( ( fXCos - 1.0f ) / ( m_vParamsFOV.x - 1.0f ) ); float fFOVYRR = ( ( fYCos - 1.0f ) / ( m_vParamsFOV.y - 1.0f ) ); // Apply the weights to the various parameters fMetRR = ( fMagRR * m_vParamsFOVWeights.x ) + ( fFOVXRR * m_vParamsFOVWeights.y ) + ( fFOVYRR * m_vParamsFOVWeights.z ); // Return true if it was contained by the FOV return ( ( fMagSqr >= m_vParamsFOV.z ) && ( fMagSqr < m_vParamsFOV.w ) && ( fXCos >= m_vParamsFOV.x ) && ( fYCos >= m_vParamsFOV.y ) ); }
//called when a capsule shape is encounered. This will provide the two end points of the capsule //relative to the transform heirarchy and the radius of the capsule virtual void HandleCapsule(const LTVector& vPt1, const LTVector& vPt2, float fRadius, float fMassKg, float fDensityG) { m_fTotalMassKg += fMassKg; float fVolume, fSurfaceArea; LTVector vApplyAt; //determine the lenght of the capsule axis float fLength = vPt1.Dist(vPt2); if(ApplyCapsuleBuoyancy(vPt1, vPt2, fLength, fRadius, m_SurfacePlane, fVolume, vApplyAt, fSurfaceArea)) { m_fSurfaceArea += fSurfaceArea; float fWaterDensity = CalcCapsuleWaterDensity(m_fDensity, fRadius, fLength, fMassKg, fDensityG); LTVector vForce = CalcBuoyancyForceVector(m_vGravity, fWaterDensity, fVolume); if( LTIsNaN( vForce ) || vForce.MagSqr() > 1000000.0f * 1000000.0f ) { LTERROR( "Invalid force detected." ); vForce.Init( 0.0f, 10.0f, 0.0f ); } g_pLTBase->PhysicsSim()->ApplyRigidBodyForceWorldSpace(m_hRigidBody, vApplyAt, vForce); } }
bool i_IntersectSegment(IntersectQuery *pQuery, IntersectInfo *pInfo, WorldTree *pWorldTree) { float InvVV, VP, testMag; ++g_nIntersectCalls; CountAdder cTicks_Intersect(&g_Ticks_Intersect); // Init.. g_pCurQuery = pQuery; g_pIntersection = LTNULL; g_pWorldIntersection = LTNULL; g_hWorldPoly = INVALID_HPOLY; g_hModelNode = INVALID_MODEL_NODE; g_bProcessNonSolid = !(pQuery->m_Flags & IGNORE_NONSOLID); g_bProcessObjects = !!(pQuery->m_Flags & INTERSECT_OBJECTS); g_bCheckIfFromPointIsInsideObject = !!(pQuery->m_Flags & CHECK_FROM_POINT_INSIDE_OBJECTS); g_bProcessModelObbs = !!(pQuery->m_Flags & INTERSECT_MODELOBBS); g_IntersectionBestDistSqr = (pQuery->m_From - pQuery->m_To).MagSqr() + 1.0f; // Precalculate stuff to totally accelerate i_QuickSphereTest. g_VOrigin = pQuery->m_From; g_V = pQuery->m_To - pQuery->m_From; // Calc Direction g_VDir = g_V.Unit(); g_LineLen = g_V.Mag(); g_V /= g_LineLen; // Was it too short? testMag = g_V.MagSqr(); if (testMag < 0.5f || testMag > 2.0f) { return false; } VP = g_V.Dot(pQuery->m_From); InvVV = 1.0f / g_V.MagSqr(); g_VTimesInvVV = g_V * InvVV; g_VPTimesInvVV = VP * InvVV; if (pQuery->m_Flags & INTERSECT_HPOLY) { g_FindIntersectionsFn = i_FindIntersectionsHPoly; } else { g_FindIntersectionsFn = i_FindIntersections; } // Start at the world tree. pWorldTree->IntersectSegment((LTVector*)&pQuery->m_From, (LTVector*)&pQuery->m_To, i_ISCallback, LTNULL); // If an object was hit, use it! if (g_pIntersection) { pInfo->m_Point = g_IntersectionPos; pInfo->m_Plane = g_IntersectionPlane; pInfo->m_hObject = (HOBJECT)g_pIntersection; pInfo->m_hPoly = g_hWorldPoly; pInfo->m_hNode = g_hModelNode; if (g_pWorldIntersection) { pInfo->m_SurfaceFlags = g_pWorldIntersection->m_pPoly->GetSurface()->m_TextureFlags; } else { pInfo->m_SurfaceFlags = 0; } return true; } else { return false; } }
// Wrap the textures, starting at a poly index void CRVTrackerTextureWrap::WrapTexture(CTWPolyInfo *pPoly, const CVector &vWrapDir, CTextExtents &cExtents) const { // Mark this poly as wrapped pPoly->m_bTouched = TRUE; CTexturedPlane& Texture = pPoly->m_pPoly->GetTexture(GetCurrTexture()); // Get the texture space LTVector vWrapO = Texture.GetO(); LTVector vWrapP = Texture.GetP(); LTVector vWrapQ = Texture.GetQ(); // Get the texture offset projections float fWrapOdotP = vWrapO.Dot(vWrapP); float fWrapOdotQ = vWrapO.Dot(vWrapQ); // Update the texturing extents for (uint32 nExtentLoop = 0; nExtentLoop < pPoly->m_aEdges.GetSize(); ++nExtentLoop) { LTVector vEdgePt = pPoly->m_aEdges[nExtentLoop]->m_aPt[0]; float fCurU = vWrapP.Dot(vEdgePt) - fWrapOdotP; float fCurV = vWrapQ.Dot(vEdgePt) - fWrapOdotQ; cExtents.m_fMinU = LTMIN(fCurU, cExtents.m_fMinU); cExtents.m_fMaxU = LTMAX(fCurU, cExtents.m_fMaxU); cExtents.m_fMinV = LTMIN(fCurV, cExtents.m_fMinV); cExtents.m_fMaxV = LTMAX(fCurV, cExtents.m_fMaxV); } CMoArray<uint32> aNeighbors; CMoArray<float> aDots; // Insert the neighbors into a list in dot-product order for (uint32 nNeighborLoop = 0; nNeighborLoop < pPoly->m_aNeighbors.GetSize(); ++nNeighborLoop) { CTWPolyInfo *pNeighbor = pPoly->m_aNeighbors[nNeighborLoop]; // Skip edges that don't have a neighbor if (!pNeighbor) continue; // Skip neighbors that are already wrapped if (pNeighbor->m_bTouched) continue; // Get our dot product float fCurDot = vWrapDir.Dot(pPoly->m_aEdges[nNeighborLoop]->m_Plane.m_Normal); if ((m_bRestrictWalkDir) && (fCurDot < 0.707f)) continue; // Mark this neighbor as touched (to avoid later polygons pushing it onto the stack) pNeighbor->m_bTouched = TRUE; // Insert it into the list for (uint32 nInsertLoop = 0; nInsertLoop < aNeighbors.GetSize(); ++nInsertLoop) { if (fCurDot > aDots[nInsertLoop]) break; } aDots.Insert(nInsertLoop, fCurDot); aNeighbors.Insert(nInsertLoop, nNeighborLoop); } // Recurse through its neighbors for (uint32 nWrapLoop = 0; nWrapLoop < aNeighbors.GetSize(); ++nWrapLoop) { CTWPolyInfo *pNeighbor = pPoly->m_aNeighbors[aNeighbors[nWrapLoop]]; CTWEdgeInfo *pEdge = pPoly->m_aEdges[aNeighbors[nWrapLoop]]; ////////////////////////////////////////////////////////////////////////////// // Wrap this neighbor // Create a matrix representing the basis of the polygon in relation to this edge LTMatrix mPolyBasis; mPolyBasis.SetTranslation(0.0f, 0.0f, 0.0f); mPolyBasis.SetBasisVectors(&pEdge->m_vDir, &pPoly->m_pPoly->m_Plane.m_Normal, &pEdge->m_Plane.m_Normal); // Create a new basis for the neighbor polygon LTMatrix mNeighborBasis; LTVector vNeighborForward; vNeighborForward = pNeighbor->m_pPoly->m_Plane.m_Normal.Cross(pEdge->m_vDir); // Just to be sure.. vNeighborForward.Norm(); mNeighborBasis.SetTranslation(0.0f, 0.0f, 0.0f); mNeighborBasis.SetBasisVectors(&pEdge->m_vDir, &pNeighbor->m_pPoly->m_Plane.m_Normal, &vNeighborForward); // Create a rotation matrix from here to there LTMatrix mRotation; mRotation = mNeighborBasis * ~mPolyBasis; // Rotate the various vectors LTVector vNewP; LTVector vNewQ; LTVector vNewDir; mRotation.Apply3x3(vWrapP, vNewP); mRotation.Apply3x3(vWrapQ, vNewQ); mRotation.Apply3x3(vWrapDir, vNewDir); // Rotate the texture basis if we're following a path if (m_nWrapStyle == k_WrapPath) { LTVector vNeighborEdgeDir; if (GetSimilarEdgeDir(pNeighbor, vNewDir, vNeighborEdgeDir, 0.707f)) { LTMatrix mRotatedNeighbor; LTVector vNeighborRight; vNeighborRight = vNeighborEdgeDir.Cross(pNeighbor->m_pPoly->m_Plane.m_Normal); vNeighborRight.Norm(); // Make sure we're pointing the right way... if (vNeighborRight.Dot(pEdge->m_vDir) < 0.0f) vNeighborRight = -vNeighborRight; mRotatedNeighbor.SetTranslation(0.0f, 0.0f, 0.0f); mRotatedNeighbor.SetBasisVectors(&vNeighborRight, &pNeighbor->m_pPoly->m_Plane.m_Normal, &vNeighborEdgeDir); // Build a basis based on an edge from the current polygon LTVector vBestPolyEdge; GetSimilarEdgeDir(pPoly, vWrapDir, vBestPolyEdge); LTVector vPolyRight = vBestPolyEdge.Cross(pNeighbor->m_pPoly->m_Plane.m_Normal); vPolyRight.Norm(); // Make sure we're pointing the right way... if (vPolyRight.Dot(pEdge->m_vDir) < 0.0f) vPolyRight = -vPolyRight; // Build the poly edge matrix LTMatrix mPolyEdgeBasis; mPolyEdgeBasis.SetTranslation(0.0f, 0.0f, 0.0f); mPolyEdgeBasis.SetBasisVectors(&vPolyRight, &pNeighbor->m_pPoly->m_Plane.m_Normal, &vBestPolyEdge); // Get a matrix from here to there LTMatrix mRotator; mRotator = mRotatedNeighbor * ~mPolyEdgeBasis; // Rotate the texture basis mRotator.Apply3x3(vNewP); mRotator.Apply3x3(vNewQ); // And use the new edge as the new direction vNewDir = vNeighborEdgeDir; } // Remove skew from vNewP/vNewQ if ((float)fabs(vNewP.Dot(vNewQ)) > 0.001f) { float fMagP = vNewP.Mag(); float fMagQ = vNewQ.Mag(); vNewQ *= 1.0f / fMagQ; vNewP -= vNewQ * vNewQ.Dot(vNewP); vNewP.Norm(fMagP); vNewQ *= fMagQ; } } // Get the first edge point.. CVector vEdgePt = pEdge->m_aPt[0]; // Calculate the texture coordinate at this point float fWrapU = vWrapP.Dot(vEdgePt) - fWrapOdotP; float fWrapV = vWrapQ.Dot(vEdgePt) - fWrapOdotQ; // Build the new offset float fNewOdotP = vNewP.Dot(vEdgePt) - fWrapU; float fNewOdotQ = vNewQ.Dot(vEdgePt) - fWrapV; LTVector vNewO; vNewO.Init(); float fNewPMag = vNewP.MagSqr(); if (fNewPMag > 0.0f) vNewO += vNewP * (fNewOdotP / fNewPMag); float fNewQMag = vNewQ.MagSqr(); if (fNewQMag > 0.0f) vNewO += vNewQ * (fNewOdotQ / fNewQMag); pNeighbor->m_pPoly->SetTextureSpace(GetCurrTexture(), vNewO, vNewP, vNewQ); // Recurse into this neighbor WrapTexture(pNeighbor, vNewDir, cExtents); } }
LTBOOL CAIMovement::Update() { // Clear any past movement. m_pAI->Stop(); m_pAI->SetCheapMovement(LTTRUE); LTVector vNewPos; LTBOOL bMove = LTTRUE; // Bound a new path to the volume. if( ( m_eState == eStateSet ) && m_bNewPathSet ) { BoundPathToVolume( m_pDestVolume ); m_bNewPathSet = LTFALSE; m_iBoundPt = 0; } EnumAnimMovement eMovementType = m_pAI->GetAnimationContext()->GetAnimMovementType(); switch( eMovementType ) { case kAM_None: // Safety mechanism to pop AI up if they fall thru the level. if( m_bMoved && ( m_pAI->GetLastVolume() ) && ( m_pAI->GetPosition().y + Max( 16.f, m_pAI->GetDims().y ) < m_pAI->GetLastVolume()->GetBackBottomLeft().y ) ) { LTVector vPos = m_pAI->GetPosition(); vPos = ConvertToDEditPos( vPos ); AIError( "AI '%s' fell thru the level at pos(%.2f %.2f %.2f )!! Popping back up.", m_pAI->GetName(), vPos.x, vPos.y, vPos.z ); LTVector vDir = m_pAI->GetPosition() - m_vLastValidVolumePos; vDir.y = 0.f; if( vDir.MagSqr() == 0.f ) { vDir = LTVector( 1.f, 0.f, 0.f ); } vDir.Normalize(); vNewPos = m_pAI->GetPosition() + vDir; vNewPos.y = m_pAI->GetLastVolume()->GetBackBottomLeft().y + m_pAI->GetDims().y; m_pAI->Move( vNewPos ); m_bMoved = LTFALSE; return LTTRUE; } // Ensure AI stays in volumes. bMove = LTFALSE; m_bMoved = LTFALSE; if( m_pAI->GetCurrentVolume() ) { m_vLastValidVolumePos = m_pAI->GetPosition(); } else if( ( !m_bIgnoreVolumes ) && m_pAI->GetLastVolume() ) { vNewPos = m_vLastValidVolumePos; bMove = LTTRUE; } break; case kAM_Set: case kAM_Walk: case kAM_Run: case kAM_JumpUp: case kAM_JumpOver: case kAM_Fall: case kAM_Climb: case kAM_Swim: case kAM_Hover: if( (m_eState != eStateSet) || (!UpdateConstantVelocity( eMovementType, &vNewPos ) ) ) { bMove = LTFALSE; } break; case kAM_Encode_NG: case kAM_Encode_G: case kAM_Encode_GB: case kAM_Encode_V: if( LTFALSE == UpdateMovementEncoding( eMovementType, &vNewPos ) ) { bMove = LTFALSE; } break; default: AIASSERT( 0, m_pAI->GetHOBJECT(), "Unknown Movement type!" ); break; } m_pAI->ClearLastHintTransform(); m_eLastMovementType = eMovementType; if( bMove && ( m_pAI->GetPosition() != vNewPos ) ) { // Make sure new position is inside an AI volume. // This NEEDS to check eAxisAll with the vertical threshold. AIVolume* pVolume = g_pAIVolumeMgr->FindContainingVolume( LTNULL, vNewPos, eAxisAll, m_pAI->GetVerticalThreshold(), m_pAI->GetLastVolume() ); if( ( pVolume && pVolume->IsVolumeEnabled() ) || m_bIgnoreVolumes ) { if( m_eState != eStateSet ) { // If an AI is playing a movement encoded animation with // gravity, and does not have a destination, do not allow // him to move over a large verticle drop. // (For example, do not let ninjas fall off roofs while // drawing a sword and taking a step). if( ( ( eMovementType == kAM_Encode_G ) || ( eMovementType == kAM_Encode_GB ) ) && pVolume && ( m_vLastValidVolumePos.y > pVolume->GetBackTopLeft().y + m_pAI->GetVerticalThreshold() ) ) { return LTFALSE; } // If an AI does not have a destination do not allow // him to move into a JumpOver volume. if( pVolume && ( pVolume->GetVolumeType() == AIVolume::kVolumeType_JumpOver ) ) { return LTFALSE; } } // Adjust the height for a parabola. if( m_bDoParabola ) { m_pAI->SetCheapMovement( LTFALSE ); vNewPos.y = m_vParabolaOrigin.y + UpdateParabola(); } // Do not allow any elevation changes in door volumes. // Keeps AI from popping onto doors. if( pVolume && pVolume->HasDoors() ) { m_pAI->SetCheapMovement(LTFALSE); } // Avoid characters if AI has a destination, and movement // is not locked, and AI is in volumes. if( ( m_eState == eStateSet ) && ( m_pDestVolume ) && ( !m_bNoDynamicPathfinding ) && ( !m_bIgnoreVolumes ) && ( !m_bMovementLocked ) && ( !m_bRotationLocked ) && ( eMovementType != kAM_Encode_V ) ) { AvoidDynamicObstacles( &vNewPos, eMovementType ); } // Move us - tells the AI where to move to m_pAI->Move(vNewPos); m_bMoved = LTTRUE; // Record last valid volume position. if( pVolume ) { m_vLastValidVolumePos = vNewPos; } // If we reached an intermediate bound point, move on // to the next one. if( ( m_eState == eStateDone ) && ( m_iBoundPt + 1 < m_cBoundPts ) ) { ++m_iBoundPt; m_vDest = m_vBoundPts[m_iBoundPt]; m_eState = eStateSet; } return LTTRUE; } // Destination is unreachable by volumes. // Put us somewhere valid. else if( m_eState == eStateSet ) { m_pAI->Move(m_vLastValidVolumePos); m_eState = eStateDone; m_bMoved = LTTRUE; } } return LTFALSE; }
//* // ----------------------------------------------------------------------- // // Returns the vector that the physics would have the object move by. // ----------------------------------------------------------------------- // void CalcMotion ( MotionInfo* pInfo, LTObject* pObj, //the object LTVector& dr, //displacement LTVector& v, //velocity LTVector& a, //acceleration const bool bApplyGravity, const float dt //time step ) { LTVector velocityDelta, accelDelta; LTVector q, slopeVel, slopeAccel, vel; const LTVector *n; LTVector objectNormal, vTemp, vTemp2; float fExp; float timeIntegral; float velocityMagSqr, accelMagSqr; LTBOOL bFriction; bFriction = LTFALSE; timeIntegral = dt * dt * 0.5f; velocityMagSqr = v.MagSqr(); accelMagSqr = a.MagSqr(); // [KLS - 3/12/02] - Added support for per-object force override... LTVector vForce = pObj->GetGlobalForceOverride(); // If the global force override is zero, use the MotionInfo force... if (LTVector(0.0, 0.0, 0.0) == vForce) { vForce = pInfo->m_Force; } /* // Debug variables LTVector vOldAccel, vOldVel, vOldPos; vOldAccel = a; vOldVel = v; vOldPos = pObj->GetPos(); //*/ // Stop objects that are moving very slowly... if(velocityMagSqr < 0.1f ) { v.Init(); velocityMagSqr = 0; } if( accelMagSqr < 0.1f ) { a.Init(); accelMagSqr = 0; } // Zero out the displacement to start with. dr.Init(); // Update objects affected by gravity... if(bApplyGravity) { // Add friction to objects standing on something... if(pObj->m_pStandingOn) { // Try to disable their physics. if( velocityMagSqr < 0.1f && accelMagSqr < 0.1f ) { pObj->m_Velocity.Init(); pObj->m_Acceleration.Init(); pObj->m_InternalFlags &= ~IFLAG_APPLYPHYSICS; return; } else { // Check if object on world geometry... if( pObj->m_pNodeStandingOn ) { // Calculate vector parallel to plane... n = &pObj->m_pNodeStandingOn->GetPlane()->m_Normal; } else { // Object standing on another object, so assume opposite to gravity... n = &objectNormal; objectNormal = -pInfo->m_UnitForce; } // Calculate the acceleration including the force LTVector vAccelWithForce = a + vForce; // If we're on a slope that's at enough of an angle, allow it to slide LTVector vForceDir = vForce; vForceDir.Norm(); if (vForceDir.Dot(*n) > pInfo->m_SlideRatio) { float fAccelMag = vAccelWithForce.Mag(); a = vAccelWithForce - *n * n->Dot(vAccelWithForce); // Don't allow it to accelerate up the slope.. float fAccelDotForce = a.Dot(vForceDir); if (fAccelDotForce < 0.0f) { a -= vForceDir * fAccelDotForce; } a.Norm(fAccelMag); // dsi_ConsolePrint("Steep"); } else { // Figure out what our new velocity would be if only the force was used (i.e. are they jumping?) LTVector vNewVel = v + vForce * dt; // If we're going to be moving away from the plane, use the full force if (vNewVel.Dot(*n) > 0.01f) { a = vAccelWithForce; // dsi_ConsolePrint("Jump"); } // If the acceleration without the force isn't moving into the surface, project it there else if (a.Dot(*n) > 0.01f) { float fAccelMag = a.Mag(); a -= *n * (n->Dot(a) + 1.0f); a.Norm(fAccelMag); bFriction = LTTRUE; // dsi_ConsolePrint("Downhill"); } else { bFriction = LTTRUE; // dsi_ConsolePrint("Walk"); } } } } // Otherwise just apply gravity else { a += vForce; // dsi_ConsolePrint("Fall"); } } // If there's no gravity and they aren't moving, then disable their physics else if( velocityMagSqr < 0.1f && accelMagSqr < 0.1f ) { pObj->m_Velocity.Init(); pObj->m_Acceleration.Init(); pObj->m_InternalFlags &= ~IFLAG_APPLYPHYSICS; return; } // If friction // new velocity is given by: v = ( a / k ) + ( v_0 - a / k ) * exp( -k * t ) // new position is given by: x = x_0 + ( a / k ) * t + ( k * v_0 - a ) * ( 1 - exp( -k * t )) / k^2 if( bFriction && pObj->m_FrictionCoefficient > 0.0f ) { // Velocity... fExp = ( float )exp( -pObj->m_FrictionCoefficient * dt ); vTemp = a / pObj->m_FrictionCoefficient; vTemp2 = v - vTemp; vTemp2 *= fExp; vel = vTemp2 + vTemp; // Position delta... dr = vTemp * dt; vTemp = v * pObj->m_FrictionCoefficient; vTemp -= a; vTemp *= (( 1.0f - fExp ) / pObj->m_FrictionCoefficient / pObj->m_FrictionCoefficient); dr += vTemp; pObj->m_Velocity = vel; v = pObj->m_Velocity; } // If no friction // new velocity is given by: v = v_0 + a * t // new position is given by: x = x_0 + v_0 * t + .5 * a * t^2 else { // Find the change in velocity... velocityDelta = a * dt; // Position delta... dr = v * dt; vTemp = a * (timeIntegral * 0.5f); dr += vTemp; // Add the final velocity to the new velocity. pObj->m_Velocity += velocityDelta; v = pObj->m_Velocity; } /* // Show debug information, filtering out the server-side player object if(bApplyGravity) { dsi_ConsolePrint("Old - A:<%7.1f,%7.1f,%7.1f> V:<%7.1f,%7.1f,%7.1f> P:<%7.1f,%7.1f,%7.1f>", VEC_EXPAND(vOldAccel), VEC_EXPAND(vOldVel), VEC_EXPAND(vOldPos)); LTVector vNewAccel, vNewVel, vNewPos; vNewAccel = a; vNewVel = v; vNewPos = vOldPos + dr; dsi_ConsolePrint("New - A:<%7.1f,%7.1f,%7.1f> V:<%7.1f,%7.1f,%7.1f> P:<%7.1f,%7.1f,%7.1f>", VEC_EXPAND(vNewAccel), VEC_EXPAND(vNewVel), VEC_EXPAND(vNewPos)); } //*/ return; }