void plPhysicalControllerCore::IApply(float delSecs) { fSimLength = delSecs; // Match controller to owner if transform has changed since the last frame plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (!CompareMatrices(fLastGlobalLoc, l2w, 0.0001f)) SetGlobalLoc(l2w); if (fEnabled) { // Convert velocity from avatar to world space if (!fLinearVelocity.IsEmpty()) { fLinearVelocity = l2w * fLinearVelocity; const plCoordinateInterface* subworldCI = GetSubworldCI(); if (subworldCI) fLinearVelocity = subworldCI->GetWorldToLocal() * fLinearVelocity; } fMovementStrategy->Apply(delSecs); } }
// Called after the simulation has run....sends new positions to the various scene objects // *** want to do this in response to an update message.... void plPXPhysical::SendNewLocation(bool synchTransform, bool isSynchUpdate) { // we only send if: // - the body is active or forceUpdate is on // - the mass is non-zero // - the physical is not passive bool bodyActive = !fActor->isSleeping(); bool dynamic = fActor->isDynamic(); if ((bodyActive || isSynchUpdate) && dynamic)// && fInitialTransform) { plProfile_Inc(MaySendLocation); if (!GetProperty(plSimulationInterface::kPassive)) { hsMatrix44 curl2w = fCachedLocal2World; // we're going to cache the transform before sending so we can recognize if it comes back IGetTransformGlobal(fCachedLocal2World); if (!CompareMatrices(curl2w, fCachedLocal2World, .0001f)) { plProfile_Inc(LocationsSent); plProfile_BeginLap(PhysicsUpdates, GetKeyName().c_str()); // quick peek at the translation...last time it was corrupted because we applied a non-unit quaternion // hsAssert(real_finite(fCachedLocal2World.fMap[0][3]) && // real_finite(fCachedLocal2World.fMap[1][3]) && // real_finite(fCachedLocal2World.fMap[2][3]), "Bad transform outgoing"); if (fCachedLocal2World.GetTranslate().fZ < kMaxNegativeZPos) { SimLog("Physical %s fell to %.1f (%.1f is the max). Suppressing.", GetKeyName().c_str(), fCachedLocal2World.GetTranslate().fZ, kMaxNegativeZPos); // Since this has probably been falling for a while, and thus not getting any syncs, // make sure to save it's current pos so we'll know to reset it later DirtySynchState(kSDLPhysical, plSynchedObject::kBCastToClients); IEnable(false); } hsMatrix44 w2l; fCachedLocal2World.GetInverse(&w2l); plCorrectionMsg *pCorrMsg = new plCorrectionMsg(GetObjectKey(), fCachedLocal2World, w2l, synchTransform); pCorrMsg->Send(); if (fProxyGen) fProxyGen->SetTransform(fCachedLocal2World, w2l); plProfile_EndLap(PhysicsUpdates, GetKeyName().c_str()); } } } }
void plMovementStrategy::IApplyKinematic() { // first apply sceneobject update to the kinematic plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); if (so) { // If we've been moved since the last physics update (somebody warped us), // update the physics before we apply velocity. const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) { fCore->SetKinematicLoc(l2w); //fCore->SetGlobalLoc(l2w); } } }
// This form is assumed by convention to be global. void plPXPhysical::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, bool force) { // hsAssert(real_finite(l2w.fMap[0][3]) && real_finite(l2w.fMap[1][3]) && real_finite(l2w.fMap[2][3]), "Bad transform incoming"); // make sure the physical is dynamic. // also make sure there is some difference between the matrices... // ... but not when a subworld... because the subworld maybe animating and if the object is still then it is actually moving within the subworld if (force || (fActor->isDynamic() && (fWorldKey || !CompareMatrices(l2w, fCachedLocal2World, .0001f))) ) { ISetTransformGlobal(l2w); plProfile_Inc(SetTransforms); } else { if ( !fActor->isDynamic() && plSimulationMgr::fExtraProfile) SimLog("Setting transform on non-dynamic: %s.", GetKeyName().c_str()); } }
void plPhysicalControllerCore::IUpdateNonPhysical(float alpha) { // Update global location if owner transform hasn't changed. plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (CompareMatrices(fLastGlobalLoc, l2w, 0.0001f)) { if (fEnabled) { hsVector3 displacement = (hsVector3)(fLocalPosition - fLastLocalPosition); hsPoint3 interpLocalPos = fLastLocalPosition + (displacement * alpha); fLocalRotation.MakeMatrix(&fLastGlobalLoc); fLastGlobalLoc.SetTranslate(&interpLocalPos); const plCoordinateInterface* subworldCI = GetSubworldCI(); if (subworldCI) { const hsMatrix44& subL2W = subworldCI->GetLocalToWorld(); fLastGlobalLoc = subL2W * fLastGlobalLoc; fPrevSubworldW2L = subworldCI->GetWorldToLocal(); } ISendCorrectionMessages(); } else { // Update global location if in a subworld const plCoordinateInterface* subworldCI = GetSubworldCI(); if (subworldCI) { hsMatrix44 l2s = fPrevSubworldW2L * fLastGlobalLoc; const hsMatrix44& subL2W = subworldCI->GetLocalToWorld(); fLastGlobalLoc = subL2W * l2s; fPrevSubworldW2L = subworldCI->GetWorldToLocal(); ISendCorrectionMessages(); } } } }
void plRidingAnimatedPhysicalStrategy::Apply(float delSecs) { hsVector3 LinearVelocity=fCore->GetLinearVelocity(); hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); if (fCore->IsKinematic()) { //want to make sure nothing funky happens in the sim IApplyKinematic(); return; } if (!fCore->IsEnabled()) return; //need to sweep ahead to see what we might hit. // if we hit anything we should probably apply the force that would normally be applied in fCore->SetPushingPhysical(nil); fCore->SetFacingPushingPhysical( false); plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); hsPoint3 startPos, desiredDestination, endPos; fCore->GetPositionSim(startPos); uint32_t collideFlags = 1<<plSimDefs::kGroupStatic | 1<<plSimDefs::kGroupAvatarBlocker | 1<<plSimDefs::kGroupDynamic; std::multiset<plControllerSweepRecord> GroundHitRecords; int possiblePlatformCount =fCore->SweepControllerPath(startPos, startPos + hsPoint3(0.f,0.f, -0.002f), true, true, collideFlags, GroundHitRecords); float maxPlatformVel = - FLT_MAX; int platformCount=0; fGroundHit = false; if(possiblePlatformCount) { std::multiset<plControllerSweepRecord>::iterator curRecord; for(curRecord = GroundHitRecords.begin(); curRecord != GroundHitRecords.end(); curRecord++) { hsBool groundlike=false; if((curRecord->locHit.fZ - startPos.fZ)<= .2) groundlike= true; if(groundlike) { if(curRecord->ObjHit !=nil) { hsVector3 vel; curRecord->ObjHit->GetLinearVelocitySim(vel); if(vel.fZ > maxPlatformVel) { maxPlatformVel= vel.fZ; } } platformCount ++; fGroundHit = true; } } } bool gotGroundHit = fGroundHit; if (so) { // If we've been moved since the last physics update (somebody warped us), // update the physics before we apply velocity. const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) fCore->SetGlobalLoc(l2w); // Convert our avatar relative velocity to subworld relative if (!LinearVelocity.IsEmpty()) { LinearVelocity = l2w * LinearVelocity; const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); if (subworldCI) LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; } if(!IsOnGround()) { if(!fNeedVelocityOverride) { LinearVelocity.fZ= AchievedLinearVelocity.fZ; } else { LinearVelocity = fOverrideVelocity; } } if(fStartJump) { LinearVelocity.fZ =12.0f; } if(platformCount) { LinearVelocity.fZ = LinearVelocity.fZ + maxPlatformVel; } //probably neeed to do something with contact normals in here //for false ground stuff fFalseGround = false; hsVector3 testLength = LinearVelocity * delSecs + hsVector3(0.0, 0.0, -0.00f); // hsPoint3 desiredDestination= startPos + testLength; if(!IsOnGround()) { if(ICheckMove(startPos, desiredDestination)) {//we can get there soley by the LinearVelocity fNeedVelocityOverride =false; } else { fNeedVelocityOverride =true; fOverrideVelocity = LinearVelocity; fOverrideVelocity.fZ -= delSecs * 32.f; } } else { fNeedVelocityOverride =false; } fCore->SetLinearVelocity(LinearVelocity); } }
void plSwimStrategy::Apply(float delSecs) { hsAssert(fCore,"PlSwimStrategy::Apply No Core shouldn't be Applying"); uint32_t collideFlags = 1<<plSimDefs::kGroupStatic | 1<<plSimDefs::kGroupAvatarBlocker | 1<<plSimDefs::kGroupDynamic; if(!fCore->IsSeeking()) { collideFlags|=(1<<plSimDefs::kGroupExcludeRegion); } hsVector3 LinearVelocity=fCore->GetLinearVelocity(); hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); if (fCore->IsKinematic()) { plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); if (so) { // If we've been moved since the last physics update (somebody warped us), // update the physics before we apply velocity. const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) { fCore->SetKinematicLoc(l2w); fCore->SetGlobalLoc(l2w); } } return; } if (!fCore->IsEnabled()) return; fCore->SetPushingPhysical(nil); fCore->SetFacingPushingPhysical( false); fHadContacts=false; fOnGround=false; plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); if (so) { // If we've been moved since the last physics update (somebody warped us), // update the physics before we apply velocity. const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) fCore->SetGlobalLoc(l2w); // Convert our avatar relative velocity to subworld relative if (!LinearVelocity.IsEmpty()) { LinearVelocity = l2w * LinearVelocity; const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); if (subworldCI) LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; } IAdjustBuoyancy(); float zacc; float retardent=0.0f; static float FinalBobSpeed=0.5f; //trying to dampen the oscillations if((AchievedLinearVelocity.fZ>FinalBobSpeed)||(AchievedLinearVelocity.fZ<-FinalBobSpeed)) retardent=AchievedLinearVelocity.fZ *-.90f; zacc=(1-fBuoyancy)*-32.f + retardent; hsVector3 linCurrent(0.0f,0.0f,0.0f); float angCurrent = 0.f; if (fCurrentRegion != nil) { fCurrentRegion->GetCurrent(fCore, linCurrent, angCurrent, delSecs); //fAngularVelocity+= angCurrent; } hsVector3 vel(LinearVelocity.fX , LinearVelocity.fY , AchievedLinearVelocity.fZ+ LinearVelocity.fZ ); vel.fZ= vel.fZ + zacc*delSecs; if(fCurrentRegion!=nil){ if (vel.fZ > fCurrentRegion->fMaxUpwardVel) { vel.fZ = fCurrentRegion->fMaxUpwardVel; } vel+= linCurrent; } static const float kGravity = -32.f; if(vel.fZ<kGravity) {//applying this terminal velocity just to avoid shooting 100 feet below the surface // and losing our surface ray cast vel.fZ =kGravity; } hsVector3 displacement= vel*delSecs; unsigned int colFlags = 0; fContactNormals.SetCount(0); fCore->Move(displacement,collideFlags,colFlags); if((colFlags&kBottom)||(colFlags&kSides))fHadContacts=true; float angvel=fCore->GetAngularVelocity(); fCore->SetAngularVelocity(angvel +angCurrent); } }
/////////////////////////// //Walking Strategy void plWalkingStrategy::Apply(float delSecs) { //Apply Should Only be Called from a PhysicalControllerCore hsAssert(fCore,"No Core shouldn't be Applying"); uint32_t collideFlags = 1<<plSimDefs::kGroupStatic | 1<<plSimDefs::kGroupAvatarBlocker | 1<<plSimDefs::kGroupDynamic; if(!fCore->IsSeeking()) { collideFlags|=(1<<plSimDefs::kGroupExcludeRegion); } bool OnTopOfAnimatedPhys=false; hsVector3 LinearVelocity=fCore->GetLinearVelocity(); hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); hsPoint3 positionBegin; fCore->GetPositionSim(positionBegin); bool recovered=false; if (fCore->IsKinematic()) { plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); if (so) { // If we've been moved since the last physics update (somebody warped us), // update the physics before we apply velocity. const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) { fCore->SetKinematicLoc(l2w); fCore->SetGlobalLoc(l2w); } } return; } if (!fCore->IsEnabled()) return; bool gotGroundHit = fGroundHit; fGroundHit = false; fCore->SetPushingPhysical(nil); fCore->SetFacingPushingPhysical( false); plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); if (so) { static const float kGravity = -32.f; // If we've been moved since the last physics update (somebody warped us), // update the physics before we apply velocity. const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) fCore->SetGlobalLoc(l2w); // Convert our avatar relative velocity to subworld relative if (!LinearVelocity.IsEmpty()) { LinearVelocity = l2w * LinearVelocity; const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); if (subworldCI) LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; } // Add in gravity if the avatar's z velocity isn't being set explicitly // (Add in a little fudge factor, since the animations usually add a // tiny bit of z.) if (hsABS(LinearVelocity.fZ) < 0.001f) { // Get our previous z velocity. If we're on the ground, clamp it to zero at // the largest, so we won't launch into the air if we're running uphill. float prevZVel = AchievedLinearVelocity.fZ; if (IsOnGround()) prevZVel = hsMinimum(prevZVel, 0.f); float grav = kGravity * delSecs; // If our gravity contribution isn't high enough this frame, we won't // report a collision even when standing on solid ground. float maxGrav = -.001f / delSecs; if (grav > maxGrav) grav = maxGrav; LinearVelocity.fZ = prevZVel + grav; } // If we're airborne and the velocity isn't set, use the velocity from // the last frame so we maintain momentum. if (!IsOnGround() && LinearVelocity.fX == 0.f && LinearVelocity.fY == 0.f) { LinearVelocity.fX = AchievedLinearVelocity.fX; LinearVelocity.fY = AchievedLinearVelocity.fY; } //make terminal velocity equal to k. it is wrong but has been this way and //don't want to break any puzzles. on top of that it will reduce tunneling behavior if(LinearVelocity.fZ<kGravity)LinearVelocity.fZ=kGravity; fCore->SetLinearVelocity(LinearVelocity); // Scale the velocity to our actual step size (by default it's feet/sec) hsVector3 vel(LinearVelocity.fX * delSecs, LinearVelocity.fY * delSecs, LinearVelocity.fZ * delSecs); unsigned int colFlags = 0; fGroundHit = false; fFalseGround = false; fContactNormals.Swap(fPrevSlidingNormals); fContactNormals.SetCount(0); fCore->Move(vel, collideFlags, colFlags); ICheckForFalseGround(); //if(fReqMove2) fCore->Move2(vel); /*If the Physx controller thinks we have a collision from below, need to make sure we have at least have false ground, otherwise Autostepping can send us into the air, and we will some times float/panic link. For some reason the NxControllerHitReport does not always send messages regarding Controller contact with ground plane, but will (almost) always return NXCC_COLLISION_DOWN with the move method. */ if((colFlags&kBottom ) &&(fGroundHit==false)) { fFalseGround=true; } if(colFlags&kTop) { fHitHead=true; //Did you hit your head on a dynamic? //with Physx's wonderful controller hit report vs flags issues we need to actually sweep to see std::multiset< plControllerSweepRecord > HitsDynamic; uint32_t testFlag=1<<plSimDefs::kGroupDynamic; hsPoint3 startPos; hsPoint3 endPos; fCore->GetPositionSim(startPos); endPos= startPos + vel; int NumObjsHit=fCore->SweepControllerPath(startPos, endPos, true, false, testFlag, HitsDynamic); if(NumObjsHit>0) { for(std::multiset< plControllerSweepRecord >::iterator curObj= HitsDynamic.begin(); curObj!=HitsDynamic.end(); curObj++) { hsAssert(curObj->ObjHit,"We allegedly hit something, but there is no plasma physical associated with it"); if(curObj->ObjHit) {//really we shouldn't have to check hitObj should be nil only if we miss, or the physX object //doesn't have a user data associated with this either way this just shouldn't happen hsVector3 hitObjVel; curObj->ObjHit->GetLinearVelocitySim(hitObjVel); hsVector3 relativevel=LinearVelocity-hitObjVel; curObj->ObjHit->SetHitForce(relativevel * 10.0f * (*curObj).ObjHit->GetMass(), (*curObj).locHit); } } HitsDynamic.clear(); } } } }
/*----------------------------------------------------------------------------- * Draw * The Draw function is the main loop for the Graphics process. Draws each * viewport and the lines within them *-----------------------------------------------------------------------------*/ void Draw( void ) { char buf[BUF_SIZ]; GLdouble answer[MATRIX_SIZE]; GLdouble attempt[MATRIX_SIZE]; /* Clear the screen ... */ glClear( GL_COLOR_BUFFER_BIT ); if(gameComplete) { DrawText(-25.0, -25.0, DEFAULT_FONT, "GAME OVER! YOU WIN!"); } else if(levelComplete) { DrawText(-25.0, -25.0, DEFAULT_FONT, "LEVEL COMPLETE!"); } else { glPushMatrix( ); /* Draw the Outlines for Transforms */ glCallList('a'); glCallList('s'); /* Draw the Axes */ glBegin( GL_LINES ); glColor3f( 0.5, 0.5, 0.5 ); glVertex3f( 100.0, 0.0, 0.0 ); glVertex3f(-100.0, 0.0, 0.0 ); glVertex3f( 0.0, 100.0, 0.0 ); glVertex3f( 0.0,-100.0, 0.0 ); glColor3f( 1.0, 1.0, 1.0 ); glEnd( ); glPopMatrix( ); /* Draw the first set of Available Transforms */ glPushMatrix( ); /* Move to origin of first list */ glTranslatef( 115.0, 90.0, 0.0 ); /* List drawing */ CreateTransforms( &tlAvailableTransforms, VERTICAL, AVAILABLE ); glPopMatrix( ); /* Draw the second set of Used Transforms */ glPushMatrix( ); /* Move to origin of first list */ glTranslatef( -85.0, -120.0, 0.0 ); /* List drawing */ CreateTransforms( &tlSelectedTransforms, HORIZONTAL, SELECTED ); glPopMatrix( ); glPushMatrix( ); /* Do all level transforms */ RunTransformList( &tlLevel ); /* Create model house */ glLoadName( 1 ); glCallList( 'l' ); /* Get the modelview matrix */ glGetDoublev( GL_MODELVIEW_MATRIX, answer ); glPopMatrix( ); /* Create the new house */ glPushMatrix( ); /* Do all user selected transforms */ RunTransformList( &tlSelectedTransforms ); /* Create attempt house */ glLoadName( HOUSE ); glCallList( 'h' ); /* Get the modelview matrix */ glGetDoublev( GL_MODELVIEW_MATRIX, attempt ); glPopMatrix( ); /* Compare the model */ if(CompareMatrices(answer, attempt)) { levelComplete = TRUE; glutPostRedisplay( ); } } /* Flush the buffer */ glutSwapBuffers(); return; }