/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Additional permissions under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK (or a modified version of those libraries), containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL and IJG JPEG Library used as well as that of the covered work. You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #include "plPXPhysicalControllerCore.h" #include "plSimulationMgr.h" #include "plPXPhysical.h" #include "plPXConvert.h" #include "../pnSceneObject/plSimulationInterface.h" #include "../pnSceneObject/plSceneObject.h" #include "../pnMessage/plCorrectionMsg.h" #include "../plAvatar/plArmatureMod.h" #include "../pnSceneObject/plCoordinateInterface.h" #include "../plDrawable/plDrawableGenerator.h" #include "../plPhysical/plPhysicalProxy.h" #include "../pnMessage/plSetNetGroupIDMsg.h" #include "../plMessage/plCollideMsg.h" #include "../plModifier/plDetectorLog.h" //#include "NxVecExtendedVec3.h" #include "NxPhysics.h" #include "ControllerManager.h" #include "NxCapsuleController.h" #include "NxCapsuleShape.h" #define kPhysxSkinWidth 0.1f #define kPhysZOffset ((fRadius + (fHeight / 2)) + kPhysxSkinWidth) //#define kSLOPELIMIT (cosf(NxMath::degToRad(55.f))) //#define kPhysicalHeightFudge 0.4f // this fudge was used for PhysX 2.4 #define kPhysicalHeightFudge 0.0f //#define STEP_OFFSET 1.0f #define STEP_OFFSET 0.5f //#define STEP_OFFSET 0.15f #ifndef PLASMA_EXTERNAL_RELEASE hsBool plPXPhysicalControllerCore::fDebugDisplay = false; #endif // PLASMA_EXTERNAL_RELEASE int plPXPhysicalControllerCore::fPXControllersMax = 0; static ControllerManager gControllerMgr; static std::vector gControllers; static bool gRebuildCache=false; #define AvatarMass 200.f class PXControllerHitReportWalk : public NxUserControllerHitReport { public: virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit) { plPXPhysicalControllerCore* ac = plPXPhysicalControllerCore::FindController(hit.controller); NxActor& actor = hit.shape->getActor(); plPXPhysical* phys = (plPXPhysical*)actor.userData; static hsScalar SlopeLimit = kSLOPELIMIT; hsVector3 normal = plPXConvert::Vector(hit.worldNormal); ac->fMovementInterface->IAddContactNormals(normal); #ifndef PLASMA_EXTERNAL_RELEASE plDbgCollisionInfo info; info.fNormal = normal; info.fSO = plSceneObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); info.fOverlap = false; NxShape* const *shapes = hit.controller->getActor()->getShapes(); int numShapes = hit.controller->getActor()->getNbShapes(); int i; for (i = 0; i < numShapes; i++) { // should only be one capsule shape const NxCapsuleShape *capShape = shapes[i]->isCapsule(); if (capShape) { NxCapsule cap; capShape->getWorldCapsule(cap); if (hit.shape->checkOverlapCapsule(cap)) info.fOverlap = true; } } ac->fDbgCollisionInfo.Append(info); #endif PLASMA_EXTERNAL_RELEASE // If the avatar hit a movable physical, apply some force to it. hsVector3 dir = plPXConvert::Vector(hit.dir); float dirdotup=dir.fZ; hsPoint3 pos((hsScalar)hit.worldPos.x, (hsScalar)hit.worldPos.y, (hsScalar)hit.worldPos.z); NxExtendedVec3 controllerPos=hit.controller->getPosition(); hsVector3 bottomOfTheCapsule((hsScalar)controllerPos.x,(hsScalar)controllerPos.y,(hsScalar)controllerPos.z); bottomOfTheCapsule.fZ=bottomOfTheCapsule.fZ-(ac->fHeight/2.0f + ac->fRadius); if (actor.isDynamic() ) { if((hit.worldPos.z- bottomOfTheCapsule.fZ)<=ac->fRadius)//bottom hemisphere { //onTopOfSlopeLimit if (phys && phys->GetProperty(plSimulationInterface::kPhysAnim)) { if(normal.fZ>=0) {//we consider this ground ac->fMovementInterface->AddOnTopOfObject(phys); } } } if ( !actor.readBodyFlag(NX_BF_KINEMATIC) && !actor.readBodyFlag(NX_BF_FROZEN)) { // If this is the local avatar, we need to take ownership of this // dynamic if we haven't already if (ac->fLOSDB == plSimDefs::kLOSDBLocalAvatar && !phys->IsLocallyOwned() && !phys->GetProperty(plSimulationInterface::kNoOwnershipChange)) { plSynchedObject* obj = plSynchedObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); obj->SetNetGroupConstant(plNetGroup::kNetGroupLocalPhysicals); // Tell all the other clients that we own this physical plSetNetGroupIDMsg* setNetGroupID = TRACKED_NEW plSetNetGroupIDMsg; setNetGroupID->fId = plNetGroup::kNetGroupRemotePhysicals; setNetGroupID->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce); setNetGroupID->SetBCastFlag(plMessage::kLocalPropagate, false); setNetGroupID->Send(obj->GetKey()); } plSimulationMgr::GetInstance()->ConsiderSynch(phys, nil); // We only allow horizontal pushes. Vertical pushes when we stand on // dynamic objects creates useless stress on the solver. hsVector3 vel=ac->GetLinearVelocity()- plPXConvert::Vector( actor.getLinearVelocity()); if(dirdotup>=0)vel.fZ=0.001f; else vel.fZ=0.0f; static hsScalar kAvieMass = 140.f/32.f; if (!vel.IsEmpty()) { static hsScalar kForceScale = 140.0f; NxF32 coeff; NxExtendedVec3 norm2=hit.controller->getPosition(); norm2.x=hit.worldPos.x-bottomOfTheCapsule.fX; norm2.y=hit.worldPos.y-bottomOfTheCapsule.fY; if((hit.worldPos.z- bottomOfTheCapsule.fZ)fRadius)//bottom hemisphere { norm2.normalize(); norm2.z=0.01f; } else if((hit.worldPos.z- bottomOfTheCapsule.fZ)<(ac->fRadius+ac->fHeight)) { norm2.z=0.0f; norm2.normalize(); } else {//must be the top so the normal is displacement from the pos - center //of top hemisphere norm2.z=hit.worldPos.z - ((ac->fRadius+ac->fHeight + bottomOfTheCapsule.fZ)); norm2.normalize(); } float proj=(float)(norm2.x*dir.fX+dir.fY*norm2.y+dir.fZ*norm2.z); coeff =abs(proj*kForceScale*vel.Magnitude()); vel.fZ=(hsScalar)norm2.z; vel.fY=(hsScalar)norm2.y; vel.fX=(hsScalar)norm2.x; phys->SetHitForce(vel*coeff, pos); } } } else // else if the avatar hit a static { return NX_ACTION_NONE; } if (phys && phys->GetProperty(plSimulationInterface::kAvAnimPushable)) { hsQuat inverseRotation = ac->fLocalRotation.Inverse(); hsVector3 normal = plPXConvert::Vector(hit.worldNormal); ac->SetPushingPhysical( phys); ac->SetFacingPushingPhysical((inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false)); } return NX_ACTION_NONE; } virtual NxControllerAction onControllerHit(const NxControllersHit& hit) { return NX_ACTION_NONE; } } gMyReport; plPhysicalControllerCore* plPhysicalControllerCore::Create(plKey ownerSO, hsScalar height, hsScalar width) { // Test to see how many controller there already is if ( !plPXPhysicalControllerCore::fPXControllersMax || plPXPhysicalControllerCore::NumControllers() < plPXPhysicalControllerCore::fPXControllersMax ) { hsScalar radius = width / 2.f; hsScalar realHeight = height - width + kPhysicalHeightFudge; return TRACKED_NEW plPXPhysicalControllerCore(ownerSO, realHeight,radius); } return nil; } //Static Helper Func plPXPhysicalControllerCore* plPXPhysicalControllerCore::FindController(NxController* controller) { for (int i = 0; i < gControllers.size(); i++) { plPXPhysicalControllerCore* ac = gControllers[i]; if (ac->fController == controller) return ac; } return nil; } void plPXPhysicalControllerCore::RebuildCache(){gRebuildCache=true;} plPXPhysicalControllerCore* plPXPhysicalControllerCore::GetController(NxActor& actor, bool* isController) { *isController = false; for (int i = 0; i < gControllers.size(); i++) { plPXPhysicalControllerCore* ac = gControllers[i]; if (ac->fController && ac->fController->getActor() == &actor) { *isController = true; return ac; } if ( ac->fKinematicActor == &actor) { return ac; } } return nil; } void plPXPhysicalControllerCore::GetWorldSpaceCapsule(NxCapsule& cap) const { if(this->fKinematicActor) { int numshapes=fKinematicActor->getNbShapes(); if (numshapes==1) { //there should only be one shape on a controller NxShape* const *shapes=fKinematicActor->getShapes(); //and since it is a capsule controller it better be a capsule; NxCapsuleShape *capShape = shapes[0]->isCapsule(); if(capShape) capShape->getWorldCapsule(cap); } } } bool plPXPhysicalControllerCore::AnyControllersInThisWorld(plKey world) { for (int i = 0; i < gControllers.size(); i++) { plPXPhysicalControllerCore* ac = gControllers[i]; if (ac->GetSubworld() == world) return true; } return false; } int plPXPhysicalControllerCore::NumControllers() { return gControllers.size(); } int plPXPhysicalControllerCore::GetControllersInThisSubWorld(plKey world, int maxToReturn,plPXPhysicalControllerCore** bufferout) { int i=0; for (int j=0;jGetSubworld()==world) { if(iGetSubworld()==world)i++; } return i; } // plPXPhysicalControllerCore::plPXPhysicalControllerCore(plKey ownerSO, hsScalar height, hsScalar radius) : plPhysicalControllerCore(ownerSO,height,radius) , fController(nil) , fProxyGen(nil) , fKinematicActor(nil) ,fPreferedRadius(radius) ,fPreferedHeight(height) , fBehavingLikeAnimatedPhys(true) { fLocalPosition.Set(0, 0, 0); fLocalRotation.Set(0, 0, 0, 1); gControllers.push_back(this); fLastGlobalLoc.Reset(); ICreateController(); Enable(false); } void plPXPhysicalControllerCore::ISetGlobalLoc(const hsMatrix44& l2w) { fLastGlobalLoc = l2w; // Update our subworld position and rotation const plCoordinateInterface* subworldCI = GetSubworldCI(); if (subworldCI) { const hsMatrix44& w2s = fPrevSubworldW2L; hsMatrix44 l2s = w2s * l2w; l2s.GetTranslate(&fLocalPosition); fLocalRotation.SetFromMatrix44(l2s); } else { l2w.GetTranslate(&fLocalPosition); fLocalRotation.SetFromMatrix44(l2w); } hsMatrix44 w2l; l2w.GetInverse(&w2l); if (fProxyGen) fProxyGen->SetTransform(l2w, w2l); // Update the physical position NxExtendedVec3 nxPos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kPhysZOffset); fController->setPosition(nxPos); IMatchKinematicToController(); } plPXPhysicalControllerCore::~plPXPhysicalControllerCore() { IDeleteController(); //need to make sure my queued messages are released for(int j=0;jgetPosition(); NxVec3 prevKinPos = fKinematicActor->getGlobalPosition(); NxVec3 kinPos; kinPos.x = (NxReal)cPos.x; kinPos.y = (NxReal)cPos.y; kinPos.z = (NxReal)cPos.z; if (plSimulationMgr::fExtraProfile) SimLog("Match setting kinematic from %f,%f,%f to %f,%f,%f",prevKinPos.x,prevKinPos.y,prevKinPos.z,kinPos.x,kinPos.y,kinPos.z ); fKinematicActor->setGlobalPosition(kinPos); } } void plPXPhysicalControllerCore::UpdateControllerAndPhysicalRep() { if ( fKinematicActor) { if(this->fBehavingLikeAnimatedPhys) {//this means we are moving the controller and then synchnig the kin NxExtendedVec3 ControllerPos= fController->getPosition(); NxVec3 NewKinPos((NxReal)ControllerPos.x, (NxReal)ControllerPos.y, (NxReal)ControllerPos.z); if (fEnabled || fKinematic) { if (plSimulationMgr::fExtraProfile) SimLog("Moving kinematic to %f,%f,%f",NewKinPos.x, NewKinPos.y, NewKinPos.z ); // use the position fKinematicActor->moveGlobalPosition(NewKinPos); } else { if (plSimulationMgr::fExtraProfile) SimLog("Setting kinematic to %f,%f,%f", NewKinPos.x, NewKinPos.y, NewKinPos.z ); fKinematicActor->setGlobalPosition(NewKinPos); } } else { NxVec3 KinPos= fKinematicActor->getGlobalPosition(); NxExtendedVec3 NewControllerPos(KinPos.x, KinPos.y, KinPos.z); if (plSimulationMgr::fExtraProfile) SimLog("Setting Controller to %f,%f,%f", NewControllerPos.x, NewControllerPos.y, NewControllerPos.z ); fController->setPosition(NewControllerPos); } hsPoint3 curLocalPos; GetPositionSim(curLocalPos); fLocalPosition = curLocalPos; } } void plPXPhysicalControllerCore::MoveKinematicToController(hsPoint3& pos) { if ( fKinematicActor) { NxVec3 kinPos = fKinematicActor->getGlobalPosition(); if ( abs(kinPos.x-pos.fX) + abs(kinPos.y-pos.fY) + (abs(kinPos.z-pos.fZ+kPhysZOffset)) > 0.0001f) { NxVec3 newPos; newPos.x = (NxReal)pos.fX; newPos.y = (NxReal)pos.fY; newPos.z = (NxReal)pos.fZ+kPhysZOffset; if ((fEnabled || fKinematic) && fBehavingLikeAnimatedPhys) { if (plSimulationMgr::fExtraProfile) SimLog("Moving kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); // use the position fKinematicActor->moveGlobalPosition(newPos); } else { if (plSimulationMgr::fExtraProfile) SimLog("Setting kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); fKinematicActor->setGlobalPosition(newPos); } } } } void plPXPhysicalControllerCore::ISetKinematicLoc(const hsMatrix44& l2w) { hsPoint3 kPos; // Update our subworld position and rotation const plCoordinateInterface* subworldCI = GetSubworldCI(); if (subworldCI) { const hsMatrix44& w2s = subworldCI->GetWorldToLocal(); hsMatrix44 l2s = w2s * l2w; l2s.GetTranslate(&kPos); } else { l2w.GetTranslate(&kPos); } hsMatrix44 w2l; l2w.GetInverse(&w2l); if (fProxyGen) fProxyGen->SetTransform(l2w, w2l); // add z offset kPos.fZ += kPhysZOffset; // Update the physical position of kinematic if (fEnabled|| fKinematic) fKinematicActor->moveGlobalPosition(plPXConvert::Point(kPos)); else fKinematicActor->setGlobalPosition(plPXConvert::Point(kPos)); } void plPXPhysicalControllerCore::IGetPositionSim(hsPoint3& pos) const { if(this->fBehavingLikeAnimatedPhys) { const NxExtendedVec3& nxPos = fController->getPosition(); pos.Set(hsScalar(nxPos.x), hsScalar(nxPos.y), hsScalar(nxPos.z) - kPhysZOffset); } else { NxVec3 Pos = fKinematicActor->getGlobalPosition(); pos.Set(hsScalar(Pos.x), hsScalar(Pos.y), hsScalar(Pos.z) - kPhysZOffset); } } void plPXPhysicalControllerCore::ICreateController() { NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); NxCapsuleControllerDesc desc; desc.position.x = 0; desc.position.y = 0; desc.position.z = 0; desc.upDirection = NX_Z; desc.slopeLimit = kSLOPELIMIT; desc.skinWidth = kPhysxSkinWidth; desc.stepOffset = STEP_OFFSET; desc.callback = &gMyReport; desc.userData = this; desc.radius = fRadius; desc.height = fHeight; desc.interactionFlag = NXIF_INTERACTION_EXCLUDE; //desc.interactionFlag = NXIF_INTERACTION_INCLUDE; fController = (NxCapsuleController*)gControllerMgr.createController(scene, desc); // Change the avatars shape groups. The avatar doesn't actually use these when // it's determining collision, but if you're standing still and an object runs // into you, it'll pass through without this. NxActor* actor = fController->getActor(); NxShape* shape = actor->getShapes()[0]; shape->setGroup(plSimDefs::kGroupAvatar); // need to create the non-bouncing object that can be used to trigger things while the avatar is doing behaviors. NxActorDesc actorDesc; NxCapsuleShapeDesc capDesc; capDesc.radius = fRadius; capDesc.height = fHeight; capDesc.group = plSimDefs::kGroupAvatar; capDesc.materialIndex= plSimulationMgr::GetInstance()->GetMaterialIdx(scene, 0.0,0.0); actorDesc.shapes.pushBack(&capDesc); NxBodyDesc bodyDesc; bodyDesc.mass = AvatarMass;//1.f; actorDesc.body = &bodyDesc; bodyDesc.flags = NX_BF_KINEMATIC; bodyDesc.flags |=NX_BF_DISABLE_GRAVITY ; actorDesc.name = "AvatarTriggerKinematicGuy"; fSeeking=false; try { fKinematicActor = scene->createActor(actorDesc); } catch (...) { hsAssert(false, "Actor creation crashed"); } #ifdef PHYSX_KINEMATIC_IS_DISABLED // initially start as in-active fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); #endif // set the matrix to be the same as the controller's actor... that should orient it to be the same fKinematicActor->setGlobalPose(actor->getGlobalPose()); // the proxy for the debug display //hsAssert(!fProxyGen, "Already have proxy gen, double read?"); hsColorRGBA physColor; hsScalar opac = 1.0f; // local avatar is light purple and transparent physColor.Set(.2f, .1f, .2f, 1.f); opac = 0.8f; /* // the avatar proxy doesn't seem to work... not sure why? fProxyGen = TRACKED_NEW plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); fProxyGen->Init(this); */ } void plPXPhysicalControllerCore::ICreateController(const hsPoint3& pos) { NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); NxCapsuleControllerDesc desc; desc.position.x = pos.fX; desc.position.y = pos.fY; desc.position.z = pos.fZ; desc.upDirection = NX_Z; desc.slopeLimit = kSLOPELIMIT; desc.skinWidth = kPhysxSkinWidth; desc.stepOffset = STEP_OFFSET; desc.callback = &gMyReport; desc.userData = this; desc.radius = fRadius; desc.height = fHeight; desc.interactionFlag = NXIF_INTERACTION_EXCLUDE; //desc.interactionFlag = NXIF_INTERACTION_INCLUDE; fController = (NxCapsuleController*)gControllerMgr.createController(scene, desc); // Change the avatars shape groups. The avatar doesn't actually use these when // it's determining collision, but if you're standing still and an object runs // into you, it'll pass through without this. NxActor* actor = fController->getActor(); NxShape* shape = actor->getShapes()[0]; shape->setGroup(plSimDefs::kGroupAvatar); // need to create the non-bouncing object that can be used to trigger things while the avatar is doing behaviors. NxActorDesc actorDesc; NxCapsuleShapeDesc capDesc; capDesc.radius = fRadius; capDesc.height = fHeight; capDesc.group = plSimDefs::kGroupAvatar; actorDesc.shapes.pushBack(&capDesc); capDesc.materialIndex= plSimulationMgr::GetInstance()->GetMaterialIdx(scene, 0.0,0.0); actorDesc.globalPose=actor->getGlobalPose(); NxBodyDesc bodyDesc; bodyDesc.mass = AvatarMass; actorDesc.body = &bodyDesc; bodyDesc.flags = NX_BF_KINEMATIC; bodyDesc.flags |=NX_BF_DISABLE_GRAVITY ; actorDesc.name = "AvatarTriggerKinematicGuy"; fSeeking=false; try { fKinematicActor = scene->createActor(actorDesc); } catch (...) { hsAssert(false, "Actor creation crashed"); } // set the matrix to be the same as the controller's actor... that should orient it to be the same //fKinematicActor->setGlobalPose(actor->getGlobalPose()); // the proxy for the debug display //hsAssert(!fProxyGen, "Already have proxy gen, double read?"); hsColorRGBA physColor; hsScalar opac = 1.0f; // local avatar is light purple and transparent physColor.Set(.2f, .1f, .2f, 1.f); opac = 0.8f; /* // the avatar proxy doesn't seem to work... not sure why? fProxyGen = TRACKED_NEW plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); fProxyGen->Init(this); */ } void plPXPhysicalControllerCore::IDeleteController() { if (fController) { gControllerMgr.releaseController(*fController); fController = nil; if (fKinematicActor) { NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); scene->releaseActor(*fKinematicActor); fKinematicActor = nil; } plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); } } void plPXPhysicalControllerCore::IInformDetectors(bool entering,bool deferUntilNextSim=true) { static const NxU32 DetectorFlag= 1<GetScene(fWorldKey); int kNumofShapesToStore=30; NxCapsule cap; GetWorldSpaceCapsule(cap); NxShape* shapes[30]; int numCollided=scene->overlapCapsuleShapes(cap,NX_ALL_SHAPES,kNumofShapesToStore,shapes,NULL,DetectorFlag,NULL,true); for (int i=0;igetActor()); if (myactor) { plPXPhysical* physical = (plPXPhysical*)myactor->userData; if (physical) { bool doReport = physical->DoReportOn(plSimDefs::kGroupAvatar); if(doReport) { plCollideMsg* msg = TRACKED_NEW plCollideMsg; msg->fOtherKey = fOwner; msg->fEntering = entering; msg->AddReceiver(physical->GetObjectKey()); if(!deferUntilNextSim) { #ifndef PLASMA_EXTERNAL_RELEASE DetectorLog("Sending an %s msg to %s" , entering? "entering":"exit", physical->GetObjectKey()->GetName()); #endif msg->Send(); } else { #ifndef PLASMA_EXTERNAL_RELEASE DetectorLog("Queuing an %s msg to %s, which will be sent after the next simstep" , entering? "entering":"exit", physical->GetObjectKey()->GetName()); #endif //these will be fired in update prestep on the next lap fQueuedCollideMsgs.Append(msg); } } } } } #ifndef PLASMA_EXTERNAL_RELEASE DetectorLog("Done informing from plPXPhysicalControllerCore::IInformDetectors"); #endif } } void plPXPhysicalControllerCore::Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults) { collisionResults=0; if(fController) { NxVec3 dis(displacement.fX,displacement.fY,displacement.fZ); NxU32 colFlags = 0; this->fController->move(dis,collideWith,.00001,colFlags); if(colFlags&NXCC_COLLISION_DOWN)collisionResults|=kBottom; if(colFlags&NXCC_COLLISION_UP)collisionResults|=kTop; if(colFlags&&NXCC_COLLISION_SIDES)collisionResults|=kSides; } return; } void plPXPhysicalControllerCore::Enable(bool enable) { if (fEnabled != enable) { fEnabled = enable; if (fEnabled) fEnableChanged = true; else { // See ISendUpdates for why we don't re-enable right away fController->setCollision(fEnabled); } } } void plPXPhysicalControllerCore::SetSubworld(plKey world) { if (fWorldKey != world) { bool wasEnabled = fEnabled; #ifdef USE_PHYSX_CONVEXHULL_WORKAROUND // PHYSX FIXME - before leaving this world, sending leaving detector events if we are inside a convex hull detector hsPoint3 pos; IGetPositionSim(pos); plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,false); #endif // USE_PHYSX_CONVEXHULL_WORKAROUND //need to inform detectors in the old world that we are leaving IInformDetectors(false); //done informing old world SimLog("Changing subworlds!"); IDeleteController(); SimLog("Deleted old controller"); fWorldKey = world; if (GetSubworldCI()) fPrevSubworldW2L = GetSubworldCI()->GetWorldToLocal(); // Update our subworld position and rotation const plCoordinateInterface* subworldCI = GetSubworldCI(); if (subworldCI) { const hsMatrix44& w2s = fPrevSubworldW2L; hsMatrix44 l2s = w2s * fLastGlobalLoc; l2s.GetTranslate(&fLocalPosition); fLocalRotation.SetFromMatrix44(l2s); } else { fLastGlobalLoc.GetTranslate(&fLocalPosition); fLocalRotation.SetFromMatrix44(fLastGlobalLoc); } hsMatrix44 w2l; fLastGlobalLoc.GetInverse(&w2l); if (fProxyGen) fProxyGen->SetTransform(fLastGlobalLoc, w2l); // Update the physical position SimLog("creating new controller"); hsPoint3 PositionPlusOffset=fLocalPosition; PositionPlusOffset.fZ +=kPhysZOffset; //placing new controller and kinematic in the appropriate location ICreateController(PositionPlusOffset); RebuildCache(); } } const plCoordinateInterface* plPXPhysicalControllerCore::GetSubworldCI() const { if (fWorldKey) { plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); if (so) return so->GetCoordinateInterface(); } return nil; } // For the avatar SDL only void plPXPhysicalControllerCore::GetState(hsPoint3& pos, float& zRot) { // Temporarily use the position point while we get the z rotation fLocalRotation.NormalizeIfNeeded(); fLocalRotation.GetAngleAxis(&zRot, (hsVector3*)&pos); if (pos.fZ < 0) zRot = (2 * hsScalarPI) - zRot; // axis is backwards, so reverse the angle too pos = fLocalPosition; } void plPXPhysicalControllerCore::SetState(const hsPoint3& pos, float zRot) { plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); if (so) { hsQuat worldRot; hsVector3 zAxis(0.f, 0.f, 1.f); worldRot.SetAngleAxis(zRot, zAxis); hsMatrix44 l2w, w2l; worldRot.MakeMatrix(&l2w); l2w.SetTranslate(&pos); // Localize new position and rotation to global coords if we're in a subworld const plCoordinateInterface* ci = GetSubworldCI(); if (ci) { const hsMatrix44& subworldL2W = ci->GetLocalToWorld(); l2w = subworldL2W * l2w; } l2w.GetInverse(&w2l); so->SetTransform(l2w, w2l); so->FlushTransform(); } } // kinematic stuff .... should be just for when playing a behavior... void plPXPhysicalControllerCore::Kinematic(bool state) { if (fKinematic != state) { fKinematic = state; if (fKinematic) { // See ISendUpdates for why we don't re-enable right away fController->setCollision(false); #ifdef PHYSX_KINEMATIC_IS_DISABLED fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); #endif } else { fKinematicChanged = true; } } } bool plPXPhysicalControllerCore::IsKinematic() { return fKinematic; } void plPXPhysicalControllerCore::GetKinematicPosition(hsPoint3& pos) { pos.Set(-1,-1,-1); if ( fKinematicActor ) { NxVec3 klPos = fKinematicActor->getGlobalPosition(); pos.Set(hsScalar(klPos.x), hsScalar(klPos.y), hsScalar(klPos.z) - kPhysZOffset); } } void plPXPhysicalControllerCore::UpdatePoststep( hsScalar delSecs) { // Apparently the user data field of the controllers is broken // UInt32 count = gControllerMgr.getNbControllers(); // NxController* controllers = (NxController*)gControllerMgr.getControllers(); // // for (int i = 0; i < count; i++) // { // plPXPhysicalController* ac = (plPXPhysicalController*)controllers[i].getAppData(); for (int i = 0; i < gControllers.size(); i++) { plPXPhysicalControllerCore* ac = gControllers[i]; hsAssert(ac, "Bad avatar controller"); gControllerMgr.updateControllers(); if(ac->fMovementInterface) ac->Update(delSecs); if (ac->GetSubworldCI()) ac->fPrevSubworldW2L = ac->GetSubworldCI()->GetWorldToLocal(); else { if (!ac->fPrevSubworldW2L.IsIdentity()) ac->fPrevSubworldW2L.Reset(); } } } void plPXPhysicalControllerCore::UpdatePrestep(hsScalar delSecs) { for (int i = 0; i < gControllers.size(); i++) { plPXPhysicalControllerCore* ac = gControllers[i]; hsAssert(ac, "Bad avatar controller"); //FIXME #ifndef PLASMA_EXTERNAL_RELEASE ac->fDbgCollisionInfo.SetCount(0); #endif // PLASMA_EXTERNAL_RELEASE if (gRebuildCache&&ac->fController) ac->fController->reportSceneChanged(); //hsAssert(ac->fMovementInterface,"Updating a controller with out a movement strategy"); if(ac) { if(ac->fNeedsResize)ac->IHandleResize(); int storedCollideMsgs=ac->fQueuedCollideMsgs.GetCount(); if(storedCollideMsgs) { plSimulationMgr* simMgr=plSimulationMgr::GetInstance(); for(int j=0; jAddCollisionMsg(ac->fQueuedCollideMsgs[j]); } ac->fQueuedCollideMsgs.SetCount(0); } ac->Apply(delSecs); } #ifndef PLASMA_EXTERNAL_RELEASE if (fDebugDisplay) ac->IDrawDebugDisplay(); #endif // PLASMA_EXTERNAL_RELEASE } gRebuildCache = false; } void plPXPhysicalControllerCore::UpdatePostSimStep(hsScalar delSecs) { for (int i = 0; i < gControllers.size(); i++) { plPXPhysicalControllerCore* ac = gControllers[i]; hsAssert(ac, "Bad avatar controller"); ac->PostStep(delSecs); } } void plPXPhysicalControllerCore::HandleEnableChanged() { fEnableChanged = false; if(this->fBehavingLikeAnimatedPhys) { fController->setCollision(fEnabled); } else { fController->setCollision(false); } #ifdef USE_PHYSX_CONVEXHULL_WORKAROUND // PHYSX FIXME - after re-enabling check to see if we are inside any convex hull detector regions hsPoint3 pos; IGetPositionSim(pos); plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,fEnable); #endif // USE_PHYSX_CONVEXHULL_WORKAROUND //IInformDetectors(true); } void plPXPhysicalControllerCore::HandleKinematicChanged() { fKinematicChanged = false; if(this->fBehavingLikeAnimatedPhys) { fController->setCollision(true); } else { fController->setCollision(false); } #ifdef PHYSX_KINEMATIC_IS_DISABLED fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); #endif // PHYSX_KINEMATIC_IS_DISABLED } void plPXPhysicalControllerCore::HandleKinematicEnableNextUpdate() { fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); fKinematicEnableNextUpdate = false; } void plPXPhysicalControllerCore::IHandleResize() { UInt32 collideFlags = 1<GetScene(this->fWorldKey); // NxShape** response=TRACKED_NEW NxShape*[2]; NxVec3 center(fLocalPosition.fX,fLocalPosition.fY,fLocalPosition.fZ+fPreferedRadius); NxSegment Seg(center,center); const NxCapsule newCap(Seg,fPreferedRadius); int numintersect =myscene->checkOverlapCapsule(newCap,NX_ALL_SHAPES,collideFlags); //with new capsule dimensions check for overlap //with objects we would collide with if(numintersect==0) { fHeight=fPreferedHeight; fRadius=fPreferedRadius; fController->setRadius(fRadius); fController->setHeight(fHeight); fNeedsResize=false; } // delete[] response; } void plPXPhysicalControllerCore::SetControllerDimensions(hsScalar radius, hsScalar height) { fNeedsResize=false; if(fRadius!=radius) { fNeedsResize=true; } if(fHeight!=height) { fNeedsResize=true; } fPreferedRadius=radius; fPreferedHeight=height; } void plPXPhysicalControllerCore::LeaveAge() { SetPushingPhysical(nil); if(fWorldKey) this->SetSubworld(nil); this->fMovementInterface->LeaveAge(); } int plPXPhysicalControllerCore::SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, hsBool vsDynamics, hsBool vsStatics, UInt32& vsSimGroups, std::multiset< plControllerSweepRecord >& WhatWasHitOut) { NxCapsule tempCap; tempCap.p0 =plPXConvert::Point( startPos); tempCap.p0.z = tempCap.p0.z + fPreferedRadius; tempCap.radius = fPreferedRadius ; tempCap.p1 = tempCap.p0; tempCap.p1.z = tempCap.p1.z + fPreferedHeight; NxVec3 vec; vec.x = endPos.fX - startPos.fX; vec.y = endPos.fY - startPos.fY; vec.z = endPos.fZ - startPos.fZ; int numberofHits = 0; int HitsReturned = 0; WhatWasHitOut.clear(); NxScene *myscene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); NxSweepQueryHit whatdidIhit[10]; unsigned int flags = NX_SF_ALL_HITS; if(vsDynamics) flags |= NX_SF_DYNAMICS; if(vsStatics) flags |= NX_SF_STATICS; numberofHits = myscene->linearCapsuleSweep(tempCap, vec, flags, nil, 10, whatdidIhit, nil, vsSimGroups); if(numberofHits) {//we hit a dynamic object lets make sure it is not animatable for(int i=0; igetActor().userData; CurrentHit.Norm.fX = whatdidIhit[i].normal.x; CurrentHit.Norm.fY = whatdidIhit[i].normal.y; CurrentHit.Norm.fZ = whatdidIhit[i].normal.z; if(CurrentHit.ObjHit != nil) { hsPoint3 where; where.fX = whatdidIhit[i].point.x; where.fY = whatdidIhit[i].point.y; where.fZ = whatdidIhit[i].point.z; CurrentHit.locHit = where; CurrentHit.TimeHit = whatdidIhit[i].t ; WhatWasHitOut.insert(CurrentHit); HitsReturned++; } } } return HitsReturned; } void plPXPhysicalControllerCore::BehaveLikeAnimatedPhysical(hsBool actLikeAnAnimatedPhys) { hsAssert(fKinematicActor, "Changing behavior, but plPXPhysicalControllerCore has no Kinematic actor associated with it"); if(fBehavingLikeAnimatedPhys!=actLikeAnAnimatedPhys) { fBehavingLikeAnimatedPhys=actLikeAnAnimatedPhys; if(fKinematicActor) { if(actLikeAnAnimatedPhys) { //need to set BX Kinematic if true and kill any rotation fController->setCollision(fEnabled); fKinematicActor->raiseBodyFlag(NX_BF_KINEMATIC); fKinematicActor->clearBodyFlag(NX_BF_FROZEN_ROT); fKinematicActor->raiseBodyFlag(NX_BF_DISABLE_GRAVITY); } else { //don't really use the controller now don't bother with collisions fController->setCollision(false); fKinematicActor->clearBodyFlag(NX_BF_KINEMATIC); fKinematicActor->raiseBodyFlag(NX_BF_FROZEN_ROT_X); fKinematicActor->raiseBodyFlag(NX_BF_FROZEN_ROT_Y); fKinematicActor->clearBodyFlag(NX_BF_DISABLE_GRAVITY); } } } } hsBool plPXPhysicalControllerCore::BehavingLikeAnAnimatedPhysical() { hsAssert(fKinematicActor, "plPXPhysicalControllerCore is missing a kinematic actor"); return fBehavingLikeAnimatedPhys; } void plPXPhysicalControllerCore::SetLinearVelocity(const hsVector3& linearVel) { plPhysicalControllerCore::SetLinearVelocity(linearVel); if(fKinematicActor && !fBehavingLikeAnimatedPhys) { NxVec3 vel= plPXConvert::Vector(linearVel); fKinematicActor->setLinearVelocity(vel); } } void plPXPhysicalControllerCore::SetAngularVelocity(const hsScalar angvel) { plPhysicalControllerCore::SetAngularVelocity(angvel); if(fKinematicActor && !fBehavingLikeAnimatedPhys) { NxVec3 vel(0.0f, 0.0f, angvel); fKinematicActor->setAngularVelocity(vel); } } void plPXPhysicalControllerCore::SetVelocities(const hsVector3& linearVel, hsScalar angVel) { SetLinearVelocity(linearVel); SetAngularVelocity(angVel); } void plPXPhysicalControllerCore::IMatchControllerToKinematic() { NxExtendedVec3 newpos; NxVec3 pos=fKinematicActor->getGlobalPosition(); newpos.x=pos.x; newpos.y=pos.y; newpos.z=pos.z; fController->setPosition(newpos); } const hsVector3& plPXPhysicalControllerCore::GetLinearVelocity() { if(BehavingLikeAnAnimatedPhysical()) return fLinearVelocity; else { fLinearVelocity = plPXConvert::Vector(fKinematicActor->getLinearVelocity()); return fLinearVelocity; } } #include "../plSurface/hsGMaterial.h" #include "../plSurface/plLayerInterface.h" // Make a visible object that can be viewed by users for debugging purposes. plDrawableSpans* plPXPhysicalControllerCore::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) { plDrawableSpans* myDraw = addTo; hsBool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); float radius = fRadius; myDraw = plDrawableGenerator::GenerateSphericalDrawable(fLocalPosition, radius, mat, fLastGlobalLoc, blended, nil, &idx, myDraw); /* plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); if (so) { hsBool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); myDraw = plDrawableGenerator::GenerateConicalDrawable(fRadius*10, fHeight*10, mat, so->GetLocalToWorld(), blended, nil, &idx, myDraw); } */ return myDraw; } #ifndef PLASMA_EXTERNAL_RELEASE #include "../plPipeline/plDebugText.h" void plPXPhysicalControllerCore::IDrawDebugDisplay() { plDebugText &debugTxt = plDebugText::Instance(); char strBuf[ 2048 ]; int lineHeight = debugTxt.GetFontSize() + 4; UInt32 scrnWidth, scrnHeight; debugTxt.GetScreenSize( &scrnWidth, &scrnHeight ); int y = 10; int x = 10; sprintf(strBuf, "Controller Count: %d", gControllers.size()); debugTxt.DrawString(x, y, strBuf); y += lineHeight; debugTxt.DrawString(x, y, "Avatar Collisions:"); y += lineHeight; int i; for (i = 0; i < fDbgCollisionInfo.GetCount(); i++) { hsVector3 normal = fDbgCollisionInfo[i].fNormal; char *overlapStr = fDbgCollisionInfo[i].fOverlap ? "yes" : "no"; hsScalar angle = hsScalarRadToDeg(hsACosine(normal * hsVector3(0, 0, 1))); sprintf(strBuf, " Obj: %s, Normal: (%.2f, %.2f, %.2f), Angle(%.1f), Overlap(%3s)", fDbgCollisionInfo[i].fSO->GetKeyName(), normal.fX, normal.fY, normal.fZ, angle, overlapStr); debugTxt.DrawString(x, y, strBuf); y += lineHeight; } } #endif PLASMA_EXTERNAL_RELEASE