/*==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 . 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