diff --git a/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp b/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp index 024f6183..d893d216 100644 --- a/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp +++ b/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp @@ -57,7 +57,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvatar/plAvatarMgr.h" #include "plAvatar/plArmatureMod.h" -#include "plAvatar/plAvCallbackAction.h" +#include "plAvatar/plPhysicalControllerCore.h" bool plListener::fPrintDbgInfo = false; diff --git a/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp b/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp index 1488eaf1..852490d3 100644 --- a/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp +++ b/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp @@ -63,7 +63,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pnSceneObject/plSimulationInterface.h" #include "plAvatar/plAvatarMgr.h" #include "plAvatar/plArmatureMod.h" -#include "plAvatar/plAvCallbackAction.h" +#include "plAvatar/plPhysicalControllerCore.h" // new stuff diff --git a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp index 0fce342f..aafb26e2 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp @@ -554,7 +554,7 @@ bool pySceneObject::IsAvatar() return false; } -#include "plAvatar/plAvCallbackAction.h" +#include "plAvatar/plPhysicalControllerCore.h" PyObject* pySceneObject::GetAvatarVelocity() { diff --git a/Sources/Plasma/PubUtilLib/plAvatar/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plAvatar/CMakeLists.txt index c8b82c78..f687d1f1 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/plAvatar/CMakeLists.txt @@ -15,7 +15,6 @@ set(plAvatar_SOURCES plAGMasterSDLModifier.cpp plAGModifier.cpp plAnimStage.cpp - plAntiGravAction.cpp plArmatureEffects.cpp plArmatureMod.cpp plAvatarClothing.cpp @@ -32,7 +31,6 @@ set(plAvatar_SOURCES plAvBrainHuman.cpp plAvBrainRideAnimatedPhysical.cpp plAvBrainSwim.cpp - plAvCallbackAction.cpp plAvLadderModifier.cpp plAvTaskBrain.cpp plAvTaskSeek.cpp @@ -60,7 +58,6 @@ set(plAvatar_HEADERS plAGMasterSDLModifier.h plAGModifier.h plAnimStage.h - plAntiGravAction.h plArmatureEffects.h plArmatureMod.h plAvatarClothing.h @@ -78,7 +75,6 @@ set(plAvatar_HEADERS plAvBrainHuman.h plAvBrainRideAnimatedPhysical.h plAvBrainSwim.h - plAvCallbackAction.h plAvDefs.h plAvLadderModifier.h plAvTask.h diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp index 3b158848..f5dc5ac5 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp @@ -39,7 +39,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plAvCallbackAction.h" // must be first: references havok new // singular #include "plAnimStage.h" diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp deleted file mode 100644 index d5b4da3c..00000000 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/*==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==*/ -#if 0 -// havok first -#include -#include - -#include "plAntiGravAction.h" - -#include "pnSceneObject/plSceneObject.h" -#include "plHavok1/plHKPhysical.h" -#include "plAvatar/plSwimRegion.h" -#include "hsTimer.h" - -// This is meant to be a specific physicsAction for the swim behavior -plAntiGravAction::plAntiGravAction(plHKPhysical *physical, plAGApplicator *rootApp) : - plAnimatedCallbackAction(physical, rootApp), - fOnGround(false), - fBuoyancy(1.f), - fSurfaceHeight(0.f), - fCurrentRegion(nil), - fHadContacts(false) -{ -} - -plSimDefs::ActionType plAntiGravAction::GetType() -{ - return plSimDefs::kAntiGravAction; -} - -void plAntiGravAction::apply(Havok::Subspace &space, Havok::hkTime time) -{ - double elapsed = time.asDouble() - getRefresh().asDouble(); - setRefresh(time); - - IAdjustBuoyancy(); - Havok::RigidBody *body = fPhysical->GetBody(); - float mass = body->getMass(); - Havok::Vector3 gravity = space.getGravity(); - Havok::Vector3 force = -gravity * (mass * fBuoyancy); - body->applyForce(force); - - hsVector3 vel; - fPhysical->GetLinearVelocitySim(vel); - fAnimPosVel.fZ = vel.fZ; - - hsVector3 linCurrent(0.f, 0.f, 0.f); - float angCurrent = 0.f; - if (fCurrentRegion != nil) - fCurrentRegion->GetCurrent(fPhysical, linCurrent, angCurrent, (float)elapsed); - - int numContacts = fPhysical->GetNumContacts(); - fHadContacts = (numContacts > 0); - - const Havok::Vector3 straightUp(0.0f, 0.0f, 1.0f); - fOnGround = false; - int i; - for (i = 0; i < numContacts; i++) - { - const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); - float dotUp = straightUp.dot(contact->m_normal); - if (dotUp > .5) - { - fOnGround = true; - break; - } - } - - fPhysical->SetLinearVelocitySim(fAnimPosVel + linCurrent); - fPhysical->SetAngularVelocitySim(hsVector3(0.f, 0.f, fAnimAngVel + fTurnStr + angCurrent)); -} - -void plAntiGravAction::SetSurface(plSwimRegionInterface *region, float surfaceHeight) -{ - fCurrentRegion = region; - if (region != nil) - fSurfaceHeight = surfaceHeight; -} - -void plAntiGravAction::IAdjustBuoyancy() -{ - // "surface depth" refers to the depth our handle object should be below - // the surface for the avatar to be "at the surface" - static const float surfaceDepth = 4.0f; - // 1.0 = neutral buoyancy - // 0 = no buoyancy (normal gravity) - // 2.0 = opposite of gravity, floating upwards - static const float buoyancyAtSurface = 1.0f; - - if (fCurrentRegion == nil) - { - fBuoyancy = 0.f; - return; - } - - hsMatrix44 l2w, w2l; - fPhysical->GetTransform(l2w, w2l); - float depth = fSurfaceHeight - surfaceDepth - l2w.GetTranslate().fZ; - if (depth < -1) - fBuoyancy = 0.f; // Same as being above ground. Plain old gravity. - else if (depth < 0) - fBuoyancy = 1 + depth; - else - { - hsVector3 vel; - fPhysical->GetLinearVelocitySim(vel); - if (vel.fZ > 0) - { - if (vel.fZ > fCurrentRegion->fMaxUpwardVel) - { - vel.fZ = fCurrentRegion->fMaxUpwardVel; - fPhysical->SetLinearVelocitySim(vel); - } - else - { - if (depth > 1) - fBuoyancy = fCurrentRegion->fUpBuoyancy; - else - fBuoyancy = (fCurrentRegion->fUpBuoyancy - 1) * depth + 1; - } - } - else - { - if (depth > 1) - fBuoyancy = fCurrentRegion->fDownBuoyancy; - else - fBuoyancy = (fCurrentRegion->fDownBuoyancy - 1) * depth + 1; - } - } -} - -#endif \ No newline at end of file diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h b/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h deleted file mode 100644 index ee4049ab..00000000 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h +++ /dev/null @@ -1,79 +0,0 @@ -/*==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==*/ -#if 0//ndef PL_ANTI_GRAV_ACTION_H -#define PL_ANTI_GRAV_ACTION_H - -#include "plAvCallbackAction.h" - -class plSwimRegionInterface; - -class plAntiGravAction : public plAnimatedCallbackAction -{ -public: - plAntiGravAction(plHKPhysical *physical, plAGApplicator *rootApp); - - /** Return the type of the action as defined in the enum plSimDefs::ActionType. - Used to retrieve actions by entity/type indexing, and to - reuse actions that can be shared between entities. */ - virtual plSimDefs::ActionType GetType(); - - /** Called by Havok at substep frequency. */ - void apply(Havok::Subspace &s, Havok::hkTime time); - - void SetSurface(plSwimRegionInterface *region, float surfaceHeight); - float GetBuoyancy() { return fBuoyancy; } - bool IsOnGround() { return fOnGround; } - bool HadContacts() { return fHadContacts; } - -protected: - void IAdjustBuoyancy(); - - bool fOnGround; - bool fHadContacts; - float fBuoyancy; - float fSurfaceHeight; - plSwimRegionInterface *fCurrentRegion; -}; - -#endif - - diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp index 1d53edca..526710a7 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp @@ -39,7 +39,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plAvCallbackAction.h" #include "plStatusLog/plStatusLog.h" #include "plArmatureEffects.h" diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp index 018ccb09..c1b5cbe1 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp @@ -54,7 +54,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvBrainHuman.h" #include "plMatrixChannel.h" #include "plAvatarTasks.h" -#include "plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" #include "plAvBrainCritter.h" // global @@ -534,8 +534,7 @@ void plArmatureModBase::EnablePhysics(bool status, uint16_t reason /* = kDisable // i.e. normal enabled physical void plArmatureModBase::EnablePhysicsKinematic(bool status) { - if (fController) - fController->Kinematic(status); + EnablePhysics(!status, kDisableReasonKinematic); } void plArmatureModBase::EnableDrawing(bool status, uint16_t reason /* = kDisableReasonUnknown */) @@ -1994,7 +1993,11 @@ bool plArmatureMod::ValidatePhysics() return false; if (!fController) - fController = plPhysicalControllerCore::Create(GetTarget(0)->GetKey(), fPhysHeight, fPhysWidth); + { + // The kinematic actor is made taller if the avatar is human (male or female) + fController = plPhysicalControllerCore::Create(GetTarget(0)->GetKey(), fPhysHeight, + fPhysWidth, (fBodyType == kBoneBaseMale || fBodyType == kBoneBaseFemale)); + } if (fController) { @@ -2670,19 +2673,7 @@ void plArmatureMod::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *str debugTxt.DrawString(x, y, strBuf); y += lineHeight; - hsPoint3 kPos; - const char *kinematic = "n.a."; const char* frozen = "n.a."; - if (fController) - { - fController->GetKinematicPosition(kPos); - kinematic = fController->IsKinematic() ? "on" : "off"; - } - sprintf(strBuf, "kinematc(world): %.2f, %.2f, %.2f Kinematic: %3s", - kPos.fX, kPos.fY, kPos.fZ,kinematic); - debugTxt.DrawString(x, y, strBuf); - y += lineHeight; - if (fController) frozen = fController->IsEnabled() ? "no" : "yes"; diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h index c771a7a8..4f2a69c4 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h @@ -146,6 +146,7 @@ public: kDisableReasonCCR = 0x0008, kDisableReasonVehicle = 0x0010, kDisableReasonGenericBrain = 0x0020, + kDisableReasonKinematic = 0x0040 }; void EnablePhysics(bool status, uint16_t reason = kDisableReasonUnknown); void EnablePhysicsKinematic(bool status); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp index dcc7bfdb..71314a78 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp @@ -40,7 +40,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ -#include "plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" #include "plAvBrainCritter.h" #include "plAvBrainHuman.h" #include "plArmatureMod.h" @@ -125,7 +125,7 @@ protected: /////////////////////////////////////////////////////////////////////////////// -plAvBrainCritter::plAvBrainCritter(): fCallbackAction(nil), fCurMode(kIdle), fNextMode(kIdle), fFadingNextBehavior(true), +plAvBrainCritter::plAvBrainCritter(): fWalkingStrategy(nil), fCurMode(kIdle), fNextMode(kIdle), fFadingNextBehavior(true), fAvoidingAvatars(false), fFinalGoalPos(0, 0, 0), fImmediateGoalPos(0, 0, 0), fDotGoal(0), fAngRight(0) { @@ -143,8 +143,8 @@ plAvBrainCritter::~plAvBrainCritter() fBehaviors[i] = nil; } - delete fCallbackAction; - fCallbackAction = nil; + delete fWalkingStrategy; + fWalkingStrategy = nil; fUserBehaviors.clear(); fReceivers.clear(); @@ -167,8 +167,8 @@ bool plAvBrainCritter::Apply(double time, float elapsed) IProcessBehavior(time, elapsed); // just continue with the currently running one // update our controller to keep us turned and moving to where we want to go - fCallbackAction->RecalcVelocity(time, time - elapsed); - fCallbackAction->SetTurnStrength(IGetTurnStrength(time)); + fWalkingStrategy->SetTurnStrength(IGetTurnStrength(time)); + fWalkingStrategy->RecalcVelocity(time, elapsed); return plArmatureBrain::Apply(time, elapsed); } @@ -188,13 +188,13 @@ void plAvBrainCritter::Activate(plArmatureModBase* avMod) IInitBaseAnimations(); // create the controller if we haven't done so already - if (!fCallbackAction) + if (!fWalkingStrategy) { plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); plPhysicalControllerCore* controller = avMod->GetController(); - fCallbackAction = new plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); + fWalkingStrategy = new plWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); + controller->SetMovementStrategy(fWalkingStrategy); } // tell people that care that we are good to go @@ -224,7 +224,7 @@ void plAvBrainCritter::Resume() // fade in the idle fNextMode = kIdle; - fCallbackAction->Reset(false); + fWalkingStrategy->Reset(false); plArmatureBrain::Resume(); } diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h index 0615932e..561797be 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h @@ -47,7 +47,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pnKeyedObject/plKey.h" class plArmatureMod; -class plWalkingController; +class plWalkingStrategy; class plAIMsg; class plAvBrainCritter : public plArmatureBrain @@ -134,8 +134,6 @@ public: virtual void DumpToDebugDisplay(int& x, int& y, int lineHeight, char* strBuf, plDebugText& debugTxt); - plWalkingController* GetCallbackAction() {return fCallbackAction;} - // For the console static bool fDrawDebug; @@ -159,7 +157,7 @@ protected: std::vector IAvatarsICanSee() const; std::vector IAvatarsICanHear() const; - plWalkingController* fCallbackAction; + plWalkingStrategy* fWalkingStrategy; int fCurMode; // current behavior we are running int fNextMode; // the next behavior to run (-1 if we aren't switching on next eval) bool fFadingNextBehavior; // is the next behavior supposed to blend? diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp index a3b2cacd..0712a2fa 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp @@ -42,7 +42,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com // local includes #include "plAvBrainDrive.h" #include "plArmatureMod.h" -#include "plAvCallbackAction.h" // global includes #include "hsTimer.h" diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp index 24bb67ba..a014e977 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp @@ -39,7 +39,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plAvCallbackAction.h" // havok-contaminated file: must go first // singular #include "plAvBrainGeneric.h" @@ -94,7 +93,6 @@ plAvBrainGeneric::plAvBrainGeneric() fFadeIn(0.0f), fFadeOut(0.0f), fMoveMode(kMoveRelative), - fCallbackAction(nil), fBodyUsage(plAGAnim::kBodyUnknown) { } @@ -122,7 +120,6 @@ plAvBrainGeneric::plAvBrainGeneric(plAnimStageVec *stages, fFadeIn(fadeIn), fFadeOut(fadeOut), fMoveMode(moveMode), - fCallbackAction(nil), fBodyUsage(plAGAnim::kBodyUnknown) { } @@ -141,7 +138,6 @@ plAvBrainGeneric::plAvBrainGeneric(uint32_t exitFlags, float fadeIn, float fadeO fFadeIn(fadeIn), fFadeOut(fadeOut), fMoveMode(moveMode), - fCallbackAction(nil), fBodyUsage(plAGAnim::kBodyUnknown) { diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h index 4a719153..305074aa 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h @@ -49,7 +49,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com class plAnimStage; class plAnimStageVec; class plAvBrainGenericMsg; -class plHorizontalFreezeAction; class plNotifyMsg; /** \class plAvBrainGeneric @@ -300,7 +299,6 @@ protected: int fCurStage; // which stage are we playing? (zero-based) BrainType fType; // what type of brain are we? uint32_t fExitFlags; // what will cause us to exit? - plHorizontalFreezeAction *fCallbackAction; bool fForward; // are we currently moving forward or backward through the stages? // this is used by the "auto-" movement types in the stages diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp index fe7f4923..05422c7b 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp @@ -40,9 +40,9 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ -#include "plAvCallbackAction.h" // subclasses a havok object; must be in first include section #include "HeadSpin.h" +#include "plPhysicalControllerCore.h" #include "plAvBrainHuman.h" #include "plAvBrainClimb.h" #include "plAvBrainDrive.h" @@ -140,7 +140,7 @@ plAvBrainHuman::TurnCurve plAvBrainHuman::GetTurnCurve(bool walk) plAvBrainHuman::plAvBrainHuman(bool isActor /* = false */) : fHandleAGMod(nil), fStartedTurning(-1.0f), - fCallbackAction(nil), + fWalkingStrategy(nil), fPreconditions(0), fIsActor(isActor) { @@ -154,9 +154,9 @@ bool plAvBrainHuman::Apply(double timeNow, float elapsed) #endif // SetTurnStrength runs first to make sure it's set to a sane value // (or cleared). RunStandardBehaviors may overwrite it. - fCallbackAction->SetTurnStrength(IGetTurnStrength(timeNow)); + fWalkingStrategy->SetTurnStrength(IGetTurnStrength(timeNow)); RunStandardBehaviors(timeNow, elapsed); - fCallbackAction->RecalcVelocity(timeNow, timeNow - elapsed, (fPreconditions & plHBehavior::kBehaviorTypeNeedsRecalcMask)); + fWalkingStrategy->RecalcVelocity(timeNow, elapsed, (fPreconditions & plHBehavior::kBehaviorTypeNeedsRecalcMask)); plArmatureBrain::Apply(timeNow, elapsed); #ifndef _DEBUG @@ -177,13 +177,13 @@ void plAvBrainHuman::Activate(plArmatureModBase *avMod) IInitBoneMap(); IInitAnimations(); - if (!fCallbackAction) + if (!fWalkingStrategy) { plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); plPhysicalControllerCore* controller = avMod->GetController(); - fCallbackAction = new plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); + fWalkingStrategy = new plWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); + controller->SetMovementStrategy(fWalkingStrategy); } @@ -328,8 +328,8 @@ plAvBrainHuman::~plAvBrainHuman() delete fBehaviors[i]; fBehaviors.Reset(); - delete fCallbackAction; - fCallbackAction = nil; + delete fWalkingStrategy; + fWalkingStrategy = nil; } void plAvBrainHuman::Deactivate() @@ -361,7 +361,7 @@ void plAvBrainHuman::Resume() if (fAvMod->GetInputFlag(S_PUSH_TO_TALK)) IChatOn(); - fCallbackAction->Reset(false); + fWalkingStrategy->Reset(false); plArmatureBrain::Resume(); } @@ -465,25 +465,23 @@ bool plAvBrainHuman::MsgReceive(plMessage * msg) { if(ride->Entering()) { - //plAvBrainRideAnimatedPhysical *rideBrain = new plAvBrainRideAnimatedPhysical(); - //fAvMod->PushBrain(rideBrain); - delete fCallbackAction; + // Switch to dynamic walking strategy + delete fWalkingStrategy; plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); plPhysicalControllerCore* controller = fAvMod->GetController(); - fCallbackAction= new plRidingAnimatedPhysicalController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); - + fWalkingStrategy = new plDynamicWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); + controller->SetMovementStrategy(fWalkingStrategy); } else { - delete fCallbackAction; + // Restore default walking strategy + delete fWalkingStrategy; plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); plPhysicalControllerCore* controller = fAvMod->GetController(); - fCallbackAction= new plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); - //hsStatusMessage("Got an exiting ride animated physical message"); + fWalkingStrategy = new plWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); + controller->SetMovementStrategy(fWalkingStrategy); } } @@ -557,8 +555,8 @@ float plAvBrainHuman::IGetTurnStrength(double timeNow) // Turning based on keypress if ((turnLeftStrength > 0.f) || (turnRightStrength > 0.f) - || (!fCallbackAction->IsOnGround() - && !fCallbackAction->IsControlledFlight()) + || (!fWalkingStrategy->IsOnGround() + && !fWalkingStrategy->IsControlledFlight()) ) { float t = (float)(timeNow - fStartedTurning); float turnSpeed; @@ -590,7 +588,7 @@ float plAvBrainHuman::IGetTurnStrength(double timeNow) result += fAvMod->GetKeyTurnStrength() * turnSpeed; } - if (!fCallbackAction->IsControlledFlight()) + if (!fWalkingStrategy->IsControlledFlight()) result += fAvMod->GetAnalogTurnStrength() * maxTurnSpeed; return result; @@ -647,7 +645,7 @@ void plAvBrainHuman::ResetIdle() void plAvBrainHuman::IdleOnly(bool instantOff) { - if (!fCallbackAction) + if (!fWalkingStrategy) return; float rate = instantOff ? 0.f : 1.f; @@ -673,7 +671,7 @@ bool plAvBrainHuman::IsMovementZeroBlend() void plAvBrainHuman::TurnToPoint(hsPoint3 point) { - if (!fCallbackAction->IsOnGround() || IsRunningTask() || fAvMod->GetCurrentBrain() != this || !IsMovementZeroBlend()) + if (!fWalkingStrategy->IsOnGround() || IsRunningTask() || fAvMod->GetCurrentBrain() != this || !IsMovementZeroBlend()) return; hsPoint3 avPos; @@ -869,23 +867,23 @@ void plAvBrainHuman::Spawn(double timeNow) bool plAvBrainHuman::LeaveAge() { plPhysicalControllerCore* controller = fAvMod->GetController(); - if(!controller->BehavingLikeAnAnimatedPhysical()) + + // If our current walking strategy is dynamic, restore the default kinematic strategy. + if (!fWalkingStrategy->IsKinematic()) { - controller->BehaveLikeAnimatedPhysical(true); - delete fCallbackAction; + delete fWalkingStrategy; plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); - fCallbackAction= new plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); + fWalkingStrategy = new plWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); } + + fWalkingStrategy->Reset(true); + plArmatureBrain::LeaveAge(); - - // pin the physical so it doesn't fall when the world is deleted fAvMod->EnablePhysics(false); - // this will get set to true when we hit ground - fCallbackAction->Reset(true); + return false; } @@ -895,11 +893,10 @@ void plAvBrainHuman::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *st debugTxt.DrawString(x, y, strBuf); y += lineHeight; - const char *grounded = fCallbackAction->IsOnGround() ? "yes" : "no"; - const char *falseGrounded = fCallbackAction->IsOnFalseGround() ? "yes" : "no"; - const char *pushing = (fCallbackAction->GetPushingPhysical() ? (fCallbackAction->GetFacingPushingPhysical() ? "facing" : "behind") : "none"); - sprintf(strBuf, "Ground: %3s, FalseGround: %3s, AirTime: %5.2f (Peak: %5.2f), PushingPhys: %6s", - grounded, falseGrounded, fCallbackAction->GetAirTime(), fCallbackAction->GetImpactTime(), pushing); + const char *grounded = fWalkingStrategy->IsOnGround() ? "yes" : "no"; + const char *pushing = (fWalkingStrategy->GetPushingPhysical() ? (fWalkingStrategy->GetFacingPushingPhysical() ? "facing" : "behind") : "none"); + sprintf(strBuf, "Ground: %3s, AirTime: %5.2f (Peak: %5.2f), PushingPhys: %6s", + grounded, fWalkingStrategy->GetAirTime(), fWalkingStrategy->GetImpactTime(), pushing); debugTxt.DrawString(x, y, strBuf); y += lineHeight; @@ -1012,8 +1009,8 @@ bool Run::PreCondition(double time, float elapsed) { if (fAnim) { - if (fAvMod->ForwardKeyDown() && fAvMod->FastKeyDown() && fHuBrain->fCallbackAction->IsOnGround() && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + if (fAvMod->ForwardKeyDown() && fAvMod->FastKeyDown() && fHuBrain->fWalkingStrategy->IsOnGround() && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) return true; } return false; @@ -1023,8 +1020,8 @@ bool Walk::PreCondition(double time, float elapsed) { if (fAnim) { - if (fAvMod->ForwardKeyDown() && !fAvMod->FastKeyDown() && fHuBrain->fCallbackAction->IsOnGround() && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + if (fAvMod->ForwardKeyDown() && !fAvMod->FastKeyDown() && fHuBrain->fWalkingStrategy->IsOnGround() && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) return true; } return false; @@ -1034,8 +1031,8 @@ bool WalkBack::PreCondition(double time, float elapsed) { if (fAnim) { - if (fAvMod->BackwardKeyDown() && !fAvMod->ForwardKeyDown() && fHuBrain->fCallbackAction->IsOnGround() && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + if (fAvMod->BackwardKeyDown() && !fAvMod->ForwardKeyDown() && fHuBrain->fWalkingStrategy->IsOnGround() && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) return true; } return false; @@ -1048,7 +1045,7 @@ bool StepLeft::PreCondition(double time, float elapsed) return ((fAvMod->StrafeLeftKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnLeftKeyDown())) && !(fAvMod->StrafeRightKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnRightKeyDown())) && !(fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && - fHuBrain->fCallbackAction->IsOnGround()); + fHuBrain->fWalkingStrategy->IsOnGround()); } return false; } @@ -1060,7 +1057,7 @@ bool StepRight::PreCondition(double time, float elapsed) return ((fAvMod->StrafeRightKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnRightKeyDown())) && !(fAvMod->StrafeLeftKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnLeftKeyDown())) && !(fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && - fHuBrain->fCallbackAction->IsOnGround()); + fHuBrain->fWalkingStrategy->IsOnGround()); } return false; } @@ -1101,8 +1098,8 @@ bool MovingTurnLeft::PreCondition(double time, float elapsed) { if (fAvMod->GetTurnStrength() > 0) { - if (fHuBrain->fCallbackAction->IsOnGround() && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + if (fHuBrain->fWalkingStrategy->IsOnGround() && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) return true; } return false; @@ -1112,8 +1109,8 @@ bool MovingTurnRight::PreCondition(double time, float elapsed) { if (fAvMod->GetTurnStrength() < 0) { - if (fHuBrain->fCallbackAction->IsOnGround() && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + if (fHuBrain->fWalkingStrategy->IsOnGround() && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) return true; } @@ -1122,14 +1119,14 @@ bool MovingTurnRight::PreCondition(double time, float elapsed) void Jump::IStart() { - fHuBrain->fCallbackAction->EnableControlledFlight(true); + fHuBrain->fWalkingStrategy->EnableControlledFlight(true); plHBehavior::IStart(); } void Jump::IStop() { - fHuBrain->fCallbackAction->EnableControlledFlight(false); + fHuBrain->fWalkingStrategy->EnableControlledFlight(false); plHBehavior::IStop(); } @@ -1140,7 +1137,7 @@ bool StandingJump::PreCondition(double time, float elapsed) { if (GetStrength() > 0.f) { - if (!fHuBrain->fCallbackAction->IsControlledFlight() || + if (!fHuBrain->fWalkingStrategy->IsControlledFlight() || fAnim->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) >= fAnim->GetTimeConvert()->GetEnd()) { return false; @@ -1152,7 +1149,7 @@ bool StandingJump::PreCondition(double time, float elapsed) if (fAvMod->JumpKeyDown() && !fAvMod->ForwardKeyDown() && fAnim->GetBlend() == 0.0f && - fHuBrain->fCallbackAction->IsOnGround()) + fHuBrain->fWalkingStrategy->IsOnGround()) { if (fAvMod->ConsumeJump()) return true; @@ -1168,7 +1165,7 @@ bool WalkingJump::PreCondition(double time, float elapsed) { if (GetStrength() > 0.f) { - if (!fHuBrain->fCallbackAction->IsControlledFlight() || + if (!fHuBrain->fWalkingStrategy->IsControlledFlight() || fAnim->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) >= fAnim->GetTimeConvert()->GetEnd()) { return false; @@ -1181,8 +1178,8 @@ bool WalkingJump::PreCondition(double time, float elapsed) !fAvMod->FastKeyDown() && fAvMod->ForwardKeyDown() && fAnim->GetBlend() == 0.0f && - fHuBrain->fCallbackAction->IsOnGround() && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + fHuBrain->fWalkingStrategy->IsOnGround() && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) { if (fAvMod->ConsumeJump()) return true; @@ -1198,7 +1195,7 @@ bool RunningJump::PreCondition(double time, float elapsed) { if (GetStrength() > 0.f) { - if (!fHuBrain->fCallbackAction->IsControlledFlight() || + if (!fHuBrain->fWalkingStrategy->IsControlledFlight() || fAnim->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) >= fAnim->GetTimeConvert()->GetEnd()) { return false; @@ -1211,8 +1208,8 @@ bool RunningJump::PreCondition(double time, float elapsed) fAvMod->ForwardKeyDown() && fAvMod->FastKeyDown() && fAnim->GetBlend() == 0.0f && - fHuBrain->fCallbackAction->IsOnGround() && - (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + fHuBrain->fWalkingStrategy->IsOnGround() && + (!fHuBrain->fWalkingStrategy->GetPushingPhysical() || !fHuBrain->fWalkingStrategy->GetFacingPushingPhysical())) { if (fAvMod->ConsumeJump()) return true; @@ -1237,13 +1234,13 @@ bool RunningImpact::PreCondition(double time, float elapsed) { if (fDuration > 0.0f) fDuration = fDuration - elapsed; - else if (fHuBrain->fCallbackAction->IsOnGround() && fHuBrain->fCallbackAction->GetImpactTime() > kMinAirTime) + else if (fHuBrain->fWalkingStrategy->IsOnGround() && fHuBrain->fWalkingStrategy->GetImpactTime() > kMinAirTime) { - if (fHuBrain->fCallbackAction->GetImpactVelocity().fZ < -kMinImpactVel) + if (fHuBrain->fWalkingStrategy->GetImpactVelocity().fZ < -kMinImpactVel) { - if (fHuBrain->fCallbackAction->GetImpactVelocity().fY < kRunningImpactThresh) + if (fHuBrain->fWalkingStrategy->GetImpactVelocity().fY < kRunningImpactThresh) { - fMaxBlend = 0.5f + (0.5f * (-fHuBrain->fCallbackAction->GetImpactVelocity().fZ / (kFullImpactVel - kMinImpactVel))); + fMaxBlend = 0.5f + (0.5f * (-fHuBrain->fWalkingStrategy->GetImpactVelocity().fZ / (kFullImpactVel - kMinImpactVel))); if (fMaxBlend > 1) fMaxBlend = 1; fDuration = 1.0f / fFadeIn; @@ -1267,13 +1264,13 @@ bool GroundImpact::PreCondition(double time, float elapsed) bool result = false; if (fDuration > 0.0f) fDuration = fDuration - elapsed; - else if (fHuBrain->fCallbackAction->IsOnGround() && fHuBrain->fCallbackAction->GetImpactTime() > kMinAirTime) + else if (fHuBrain->fWalkingStrategy->IsOnGround() && fHuBrain->fWalkingStrategy->GetImpactTime() > kMinAirTime) { - if (fHuBrain->fCallbackAction->GetImpactVelocity().fZ < -kMinImpactVel) + if (fHuBrain->fWalkingStrategy->GetImpactVelocity().fZ < -kMinImpactVel) { - if (fHuBrain->fCallbackAction->GetImpactVelocity().fY >= kRunningImpactThresh) + if (fHuBrain->fWalkingStrategy->GetImpactVelocity().fY >= kRunningImpactThresh) { - fMaxBlend = 0.5f + (0.5f * (-fHuBrain->fCallbackAction->GetImpactVelocity().fZ / (kFullImpactVel - kMinImpactVel))); + fMaxBlend = 0.5f + (0.5f * (-fHuBrain->fWalkingStrategy->GetImpactVelocity().fZ / (kFullImpactVel - kMinImpactVel))); if (fMaxBlend > 1) fMaxBlend = 1; fDuration = 1.0f / fFadeIn; @@ -1292,7 +1289,7 @@ void GroundImpact::IStop() bool Fall::PreCondition(double time, float elapsed) { - return !fHuBrain->fCallbackAction->IsOnGround() && fHuBrain->fCallbackAction->HitGroundInThisAge(); + return !fHuBrain->fWalkingStrategy->IsOnGround() && fHuBrain->fWalkingStrategy->HitGroundInThisAge(); } void Fall::Process(double time, float elapsed) @@ -1304,7 +1301,7 @@ void Fall::Process(double time, float elapsed) if (fAnim && fAnim->GetBlend() > 0.8) { float panicThresh = plAvBrainHuman::kAirTimePanicThreshold; - if (panicThresh > 0.0f && fHuBrain->fCallbackAction->GetAirTime() > panicThresh) + if (panicThresh > 0.0f && fHuBrain->fWalkingStrategy->GetAirTime() > panicThresh) { fHuBrain->IdleOnly(); // clear the fall state; we're going somewhere new fAvMod->PanicLink(); @@ -1321,7 +1318,7 @@ void Push::Process(double time, float elapsed) fAvMod->GetPositionAndRotationSim(&pos, &rot); hsPoint3 lookAt; - fHuBrain->fCallbackAction->GetPushingPhysical()->GetPositionSim(lookAt); + fHuBrain->fWalkingStrategy->GetPushingPhysical()->GetPositionSim(lookAt); hsVector3 up(0.f, 0.f, 1.f); float angle = atan2(lookAt.fY - pos.fY, lookAt.fX - pos.fX) + M_PI / 2; hsQuat targRot(angle, &up); @@ -1335,23 +1332,23 @@ void Push::Process(double time, float elapsed) globFwd = rot.Rotate(&globFwd); if (globFwd.fX < 0) - fHuBrain->fCallbackAction->SetTurnStrength(-turnSpeed); + fHuBrain->fWalkingStrategy->SetTurnStrength(-turnSpeed); else - fHuBrain->fCallbackAction->SetTurnStrength(turnSpeed); + fHuBrain->fWalkingStrategy->SetTurnStrength(turnSpeed); } //bool PushIdle::PreCondition(double time, float elapsed) //{ -// return (fHuBrain->fCallbackAction->GetPushingPhysical() && -// fHuBrain->fCallbackAction->IsOnGround() && +// return (fHuBrain->fWalkingStrategy->GetPushingPhysical() && +// fHuBrain->fWalkingStrategy->IsOnGround() && // !fAvMod->TurnLeftKeyDown() && !fAvMod->TurnRightKeyDown() // && fAvMod->GetTurnStrength() == 0); //} bool PushWalk::PreCondition(double time, float elapsed) { - return (fHuBrain->fCallbackAction->GetPushingPhysical() && fHuBrain->fCallbackAction->GetFacingPushingPhysical() && - fHuBrain->fCallbackAction->IsOnGround() && + return (fHuBrain->fWalkingStrategy->GetPushingPhysical() && fHuBrain->fWalkingStrategy->GetFacingPushingPhysical() && + fHuBrain->fWalkingStrategy->IsOnGround() && fAvMod->ForwardKeyDown()); } @@ -1366,7 +1363,7 @@ bool PushSimpleMultiStage(plArmatureMod *avatar, const char *enterAnim, const ch { plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(avatar->FindBrainByClass(plAvBrainHuman::Index())); const char *names[3] = {enterAnim, idleAnim, exitAnim}; - if (!huBrain || !huBrain->fCallbackAction->IsOnGround() || !huBrain->fCallbackAction->HitGroundInThisAge() || huBrain->IsRunningTask() || + if (!huBrain || !huBrain->fWalkingStrategy->IsOnGround() || !huBrain->fWalkingStrategy->HitGroundInThisAge() || huBrain->IsRunningTask() || !avatar->IsPhysicsEnabled() || avatar->FindMatchingGenericBrain(names, 3)) return false; @@ -1429,7 +1426,7 @@ bool AvatarEmote(plArmatureMod *avatar, const char *emoteName) if (swimBrain && swimBrain->IsSwimming()) return false; - if (huBrain && huBrain->fCallbackAction->IsOnGround() && huBrain->fCallbackAction->HitGroundInThisAge() && !huBrain->IsRunningTask() && + if (huBrain && huBrain->fWalkingStrategy->IsOnGround() && huBrain->fWalkingStrategy->HitGroundInThisAge() && !huBrain->IsRunningTask() && emote && !alreadyActive && avatar->IsPhysicsEnabled()) { plKey avKey = avatar->GetKey(); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h index 69a40717..158b89ab 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h @@ -58,7 +58,7 @@ class plAGAnimInstance; class plAvTask; class plAvTaskMsg; class plAvBrainHuman; -class plWalkingController; +class plWalkingStrategy; class plArmatureUpdateMsg; class plClimbMsg; class plControlEventMsg; @@ -164,7 +164,7 @@ public: static const float kControlledFlightThreshold; static const float kAirTimeThreshold; static const float kAirTimePanicThreshold; - plWalkingController* fCallbackAction; + plWalkingStrategy* fWalkingStrategy; protected: plAGAnim *FindCustomAnim(const char *baseName); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp index d07ce063..4bf9661c 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp @@ -44,7 +44,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvBrainHuman.h" #include "plAvBrain.h" -#include "plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" #include "plMessage/plRideAnimatedPhysMsg.h" @@ -52,20 +52,19 @@ void plAvBrainRideAnimatedPhysical::Activate(plArmatureModBase *avMod) { plArmatureBrain::Activate(avMod); IInitAnimations(); - if (!fCallbackAction) + if (!fWalkingStrategy) { plSceneObject* avObj = fArmature->GetTarget(0); plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); plPhysicalControllerCore* controller = avMod->GetController(); - fCallbackAction = new plRidingAnimatedPhysicalController(avObj, agMod->GetApplicator(kAGPinTransform), controller); - fCallbackAction->ActivateController(); + fWalkingStrategy = new plDynamicWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller); + controller->SetMovementStrategy(fWalkingStrategy); } } plAvBrainRideAnimatedPhysical::~plAvBrainRideAnimatedPhysical() { - delete fCallbackAction; - fCallbackAction=nil; - + delete fWalkingStrategy; + fWalkingStrategy = nil; } void plAvBrainRideAnimatedPhysical::Deactivate() diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h index c1c3f578..b5e923a1 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h @@ -41,8 +41,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #include "plAvBrainHuman.h" -class plRidingAnimatedPhysicalController; - class plAvBrainRideAnimatedPhysical : public plAvBrainHuman { public: diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp index b447d820..9046f298 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp @@ -46,10 +46,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com // ///////////////////////////////////////////////////////////////////////////////////////// -//#include -//#include -#include "plAntiGravAction.h" // descends from Havok class, so must be included first, like havok objects - // singular #include "plAvBrainSwim.h" @@ -69,7 +65,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsTimer.h" #include "plPhysical.h" #include "plPhysicalControllerCore.h" -#include "plAvCallbackAction.h" // other #include "plPhysical/plCollisionDetector.h" #include "plPipeline/plDebugText.h" @@ -180,18 +175,16 @@ public: static const float timeToMaxTurn = 0.5f; static const float incPerSec = maxTurnSpeed / timeToMaxTurn; -// hsAssert(0, "fixme physx"); - float oldSpeed = fabs(fSwimBrain->fCallbackAction->GetTurnStrength()); + float oldSpeed = fabs(fSwimBrain->fSwimStrategy->GetTurnStrength()); float thisInc = elapsed * incPerSec; float newSpeed = min(oldSpeed + thisInc, maxTurnSpeed); - fSwimBrain->fCallbackAction->SetTurnStrength(newSpeed * fAvMod->GetKeyTurnStrength() + fAvMod->GetAnalogTurnStrength()); + fSwimBrain->fSwimStrategy->SetTurnStrength(newSpeed * fAvMod->GetKeyTurnStrength() + fAvMod->GetAnalogTurnStrength()); // the turn is actually applied during PhysicsUpdate } virtual void IStop() { -// hsAssert(0, "fixme physx"); - if (fSwimBrain->fCallbackAction) - fSwimBrain->fCallbackAction->SetTurnStrength(0.0f); + if (fSwimBrain->fSwimStrategy) + fSwimBrain->fSwimStrategy->SetTurnStrength(0.0f); plSwimBehavior::IStop(); } }; @@ -237,7 +230,7 @@ public: const float plAvBrainSwim::kMinSwimDepth = 4.0f; plAvBrainSwim::plAvBrainSwim() : - fCallbackAction(nil), + fSwimStrategy(nil), fMode(kWalking), fSurfaceDistance(0.f) { @@ -250,12 +243,9 @@ plAvBrainSwim::plAvBrainSwim() : plAvBrainSwim::~plAvBrainSwim() { - if(fCallbackAction) - { - IDetachAction(); - delete fCallbackAction; - fCallbackAction=nil; - } + delete fSwimStrategy; + fSwimStrategy = nil; + fSurfaceProbeMsg->UnRef(); int i; @@ -273,8 +263,7 @@ bool plAvBrainSwim::Apply(double time, float elapsed) fMode = kWading; plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(fAvMod->GetNextBrain(this)); -// hsAssert(0, "fixme physx"); - if (huBrain && !huBrain->fCallbackAction->IsOnGround()) + if (huBrain && !huBrain->fWalkingStrategy->IsOnGround()) { // We're jumping in! Trigger splash effect (sound) plArmatureEffectMsg *msg = new plArmatureEffectMsg(fAvMod->GetArmatureEffects()->GetKey(), kTime); @@ -318,8 +307,7 @@ bool plAvBrainSwim::Apply(double time, float elapsed) // The contact check is so that if buoyancy bobs us a little too high, we don't // switch to wading only to fall again. -// hsAssert(0, "fixme physx"); - if (fSurfaceDistance < kMinSwimDepth-.5 && fCallbackAction->HadContacts()) + if (fSurfaceDistance < kMinSwimDepth-.5 && fSwimStrategy->HadContacts()) IStartWading(); } else if (fMode == kSwimming3D) @@ -346,13 +334,12 @@ bool plAvBrainSwim::MsgReceive(plMessage *msg) else fSurfaceDistance = -100.f; -// hsAssert(0, "fixme physx"); - if (fCallbackAction) + if (fSwimStrategy) { if (region) - fCallbackAction->SetSurface(region, fArmature->GetTarget(0)->GetLocalToWorld().GetTranslate().fZ + fSurfaceDistance); + fSwimStrategy->SetSurface(region, fArmature->GetTarget(0)->GetLocalToWorld().GetTranslate().fZ + fSurfaceDistance); else - fCallbackAction->SetSurface(nil, 0.f); + fSwimStrategy->SetSurface(nil, 0.f); } return true; } @@ -419,20 +406,16 @@ void plAvBrainSwim::Activate(plArmatureModBase* avMod) void plAvBrainSwim::Deactivate() { plArmatureBrain::Deactivate(); - - IDetachAction(); } void plAvBrainSwim::Suspend() { - if (fMode == kSwimming2D) - IDetachAction(); } void plAvBrainSwim::Resume() { if (fMode == kSwimming2D) - IAttachAction(); + fSwimStrategy->Reset(false); } bool plAvBrainSwim::IsWalking() @@ -460,8 +443,6 @@ void plAvBrainSwim::IStartWading() for (i = 0; i < fBehaviors.GetCount(); i++) fBehaviors[i]->SetStrength(0.f, 2.f); - IDetachAction(); - if (fAvMod->IsLocalAvatar()) { plCameraMsg* pMsg = new plCameraMsg; @@ -479,7 +460,16 @@ void plAvBrainSwim::IStartSwimming(bool is2D) plArmatureBrain *nextBrain = fAvMod->GetNextBrain(this); nextBrain->Suspend(); - IAttachAction(); + if (!fSwimStrategy) + { + plSceneObject * avObj = fArmature->GetTarget(0); + plAGModifier *agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); + plPhysicalControllerCore *controller = fAvMod->GetController(); + fSwimStrategy = new plSwimStrategy(agMod->GetApplicator(kAGPinTransform), controller); + } + + fSwimStrategy->Reset(false); + if (is2D) fMode = kSwimming2D; else @@ -509,8 +499,8 @@ bool plAvBrainSwim::IProcessSwimming2D(double time, float elapsed) else behavior->SetStrength(0.f, 2.f); } -// hsAssert(0, "fixme physx"); - fCallbackAction->RecalcVelocity(time, time - elapsed); + + fSwimStrategy->RecalcVelocity(time, elapsed); return true; } @@ -568,59 +558,6 @@ bool plAvBrainSwim::IInitAnimations() return true; } -bool plAvBrainSwim::IAttachAction() -{ - bool result = false; - if(fAvMod) - { -// hsAssert(0, "fixme physx"); - plPhysicalControllerCore *physical = fAvMod->GetController(); - - if (physical) - { - if (!fCallbackAction) - { - plSceneObject * avObj = fArmature->GetTarget(0); - plAGModifier *agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); - fCallbackAction = new plSwimmingController(avObj, agMod->GetApplicator(kAGPinTransform),physical); -// physical->AttachAction(fCallbackAction, true, false); - result = true; - } - else - { - fCallbackAction->ActivateController(); - } - - } - } - return result; -} - -bool plAvBrainSwim::IDetachAction() -{ - bool result = false; - - if (fCallbackAction) - { -// hsAssert(0, "fixme physx"); -// plPhysical *physical = fAvMod->GetPhysical(); -// -// if(physical) -// { -// physical->RemoveAction(fCallbackAction); -// result = true; // there was an action and we removed it -// } - - // TODO: We get a compiler warning about deleting a pointer to an - // undefined class. So, who knows what the code is actually doing. - // Seems bad. Just putting a note in here for whoever fixes the - // physx issue. - //delete fCallbackAction; - //fCallbackAction = nil; - } - return result; -} - void plAvBrainSwim::IProbeSurface() { hsPoint3 ourPos = fAvMod->GetTarget(0)->GetLocalToWorld().GetTranslate(); @@ -676,21 +613,15 @@ void plAvBrainSwim::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *str debugTxt.DrawString(x, y, strBuf); y += lineHeight; + float buoy = fSwimStrategy ? fSwimStrategy->GetBuoyancy() : 0.0f; + sprintf(strBuf, "Distance to surface: %f Buoyancy: %f", fSurfaceDistance, buoy); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; -// hsAssert(0, "fixme physx"); -// float buoy = fCallbackAction? fCallbackAction->GetBuoyancy() : 0.0f; -// sprintf(strBuf, "Distance to surface: %f Buoyancy: %f", fSurfaceDistance, buoy); -// debugTxt.DrawString(x, y, strBuf); -// y += lineHeight; -// -// hsVector3 linV; -// fAvMod->GetPhysical()->GetLinearVelocitySim(linV); -// hsVector3 angV; -// fAvMod->GetPhysical()->GetAngularVelocitySim(angV); -// float angle = angV.fZ > 0 ? angV.Magnitude() : -angV.Magnitude(); -// sprintf(strBuf, "Velocity: Linear (%5.2f, %5.2f, %5.2f), Angular %5.2f", linV.fX, linV.fY, linV.fZ, angle); -// debugTxt.DrawString(x, y, strBuf); -// y += lineHeight; + hsVector3 linV = fAvMod->GetController()->GetAchievedLinearVelocity(); + sprintf(strBuf, "Linear Velocity: (%5.2f, %5.2f, %5.2f)", linV.fX, linV.fY, linV.fZ); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; int i; for (i = 0; i < fBehaviors.GetCount(); i++) diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h index d3eec23d..6de11ee9 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h @@ -51,7 +51,7 @@ class plAntiGravAction; class plControlEventMsg; class plLOSRequestMsg; class plSwimRegionInterface; -class plSwimmingController; +class plSwimStrategy; class plAvBrainSwim : public plArmatureBrain { public: @@ -73,7 +73,7 @@ public: bool IsSwimming(); float GetSurfaceDistance() { return fSurfaceDistance; } - plSwimmingController *fCallbackAction; + plSwimStrategy *fSwimStrategy; static const float kMinSwimDepth; protected: @@ -86,8 +86,6 @@ protected: bool IProcessBehaviors(double time, float elapsed); virtual bool IInitAnimations(); - bool IAttachAction(); - bool IDetachAction(); void IProbeSurface(); bool IHandleControlMsg(plControlEventMsg* msg); float IGetTargetZ(); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp deleted file mode 100644 index 76e22f5f..00000000 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp +++ /dev/null @@ -1,579 +0,0 @@ -/*==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 "plAvCallbackAction.h" -#include "plMessage/plLOSHitMsg.h" - -#include "plArmatureMod.h" // for LOS enum type -#include "plMatrixChannel.h" -#include "hsTimer.h" -#include "plPhysicalControllerCore.h" - -// Generic geom utils. -static bool LinearVelocity(hsVector3 &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat); -static void AngularVelocity(float &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat); -static float AngleRad2d (float x1, float y1, float x3, float y3); -inline hsVector3 GetYAxis(hsMatrix44 &mat) -{ - return hsVector3(mat.fMap[1][0], mat.fMap[1][1], mat.fMap[1][2]); -} - -plAnimatedController::plAnimatedController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) - : fRootObject(rootObject) - , fRootApp(rootApp) - , fController(controller) - , fTurnStr(0.f) - , fAnimAngVel(0.f) - , fAnimPosVel(0.f, 0.f, 0.f) -{ -} - -void plAnimatedController::RecalcVelocity(double timeNow, double timePrev, bool useAnim /* = true */) -{ - if (useAnim) - { - // while you may think it would be correct to cache this, - // what we're actually asking is "what would the animation's - // position be at the previous time given its *current* - // parameters (particularly blends)" - hsMatrix44 prevMat = ((plMatrixChannel *)fRootApp->GetChannel())->Value(timePrev, true); - hsMatrix44 curMat = ((plMatrixChannel *)fRootApp->GetChannel())->Value(timeNow, true); - - // If we get a valid linear velocity (ie, we didn't wrap around in the anim), - // use it. Otherwise just reuse the previous frames velocity. - hsVector3 linearVel; - if (LinearVelocity(linearVel, (float)(timeNow - timePrev), prevMat, curMat)) - fAnimPosVel = linearVel; - - // Automatically sets fAnimAngVel - AngularVelocity(fAnimAngVel, (float)(timeNow - timePrev), prevMat, curMat); - } - else - { - fAnimPosVel.Set(0.f, 0.f, 0.f); - fAnimAngVel = 0.f; - } - - if (fController) - fController->SetVelocities(fAnimPosVel, fAnimAngVel + fTurnStr); -} - -/////////////////////////////////////////////////////////////////////////// - -const float plWalkingController::kControlledFlightThreshold = 1.f; // seconds - -plWalkingController::plWalkingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) - : plAnimatedController(rootObject, rootApp, controller) - , fHitGroundInThisAge(false) - , fWaitingForGround(false) - , fControlledFlightTime(0) - , fControlledFlight(0) - , fImpactTime(0.f) - , fImpactVelocity(0.f, 0.f, 0.f) - , fClearImpact(false) - , fGroundLastFrame(false) -{ - if (fController) - { - fWalkingStrategy= new plWalkingStrategy(fController); - fController->SetMovementSimulationInterface(fWalkingStrategy); - } - else - fWalkingStrategy = nil; -} - -void plWalkingController::RecalcVelocity(double timeNow, double timePrev, bool useAnim) -{ - if (!fHitGroundInThisAge && fController && fController->IsEnabled() && fWalkingStrategy->IsOnGround()) - fHitGroundInThisAge = true; // if we're not pinned and we're not in an age yet, we are now. - - if (fClearImpact) - { - fImpactTime = 0.f; - fImpactVelocity.Set(0.f, 0.f, 0.f); - } - - if (fController && !fWalkingStrategy->IsOnGround()) - { - // PhysX Hack: AchievedLinearVelocity is a Cyanic fix for superjumps. LinearVelocity is - // always (0,0,0) outside of the controller update proc - fImpactTime = fWalkingStrategy->GetAirTime(); - fImpactVelocity = fController->GetAchievedLinearVelocity(); - // convert orientation from subworld to avatar-local coordinates - fImpactVelocity = (hsVector3)fController->GetLocalRotation().Rotate(&fImpactVelocity); - fClearImpact = false; - } - else - fClearImpact = true; - - if (IsControlledFlight()) - { - if (fWalkingStrategy && fWalkingStrategy->IsOnGround()) - fControlledFlightTime = fWalkingStrategy->GetAirTime(); - if(fGroundLastFrame&&(fWalkingStrategy && !fWalkingStrategy->IsOnGround())) - { - //we have started to leave the ground tell the movement strategy in case it cares - fWalkingStrategy->StartJump(); - } - if (fControlledFlightTime > kControlledFlightThreshold) - EnableControlledFlight(false); - } - if (fWalkingStrategy) - fGroundLastFrame = fWalkingStrategy->IsOnGround(); - else - fGroundLastFrame=false; - plAnimatedController::RecalcVelocity(timeNow, timePrev, useAnim); -} - -void plWalkingController::Reset(bool newAge) -{ - - ActivateController(); - if (newAge) - { - if (fWalkingStrategy) - fWalkingStrategy->ResetAirTime(); - fHitGroundInThisAge = false; - } -} - void plWalkingController::ActivateController() -{ - if (fWalkingStrategy) - { - fWalkingStrategy->RefreshConnectionToControllerCore(); - } - else - { - fWalkingStrategy= new plWalkingStrategy(fController); - fWalkingStrategy->RefreshConnectionToControllerCore(); - - } -} - -bool plWalkingController::EnableControlledFlight(bool status) -{ - if (status) - { - if (fControlledFlight == 0) - fControlledFlightTime = 0.f; - - ++fControlledFlight; - fWaitingForGround = true; - } - else - fControlledFlight = max(--fControlledFlight, 0); - - return status; -} -plWalkingController::~plWalkingController() -{ - delete fWalkingStrategy; - if (fController) - fController->SetMovementSimulationInterface(nil); -} -#if 0 -void plWalkingController::Update() -{ -// double elapsed = time.asDouble() - getRefresh().asDouble(); -// setRefresh(time); -// -// bool isPhysical = !fPhysical->GetProperty(plSimulationInterface::kPinned); -// const Havok::Vector3 straightUp(0.0f, 0.0f, 1.0f); -// bool alreadyInAge = fHitGroundInThisAge; -// -// int numContacts = fPhysical->GetNumContacts(); -// bool ground = false; -// fPushingPhysical = nil; -// int i, j; - -/* for(i = 0; i < numContacts; i++) - { - plHKPhysical *contactPhys = fPhysical->GetContactPhysical(i); - if (!contactPhys) - continue; // Physical no longer exists. Skip it. - - const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); - float dotUp = straightUp.dot(contact->m_normal); - if (dotUp > .5) - ground = true; - else if (contactPhys->GetProperty(plSimulationInterface::kAvAnimPushable)) - { - hsPoint3 position; - hsQuat rotation; - fPhysical->GetPositionAndRotationSim(&position, &rotation); - - hsQuat inverseRotation = rotation.Inverse(); - hsVector3 normal(contact->m_normal.x, contact->m_normal.y, contact->m_normal.z); - fFacingPushingPhysical = (inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false); - - fPushingPhysical = contactPhys; - } - } - - // We need to check for the case where the avatar hasn't collided with "ground", but is colliding - // with a few other objects so that he's not actually falling (wedged in between some slopes). - // We do this by answering the following question (in 2d top-down space): "If you sort the contact - // normals by angle, is there a large enough gap between normals?" - // - // If you think in terms of geometry, this means a collection of surfaces are all pushing on you. - // If they're pushing from all sides, you have nowhere to go, and you won't fall. There needs to be - // a gap, so that you're pushed out and have somewhere to fall. This is the same as finding a gap - // larger than 180 degrees between sorted normals. - // - // The problem is that on top of that, the avatar needs enough force to shove him out that gap (he - // has to overcome friction). I deal with that by making the threshold (360 - (180 - 60) = 240). I've - // seen up to 220 reached in actual gameplay in a situation where we'd want this to take effect. - // This is the same running into 2 walls where the angle between them is 60. - const float threshold = hsDegreesToRadians(240); - if (!ground && numContacts >= 2) - { - // Can probably do a special case for exactly 2 contacts. Not sure if it's worth it... - - fCollisionAngles.SetCount(numContacts); - for (i = 0; i < numContacts; i++) - { - const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); - fCollisionAngles[i] = atan2(contact->m_normal.y, contact->m_normal.x); - } - - // numContacts is rarely larger than 6, so let's do a simple bubble sort. - for (i = 0; i < numContacts; i++) - { - for (j = i + 1; j < numContacts; j++) - { - if (fCollisionAngles[i] > fCollisionAngles[j]) - { - float tempAngle = fCollisionAngles[i]; - fCollisionAngles[i] = fCollisionAngles[j]; - fCollisionAngles[j] = tempAngle; - } - } - } - - // sorted, now we check. - for (i = 1; i < numContacts; i++) - { - if (fCollisionAngles[i] - fCollisionAngles[i - 1] >= threshold) - break; - } - - if (i == numContacts) - { - // We got to the end. Check the last with the first and make your decision. - if (!(fCollisionAngles[0] - fCollisionAngles[numContacts - 1] >= (threshold - 2 * M_PI))) - ground = true; - } - } -*/ - - bool ground = fController ? fController->GotGroundHit() : true; - bool isPhysical = true; - - if (!fHitGroundInThisAge && isPhysical) - fHitGroundInThisAge = true; // if we're not pinned and we're not in an age yet, we are now. - - if (IsControlledFlight()) - fControlledFlightTime += (float)elapsed; - if (fControlledFlightTime > kControlledFlightThreshold && numContacts > 0) - EnableControlledFlight(false); - - if (ground || !isPhysical) - { - if (!IsControlledFlight() && !IsOnGround()) - { - // The first ground contact in an age doesn't count. -// if (alreadyInAge) -// { -// hsVector3 vel; -// fPhysical->GetLinearVelocitySim(vel); -// fImpactVel = vel.fZ; -// fTimeInAirPeak = (float)(fTimeInAir + elapsed); -// } - - fWaitingForGround = false; - } - fTimeInAir = 0; - } - else if (elapsed < plSimulationMgr::GetInstance()->GetMaxDelta()) - { - // If the simultation skipped a huge chunk of time, we didn't process the - // collisions, which could trick us into thinking we've just gone a long - // time without hitting ground. So we only count the time if this wasn't - // the case. - fTimeInAir += (float)elapsed; - } - - - // Tweakage so that we still fall under the right conditions. - // If we're in controlled flight, or standing still with ground solidly under us (probe hit). We only use anim velocity. -// if (!IsControlledFlight() && !(ground && fProbeHitGround && fAnimPosVel.fX == 0 && fAnimPosVel.fY == 0)) -// { -// hsVector3 curV; -// fPhysical->GetLinearVelocitySim(curV); -// fAnimPosVel.fZ = curV.fZ; -// -// // Prevents us from going airborn from running up bumps/inclines. -// if (IsOnGround() && fAnimPosVel.fZ > 0.f) -// fAnimPosVel.fZ = 0.f; -// -// // Unless we're on the ground and moving, or standing still with a probe hit, we use the sim's other axes too. -// if (!(IsOnGround() && (fProbeHitGround || fAnimPosVel.fX != 0 || fAnimPosVel.fY != 0))) -// { -// fAnimPosVel.fX = curV.fX; -// fAnimPosVel.fY = curV.fY; -// } -// } -// -// fPhysical->SetLinearVelocitySim(fAnimPosVel); -// fPhysical->SetSpin(fAnimAngVel + fTurnStr, hsVector3(0.0f, 0.0f, 1.0f)); -} -#endif - - -#if 0 - -///////////////////////////////////////////////////////////////////////// - -plSimDefs::ActionType plHorizontalFreezeAction::GetType() -{ - return plSimDefs::kHorizontalFreeze; -} - -void plHorizontalFreezeAction::apply(Havok::Subspace &s, Havok::hkTime time) -{ - double elapsed = time.asDouble() - getRefresh().asDouble(); - setRefresh(time); - - int numContacts = fPhysical->GetNumContacts(); - bool ground = false; - const Havok::Vector3 straightUp(0.0f, 0.0f, 1.0f); - int i; - for(i = 0; i < numContacts; i++) - { - const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); - float dotUp = straightUp.dot(contact->m_normal); - if (dotUp > .5) - ground = true; - } - - hsVector3 vel; - fPhysical->GetLinearVelocitySim(vel); - vel.fX = 0.0; - vel.fY = 0.0; - if (ground) - vel.fZ = 0; - fPhysical->SetLinearVelocitySim(vel); - fPhysical->ClearContacts(); -} -#endif -plSwimmingController::plSwimmingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) -:plAnimatedController(rootObject,rootApp,controller) -{ - if (controller) - fSwimmingStrategy= new plSwimStrategy(controller); - else - fSwimmingStrategy = nil; -} -plSwimmingController::~plSwimmingController() -{ - delete fSwimmingStrategy; -} - -plRidingAnimatedPhysicalController::plRidingAnimatedPhysicalController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) -: plWalkingController(rootObject, rootApp, controller) -{ - if(controller) - fWalkingStrategy = new plRidingAnimatedPhysicalStrategy(controller); - else - fWalkingStrategy = nil; -} -plRidingAnimatedPhysicalController::~plRidingAnimatedPhysicalController() -{ - delete fWalkingStrategy; - fWalkingStrategy=nil; -} - - -////////////////////////////////////////////////////////////////////////// - - -/* -Purpose: - -ANGLE_RAD_2D returns the angle in radians swept out between two rays in 2D. - -Discussion: - -Except for the zero angle case, it should be true that - -ANGLE_RAD_2D(X1,Y1,X2,Y2,X3,Y3) -+ ANGLE_RAD_2D(X3,Y3,X2,Y2,X1,Y1) = 2 * PI - -Modified: - -19 April 1999 - -Author: - -John Burkardt - -Parameters: - -Input, float X1, Y1, X2, Y2, X3, Y3, define the rays -( X1-X2, Y1-Y2 ) and ( X3-X2, Y3-Y2 ) which in turn define the -angle, counterclockwise from ( X1-X2, Y1-Y2 ). - -Output, float ANGLE_RAD_2D, the angle swept out by the rays, measured -in radians. 0 <= ANGLE_DEG_2D < 2 PI. If either ray has zero length, -then ANGLE_RAD_2D is set to 0. -*/ - -static float AngleRad2d ( float x1, float y1, float x3, float y3 ) -{ - float value; - float x; - float y; - - x = ( x1 ) * ( x3 ) + ( y1 ) * ( y3 ); - y = ( x1 ) * ( y3 ) - ( y1 ) * ( x3 ); - - if ( x == 0.0 && y == 0.0 ) { - value = 0.0; - } - else - { - value = atan2 ( y, x ); - - if ( value < 0.0 ) - { - value = (float)(value + TWO_PI); - } - } - return value; -} - -static bool LinearVelocity(hsVector3 &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat) -{ - bool result = false; - - hsPoint3 startPos(0.0f, 0.0f, 0.0f); // default position (at start of anim) - hsPoint3 prevPos = prevMat.GetTranslate(); // position previous frame - hsPoint3 nowPos = curMat.GetTranslate(); // position current frame - - hsVector3 prev2Now = (hsVector3)(nowPos - prevPos); // frame-to-frame delta - - if (fabs(prev2Now.fX) < 0.0001f && fabs(prev2Now.fY) < 0.0001f && fabs(prev2Now.fZ) < 0.0001f) - { - outputV.Set(0.f, 0.f, 0.f); - result = true; - } - else - { - hsVector3 start2Now = (hsVector3)(nowPos - startPos); // start-to-frame delta - - float prev2NowMagSqr = prev2Now.MagnitudeSquared(); - float start2NowMagSqr = start2Now.MagnitudeSquared(); - - float dot = prev2Now.InnerProduct(start2Now); - - // HANDLING ANIMATION WRAPPING: - // the vector from the animation origin to the current frame should point in roughly - // the same direction as the vector from the previous animation position to the - // current animation position. - // - // If they don't agree (dot < 0,) then we probably mpst wrapped around. - // The right answer would be to compare the current frame to the start of - // the anim loop, but it's cheaper to cheat and return false, - // telling the caller to use the previous frame's velocity. - if (dot > 0.0f) - { - prev2Now /= elapsed; - - float xfabs = fabs(prev2Now.fX); - float yfabs = fabs(prev2Now.fY); - float zfabs = fabs(prev2Now.fZ); - static const float maxVel = 20.0f; - bool valid = xfabs < maxVel && yfabs < maxVel && zfabs < maxVel; - - if (valid) - { - outputV = prev2Now; - result = true; - } - } - } - - return result; -} - -static void AngularVelocity(float &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat) -{ - outputV = 0.f; - float appliedVelocity = 0.0f; - hsVector3 prevForward = GetYAxis(prevMat); - hsVector3 curForward = GetYAxis(curMat); - - float angleSincePrev = AngleRad2d(curForward.fX, curForward.fY, prevForward.fX, prevForward.fY); - bool sincePrevSign = angleSincePrev > 0.0f; - if (angleSincePrev > M_PI) - angleSincePrev = angleSincePrev - TWO_PI; - - const hsVector3 startForward = hsVector3(0, -1.0, 0); // the Y orientation of a "resting" armature.... - float angleSinceStart = AngleRad2d(curForward.fX, curForward.fY, startForward.fX, startForward.fY); - bool sinceStartSign = angleSinceStart > 0.0f; - if (angleSinceStart > M_PI) - angleSinceStart = angleSinceStart - TWO_PI; - - // HANDLING ANIMATION WRAPPING: - // under normal conditions, the angle from rest to the current frame will have the same - // sign as the angle from the previous frame to the current frame. - // if it does not, we have (most likely) wrapped the motivating animation from frame n back - // to frame zero, creating a large angle from the previous frame to the current one - if (sincePrevSign == sinceStartSign) - { - // signs are the same; didn't wrap; use the frame-to-frame angle difference - appliedVelocity = angleSincePrev / elapsed; // rotation / time - if (fabs(appliedVelocity) < 3) - { - outputV = appliedVelocity; - } - } -} diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h deleted file mode 100644 index 3462ca08..00000000 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h +++ /dev/null @@ -1,217 +0,0 @@ -/*==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==*/ -#ifndef PL_HK_CALLBACK_ACTION_H -#define PL_HK_CALLBACK_ACTION_H - -#include "hsGeometry3.h" -#include "hsMatrix44.h" -#include "hsTemplates.h" -#include "pnKeyedObject/plKey.h" -#include "plPhysical/plSimDefs.h" -#include "pnMessage/plMessage.h" -#include "plPhysicalControllerCore.h" -class plLOSHitMsg; -class plAGApplicator; -class plSceneObject; -class plPhysical; -class plAvatarController; -class plCoordinateInterface; -class plPhysicalControllerCore; -// Used by the other controllers to actually move the avatar. The actual -// implementation is in the physics system. -/*class plPhysicalController -{ -public: - // Implemented in the physics system. If you're linking this without that for - // some reason, just stub this function out. - // - // Pass in the key to the root sceneobject for the avatar - static plPhysicalController* Create(plKey ownerSO, float height, float width); - - virtual ~plPhysicalController() {} - - // A disabled avatar doesn't move or accumulate air time if he's off the ground. - virtual void Enable(bool enable) = 0; - virtual bool IsEnabled() const = 0; - - // Set the LOS DB this avatar will be in (only one) - virtual void SetLOSDB(plSimDefs::plLOSDB losDB) = 0; - - // Call this once per frame with the velocities of the avatar in avatar space. - virtual void SetVelocities(const hsVector3& linearVel, float angVel) = 0; - - // Gets the actual velocity we achieved in the last step (relative to our subworld) - virtual const hsVector3& GetLinearVelocity() const = 0; - virtual void ResetAchievedLinearVelocity() = 0; - - // Get and set the current subworld for the avatar. Use nil for the main world. - virtual plKey GetSubworld() const = 0; - virtual void SetSubworld(plKey world) = 0; - - // If IsOnGround returns false, GetAirTime will tell you how long the avatar - // has been airborne. Use ResetAirTime to reset the air time to zero, for - // cases like when the avatar spawns into a new age. - virtual bool IsOnGround() const = 0; - virtual bool IsOnFalseGround() const = 0; - virtual float GetAirTime() const = 0; - virtual void ResetAirTime() = 0; - - virtual plPhysical* GetPushingPhysical() const = 0; - virtual bool GetFacingPushingPhysical() const = 0; - - // A helper function to get the coordinate interface for the avatars current - // world. Handy if you need to convert points to and from that. This will - // return nil if the avatar is in the main world (ie, you don't need to do - // any translation). - virtual const plCoordinateInterface* GetSubworldCI() const = 0; - - // For the avatar SDL only - virtual void GetState(hsPoint3& pos, float& zRot) = 0; - virtual void SetState(const hsPoint3& pos, float zRot) = 0; - - // kinematic stuff .... should be just for when playing a behavior... - virtual void Kinematic(bool state) = 0; - virtual bool IsKinematic() = 0; - virtual void GetKinematicPosition(hsPoint3& pos) = 0; - - virtual const hsMatrix44& GetPrevSubworldW2L() = 0; - - //when seeking no longer want to interact with exclusion regions - virtual void SetSeek(bool seek)=0; - - -}; -*/ -class plAvatarController -{ -public: - virtual ~plAvatarController() {} -}; - -class plAnimatedController : public plAvatarController -{ -public: - plAnimatedController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); - - virtual void RecalcVelocity(double timeNow, double timePrev, bool useAnim = true); - void SetTurnStrength(float val) { fTurnStr = val; } - float GetTurnStrength() { return fTurnStr; } - virtual void ActivateController()=0; -protected: - plSceneObject* fRootObject; - plPhysicalControllerCore* fController; - plAGApplicator* fRootApp; - float fAnimAngVel; - hsVector3 fAnimPosVel; - float fTurnStr; // Explicit turning, separate from animations -}; - -class plWalkingController : public plAnimatedController -{ -public: - plWalkingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); - virtual ~plWalkingController(); - virtual void RecalcVelocity(double timeNow, double timePrev, bool useAnim = true); - - void Reset(bool newAge); - bool IsControlledFlight() const { return fControlledFlight != 0; } - bool IsOnGround() const { return fWalkingStrategy ? fWalkingStrategy->IsOnGround() : true; } - bool IsOnFalseGround() const { return fWalkingStrategy ? fWalkingStrategy->IsOnFalseGround() : true; } - bool HitGroundInThisAge() const { return fHitGroundInThisAge; } - bool EnableControlledFlight(bool status); - float GetAirTime() const { return fWalkingStrategy ? fWalkingStrategy->GetAirTime() : 0.f; } - void ResetAirTime() { if (fWalkingStrategy) fWalkingStrategy->ResetAirTime(); } - float GetForwardVelocity() const; - void ActivateController(); - // Check these after the avatar the avatar hits the ground for his total - // hangtime and impact velocity. - float GetImpactTime() const { return fImpactTime; } - const hsVector3& GetImpactVelocity() const { return fImpactVelocity; } - - plPhysical* GetPushingPhysical() const - { - if(fController)return fController->GetPushingPhysical(); - else return nil; - } - bool GetFacingPushingPhysical() const - { if(fController)return fController->GetFacingPushingPhysical(); - else return false; - } - - -protected: - bool fHitGroundInThisAge; - bool fWaitingForGround; // We've gone airborne. IsOnGround() returns false until we hit ground again. - float fControlledFlightTime; - int fControlledFlight; // Count of how many are currently forcing flight - plWalkingStrategy* fWalkingStrategy; - float fImpactTime; - hsVector3 fImpactVelocity; - bool fClearImpact; - bool fGroundLastFrame;//used for a test to pass the event of first getting air during a jump - static const float kControlledFlightThreshold; -}; -class plSwimmingController: public plAnimatedController -{ -public : - plSwimmingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); - virtual ~plSwimmingController(); - void SetSurface(plSwimRegionInterface *region, float surfaceHeight){ - fSwimmingStrategy->SetSurface(region,surfaceHeight); - } - float GetBuoyancy() { return fSwimmingStrategy->GetBuoyancy(); } - bool IsOnGround() { return fSwimmingStrategy->IsOnGround(); } - bool HadContacts() { return fSwimmingStrategy->HadContacts();} - void Enable(bool en){if (fController) fController->Enable(en);} - plPhysicalControllerCore* GetController(){return fController;} - virtual void ActivateController(){fSwimmingStrategy->RefreshConnectionToControllerCore();} -protected: - plSwimStrategy* fSwimmingStrategy; - -}; -class plRidingAnimatedPhysicalController: public plWalkingController -{ -public: - plRidingAnimatedPhysicalController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); - virtual ~plRidingAnimatedPhysicalController(); -}; -#endif // PL_HK_CALLBACK_ACTION_H diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp index f1ef12f5..a1fdf22d 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp @@ -39,7 +39,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plAvatar/plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" #include "HeadSpin.h" @@ -129,7 +129,7 @@ bool plAvLadderMod::IIsReadyToClimb() if (armMod) { plAvBrainHuman* brain = plAvBrainHuman::ConvertNoRef(armMod->GetCurrentBrain()); - if (brain && brain->IsMovingForward() && brain->fCallbackAction->IsOnGround()) + if (brain && brain->IsMovingForward() && brain->fWalkingStrategy->IsOnGround()) movingForward = true; } diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp index 1207a180..60f2322e 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp @@ -54,7 +54,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAGAnim.h" #include "plArmatureMod.h" #include "plAvatarMgr.h" -#include "plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" // other #include "plMessage/plAvatarMsg.h" @@ -463,10 +463,10 @@ bool plAvTaskSeek::IFinishPosition(hsPoint3 &newPosition, { // While warping, we might be hovering just above the ground. Don't want that to // trigger any falling behavior. - if(brain&&brain->fCallbackAction) + if(brain&&brain->fWalkingStrategy) { - brain->fCallbackAction->ResetAirTime(); + brain->fWalkingStrategy->ResetAirTime(); } // how far will we translate this frame? float thisDist = kFloatSpeed * elapsed; diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp index 5d202273..ab3f9070 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp @@ -47,7 +47,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvatar/plAvBrainClimb.h" #include "plAvatar/plAvBrainDrive.h" #include "plAvatar/plAnimStage.h" -#include "plAvatar/plAvCallbackAction.h" +#include "plAvatar/plPhysicalControllerCore.h" #include "pnSceneObject/plSceneObject.h" #include "pnMessage/plSDLModifierMsg.h" #include "plSDL/plSDL.h" diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp index b05d666c..a7f500c7 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp @@ -52,7 +52,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAGAnimInstance.h" #include "plAGModifier.h" #include "plMatrixChannel.h" -#include "plAvCallbackAction.h" +#include "plPhysicalControllerCore.h" #include "plAvatarMgr.h" // global @@ -671,7 +671,7 @@ bool plAvOneShotTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, dou if(fEnablePhysicsAtEnd) { #if 0//ndef PLASMA_EXTERNAL_RELEASE - if (!humanBrain || humanBrain->fCallbackAction->HitGroundInThisAge()) + if (!humanBrain || humanBrain->fWalkingStrategy->HitGroundInThisAge()) { // For some reason, calling CheckValidPosition at the beginning of // an age can cause detectors to incorrectly report collisions. So diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp index 9ccb20a6..6fa701fc 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp @@ -40,507 +40,631 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #include "plPhysicalControllerCore.h" -#include "plMessage/plLOSHitMsg.h" + +#include "plArmatureMod.h" +#include "plSwimRegion.h" +#include "plMatrixChannel.h" #include "pnSceneObject/plCoordinateInterface.h" #include "plPhysical.h" #include "pnMessage/plCorrectionMsg.h" -#include "plSwimRegion.h" -#include "plArmatureMod.h" // for LOS enum type -#include "plMatrixChannel.h" -#include "hsTimer.h" -#include "plPhysX/plSimulationMgr.h" -#include "plPhysX/plPXPhysical.h" -#include "pnMessage/plSetNetGroupIDMsg.h" -#define kSWIMRADIUS 1.1f -#define kSWIMHEIGHT 2.8f -#define kGENERICCONTROLLERRADIUS 1.1f -#define kGENERICCONTROLLERHEIGHT 2.8f - -//#define kSWIMMINGCONTACTSLOPELIMIT (cosf(hsDegreesToRadians(80.f))) -const float plMovementStrategy::kAirTimeThreshold = .1f; // seconds + +// Gravity constants +#define kGravity -32.174f +#define kTerminalVelocity kGravity + +static inline hsVector3 GetYAxis(hsMatrix44 &mat) { return hsVector3(mat.fMap[1][0], mat.fMap[1][1], mat.fMap[1][2]); } +static float AngleRad2d(float x1, float y1, float x3, float y3); + bool CompareMatrices(const hsMatrix44 &matA, const hsMatrix44 &matB, float tolerance); -bool operator<(const plControllerSweepRecord left, const plControllerSweepRecord right) + + +// plPhysicalControllerCore +plPhysicalControllerCore::plPhysicalControllerCore(plKey OwnerSceneObject, float height, float radius) + : fOwner(OwnerSceneObject), + fWorldKey(nil), + fHeight(height), + fRadius(radius), + fLOSDB(plSimDefs::kLOSDBNone), + fMovementStrategy(nil), + fSimLength(0.0f), + fLocalRotation(0.0f, 0.0f, 0.0f, 1.0f), + fLocalPosition(0.0f, 0.0f, 0.0f), + fLastLocalPosition(0.0f, 0.0f, 0.0f), + fLinearVelocity(0.0f, 0.0f, 0.0f), + fAchievedLinearVelocity(0.0f, 0.0f, 0.0f), + fPushingPhysical(nil), + fFacingPushingPhysical(false), + fSeeking(false), + fEnabled(false), + fEnableChanged(false) { - if(left.TimeHit < right.TimeHit) return true; - else return false; + fLastGlobalLoc.Reset(); + fPrevSubworldW2L.Reset(); } -plMovementStrategy::plMovementStrategy(plPhysicalControllerCore* core) + +const plCoordinateInterface* plPhysicalControllerCore::GetSubworldCI() { - this->fTimeInAir=0.0f; - fCore=core; - fOwner=core->GetOwner(); - this->fPreferedControllerHeight=kGENERICCONTROLLERHEIGHT; - this->fPreferedControllerWidth=kGENERICCONTROLLERRADIUS; + if (fWorldKey) + { + plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); + if (so) + return so->GetCoordinateInterface(); + } + + return nil; } -void plMovementStrategy::IApplyKinematic() + +void plPhysicalControllerCore::IncrementAngle(float deltaAngle) { - // first apply sceneobject update to the kinematic + hsVector3 axis; + float angle; + + fLocalRotation.NormalizeIfNeeded(); + fLocalRotation.GetAngleAxis(&angle, &axis); + if (axis.fZ < 0) + angle = (2.0f * float(M_PI)) - angle; // axis is backwards, so reverse the angle too + + angle += deltaAngle; + + // make sure we wrap around + if (angle < 0.0f) + angle = (2.0f * float(M_PI)) + angle; // angle is -, so this works like a subtract + if (angle >= (2.0f * float(M_PI))) + angle = angle - (2.0f * float(M_PI)); + + // set the new angle + axis.Set(0.0f, 0.0f, 1.0f); + fLocalRotation.SetAngleAxis(angle, axis); +} + +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()); - if (so) + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (!CompareMatrices(fLastGlobalLoc, l2w, 0.0001f)) + SetGlobalLoc(l2w); + + if (fEnabled) { - // 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)) + // Convert velocity from avatar to world space + if (!fLinearVelocity.IsEmpty()) { - fCore->SetKinematicLoc(l2w); - //fCore->SetGlobalLoc(l2w); + fLinearVelocity = l2w * fLinearVelocity; + + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + fLinearVelocity = subworldCI->GetWorldToLocal() * fLinearVelocity; } + + fMovementStrategy->Apply(delSecs); } } -plPhysicalControllerCore::~plPhysicalControllerCore() +void plPhysicalControllerCore::IUpdate(int numSubSteps, float alpha) { + if (fEnabled) + { + // Update local position and acheived velocity + fLastLocalPosition = fLocalPosition; + GetPositionSim(fLocalPosition); + hsVector3 displacement = (hsVector3)(fLocalPosition - fLastLocalPosition); + fAchievedLinearVelocity = displacement / fSimLength; + + displacement /= (float)numSubSteps; + fLastLocalPosition = fLocalPosition - displacement; + hsPoint3 interpLocalPos = fLastLocalPosition + (displacement * alpha); + + // Update global location + fLocalRotation.MakeMatrix(&fLastGlobalLoc); + fLastGlobalLoc.SetTranslate(&interpLocalPos); + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + const hsMatrix44& subL2W = subworldCI->GetLocalToWorld(); + fLastGlobalLoc = subL2W * fLastGlobalLoc; + fPrevSubworldW2L = subworldCI->GetWorldToLocal(); + } + + fMovementStrategy->Update(fSimLength); + ISendCorrectionMessages(true); + } + else + { + fAchievedLinearVelocity.Set(0.0f, 0.0f, 0.0f); + + // 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(); + } + } + + if (fEnableChanged) + IHandleEnableChanged(); } -void plPhysicalControllerCore::Apply(float delSecs) -{ - fSimLength=delSecs; - hsAssert(fMovementInterface, "plPhysicalControllerCore::Apply() missing a movement interface"); - if(fMovementInterface)fMovementInterface->Apply(delSecs); -} -void plPhysicalControllerCore::PostStep(float delSecs) -{ - hsAssert(fMovementInterface, "plPhysicalControllerCore::PostStep() missing a movement interface"); - if(fMovementInterface)fMovementInterface->PostStep(delSecs); -} -void plPhysicalControllerCore::Update(float delSecs) +void plPhysicalControllerCore::IUpdateNonPhysical(float alpha) { - hsAssert(fMovementInterface, "plPhysicalControllerCore::Update() missing a movement interface"); - if(fMovementInterface)fMovementInterface->Update(delSecs); - + // 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 plPhysicalControllerCore::SendCorrectionMessages() + +void plPhysicalControllerCore::ISendCorrectionMessages(bool dirtySynch) { - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - plCorrectionMsg* corrMsg = new plCorrectionMsg; + plCorrectionMsg* corrMsg = new plCorrectionMsg(); corrMsg->fLocalToWorld = fLastGlobalLoc; corrMsg->fLocalToWorld.GetInverse(&corrMsg->fWorldToLocal); - corrMsg->fDirtySynch = true; - // Send the new position to the plArmatureMod and the scene object - const plArmatureMod* armMod = plArmatureMod::ConvertNoRef(so->GetModifierByType(plArmatureMod::Index())); - if (armMod) - corrMsg->AddReceiver(armMod->GetKey()); + corrMsg->fDirtySynch = dirtySynch; corrMsg->AddReceiver(fOwner); corrMsg->Send(); } -plPhysicalControllerCore::plPhysicalControllerCore(plKey OwnerSceneObject, float height, float radius) -:fMovementInterface(nil) -,fOwner(OwnerSceneObject) -,fHeight(height) -,fRadius(radius) -,fWorldKey(nil) -,fLinearVelocity(0.f,0.f,0.f) -,fAngularVelocity(0.f) -,fAchievedLinearVelocity(0.0f,0.0f,0.0f) -,fLocalPosition(0.0f,0.0f,0.0f) -,fLocalRotation(0.0f,0.0f,0.0f,1.0f) -,fSeeking(false) -,fEnabled(true) -,fEnableChanged(false) -,fLOSDB(plSimDefs::kLOSDBNone) -,fDisplacementThisStep(0.f,0.f,0.f) -,fSimLength(0.f) -,fKinematic(false) -,fKinematicEnableNextUpdate(false) -,fNeedsResize(false) -,fPushingPhysical(nil) + + +// Movement Strategy +plMovementStrategy::plMovementStrategy(plPhysicalControllerCore* controller) + : fController(controller) { } -void plPhysicalControllerCore::UpdateSubstepNonPhysical() -{ - // When we're in non-phys or a behavior we can't go through the rest of the function - // so we need to get out early, but we need to update the current position if we're - // in a subworld. - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - const plCoordinateInterface* ci = GetSubworldCI(); - if (ci && so) - { - const hsMatrix44& soL2W = so->GetCoordinateInterface()->GetLocalToWorld(); - const hsMatrix44& ciL2W = ci->GetLocalToWorld(); - hsMatrix44 l2w =GetPrevSubworldW2L()* soL2W; - l2w = ciL2W * l2w; - hsMatrix44 w2l; - l2w.GetInverse(&w2l); - ((plCoordinateInterface*)so->GetCoordinateInterface())->SetTransform(l2w, w2l); - ((plCoordinateInterface*)so->GetCoordinateInterface())->FlushTransform(); - SetGlobalLoc(l2w); - } +void plMovementStrategy::Reset(bool newAge) { fController->SetMovementStrategy(this); } -} -void plPhysicalControllerCore::CheckAndHandleAnyStateChanges() +// Animated Movement Strategy +plAnimatedMovementStrategy::plAnimatedMovementStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller) + : plMovementStrategy(controller), + fRootApp(rootApp), + fAnimLinearVel(0.0f, 0.0f, 0.0f), + fAnimAngularVel(0.0f), + fTurnStr(0.0f) { - if (IsEnabledChanged())HandleEnableChanged(); - if (IsKinematicChanged())HandleKinematicChanged(); - if (IsKinematicEnableNextUpdate())HandleKinematicEnableNextUpdate(); -} -void plPhysicalControllerCore::MoveActorToSim() -{ - // Get the current position of the physical - hsPoint3 curLocalPos; - hsPoint3 lastLocalPos; - GetPositionSim(curLocalPos); - MoveKinematicToController(curLocalPos); - lastLocalPos=GetLocalPosition(); - fDisplacementThisStep= hsVector3(curLocalPos - lastLocalPos); - fLocalPosition = curLocalPos; - if(fSimLength>0.0f) - fAchievedLinearVelocity=fDisplacementThisStep/fSimLength; - else fAchievedLinearVelocity.Set(0.0f,0.0f,0.0f); -} -void plPhysicalControllerCore::IncrementAngle(float deltaAngle) -{ - float angle; - hsVector3 axis; - fLocalRotation.NormalizeIfNeeded(); - fLocalRotation.GetAngleAxis(&angle, &axis); - // adjust it (quaternions are weird...) - if (axis.fZ < 0) - angle = (2*M_PI) - angle; // axis is backwards, so reverse the angle too - angle += deltaAngle; - // make sure we wrap around - if (angle < 0) - angle = (2*M_PI) + angle; // angle is -, so this works like a subtract - if (angle >= (2*M_PI)) - angle = angle - (2*M_PI); - // and set the new angle - fLocalRotation.SetAngleAxis(angle, hsVector3(0,0,1)); } -void plPhysicalControllerCore::UpdateWorldRelativePos() +void plAnimatedMovementStrategy::RecalcVelocity(double timeNow, float elapsed, bool useAnim) { + if (useAnim) + { + // while you may think it would be correct to cache this, what we're actually asking is "what would the animation's + // position be at the previous time given its *current* parameters (particularly blends)" + hsMatrix44 prevMat = ((plMatrixChannel *)fRootApp->GetChannel())->Value(timeNow - elapsed, true); + hsMatrix44 curMat = ((plMatrixChannel *)fRootApp->GetChannel())->Value(timeNow, true); - // Apply rotation and translation - fLocalRotation.MakeMatrix(&fLastGlobalLoc); - fLastGlobalLoc.SetTranslate(&fLocalPosition); - // Localize to global coords if in a subworld - const plCoordinateInterface* ci = GetSubworldCI(); - if (ci) + IRecalcLinearVelocity(elapsed, prevMat, curMat); + IRecalcAngularVelocity(elapsed, prevMat, curMat); + } + else { - const hsMatrix44& l2w = ci->GetLocalToWorld(); - fLastGlobalLoc = l2w * fLastGlobalLoc; + fAnimLinearVel.Set(0.0f, 0.0f, 0.0f); + fAnimAngularVel = 0.0f; } + + // Update controller rotation + float zRot = fAnimAngularVel + fTurnStr; + if (hsABS(zRot) > 0.0001f) + fController->IncrementAngle(zRot * elapsed); + + // Update controller velocity + fController->SetLinearVelocity(fAnimLinearVel); } -plPhysical* plPhysicalControllerCore::GetPushingPhysical() + +void plAnimatedMovementStrategy::IRecalcLinearVelocity(float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat) { - return fPushingPhysical; + hsPoint3 startPos(0.0f, 0.0f, 0.0f); // default position (at start of anim) + hsPoint3 prevPos = prevMat.GetTranslate(); // position previous frame + hsPoint3 nowPos = curMat.GetTranslate(); // position current frame + + hsVector3 prev2Now = (hsVector3)(nowPos - prevPos); // frame-to-frame delta + + if (fabs(prev2Now.fX) < 0.0001f && fabs(prev2Now.fY) < 0.0001f && fabs(prev2Now.fZ) < 0.0001f) + { + fAnimLinearVel.Set(0.f, 0.f, 0.f); + } + else + { + hsVector3 start2Now = (hsVector3)(nowPos - startPos); // start-to-frame delta + + float prev2NowMagSqr = prev2Now.MagnitudeSquared(); + float start2NowMagSqr = start2Now.MagnitudeSquared(); + + float dot = prev2Now.InnerProduct(start2Now); + + // HANDLING ANIMATION WRAPPING: + // the vector from the animation origin to the current frame should point in roughly + // the same direction as the vector from the previous animation position to the + // current animation position. + // + // If they don't agree (dot < 0,) then we probably mpst wrapped around. + // The right answer would be to compare the current frame to the start of + // the anim loop, but it's cheaper to cheat and use the previous frame's velocity. + if (dot > 0.0f) + { + prev2Now /= elapsed; + + float xfabs = fabs(prev2Now.fX); + float yfabs = fabs(prev2Now.fY); + float zfabs = fabs(prev2Now.fZ); + static const float maxVel = 20.0f; + bool valid = xfabs < maxVel && yfabs < maxVel && zfabs < maxVel; + + if (valid) + { + fAnimLinearVel = prev2Now; + } + } + } } -const hsVector3& plPhysicalControllerCore::GetLinearVelocity() + +void plAnimatedMovementStrategy::IRecalcAngularVelocity(float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat) { - return fLinearVelocity; + fAnimAngularVel = 0.0f; + float appliedVelocity = 0.0f; + hsVector3 prevForward = GetYAxis(prevMat); + hsVector3 curForward = GetYAxis(curMat); + + float angleSincePrev = AngleRad2d(curForward.fX, curForward.fY, prevForward.fX, prevForward.fY); + bool sincePrevSign = angleSincePrev > 0.0f; + if (angleSincePrev > float(M_PI)) + angleSincePrev = angleSincePrev - TWO_PI; + + const hsVector3 startForward = hsVector3(0.0f, -1.0f, 0.0f); // the Y orientation of a "resting" armature.... + float angleSinceStart = AngleRad2d(curForward.fX, curForward.fY, startForward.fX, startForward.fY); + bool sinceStartSign = angleSinceStart > 0.0f; + if (angleSinceStart > float(M_PI)) + angleSinceStart = angleSinceStart - TWO_PI; + + // HANDLING ANIMATION WRAPPING: + // under normal conditions, the angle from rest to the current frame will have the same + // sign as the angle from the previous frame to the current frame. + // if it does not, we have (most likely) wrapped the motivating animation from frame n back + // to frame zero, creating a large angle from the previous frame to the current one + if (sincePrevSign == sinceStartSign) + { + // signs are the same; didn't wrap; use the frame-to-frame angle difference + appliedVelocity = angleSincePrev / elapsed; // rotation / time + if (fabs(appliedVelocity) < 3) + { + fAnimAngularVel = appliedVelocity; + } + } } -bool plPhysicalControllerCore::GetFacingPushingPhysical() + + +// Walking Strategy +plWalkingStrategy::plWalkingStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller) + : plAnimatedMovementStrategy(rootApp, controller), + fSlidingNormals(), + fImpactVelocity(0.0f, 0.0f, 0.0f), + fImpactTime(0.0f), + fTimeInAir(0.0f), + fControlledFlightTime(0.0f), + fControlledFlight(0), + fGroundHit(false), + fFalseGround(false), + fHeadHit(false), + fClearImpact(false), + fHitGroundInThisAge(false) { - return fFacingPushingPhysical; } -/////////////////////////// -//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<IsSeeking()) + hsVector3 velocity = fController->GetLinearVelocity(); + hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); + + // Add in gravity if the avatar's z velocity isn't being set explicitly + if (hsABS(velocity.fZ) < 0.001f) { - collideFlags|=(1<GetLinearVelocity(); - hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); - hsPoint3 positionBegin; - fCore->GetPositionSim(positionBegin); - bool recovered=false; - if (fCore->IsKinematic()) + + // If we're airborne and the velocity isn't set, use the velocity from + // the last frame so we maintain momentum. + if (!IsOnGround() && velocity.fX == 0.0f && velocity.fY == 0.0f) { - 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; + velocity.fX = achievedVelocity.fX; + velocity.fY = achievedVelocity.fY; } - if (!fCore->IsEnabled()) - return; - - bool gotGroundHit = fGroundHit; - fGroundHit = false; - - fCore->SetPushingPhysical(nil); - fCore->SetFacingPushingPhysical( false); - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) + if (!fGroundHit && fSlidingNormals.Count()) { - 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()) + // We're not on solid ground, so we should be sliding against whatever + // we're hitting (like a rock cliff). Each vector in fSlidingNormals is + // the surface normal of a collision that's too steep to be ground, so + // we project our current velocity onto that plane and slide along the + // wall. + // + // Also, sometimes PhysX reports a bunch of collisions from the wall, + // but nothing from underneath (when there should be). So if we're not + // touching ground, we offset the avatar in the direction of the + // surface normal(s). This doesn't fix the issue 100%, but it's a hell + // of a lot better than nothing, and suitable duct tape until a future + // PhysX revision fixes the issue. + // + // Yes, there's room for optimization here if we care. + hsVector3 offset(0.0f, 0.0f, 0.0f); + for (int i = 0; i < fSlidingNormals.GetCount(); i++) { - LinearVelocity = l2w * LinearVelocity; - const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); - if (subworldCI) - LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; - } + offset += fSlidingNormals[i]; + hsVector3 velNorm = velocity; - // 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; - } + if (velNorm.MagnitudeSquared() > 0.0f) + velNorm.Normalize(); - //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.fZSetLinearVelocity(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; + velocity = velocity.Magnitude() * proj; + } } - - if(colFlags&kTop) + if (offset.MagnitudeSquared() > 0.0f) { - 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<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(); - } + // 5 ft/sec is roughly the speed we walk backwards. + // The higher the value, the less likely you'll trip + // the bug, and this seems reasonable. + offset.Normalize(); + velocity += offset * 5.0f; } } + + if (velocity.fZ < kTerminalVelocity) + velocity.fZ = kTerminalVelocity; + + // Convert to a displacement vector + hsVector3 displacement = velocity * delSecs; + + // Reset vars and move the controller + fController->SetPushingPhysical(nil); + fController->SetFacingPushingPhysical(false); + fGroundHit = fFalseGround = fHeadHit = false; + fSlidingNormals.SetCount(0); + + unsigned int collideResults = 0; + unsigned int collideFlags = 1<IsSeeking()) + collideFlags |= (1<Move(displacement, collideFlags, collideResults); + + if ((!fGroundHit) && (collideResults & kBottom)) + fFalseGround = true; + + if (collideResults & kTop) + fHeadHit = true; } -void plWalkingStrategy::ICheckForFalseGround() +void plWalkingStrategy::Update(float delSecs) { - if (fGroundHit) - return; // Already collided with "real" ground. - - // We need to check for the case where the avatar hasn't collided with "ground", but is colliding - // with a few other objects so that he's not actually falling (wedged in between some slopes). - // We do this by answering the following question (in 2d top-down space): "If you sort the contact - // normals by angle, is there a large enough gap between normals?" - // - // If you think in terms of geometry, this means a collection of surfaces are all pushing on you. - // If they're pushing from all sides, you have nowhere to go, and you won't fall. There needs to be - // a gap, so that you're pushed out and have somewhere to fall. This is the same as finding a gap - // larger than 180 degrees between sorted normals. - // - // The problem is that on top of that, the avatar needs enough force to shove him out that gap (he - // has to overcome friction). I deal with that by making the threshold (360 - (180 - 60) = 240). I've - // seen up to 220 reached in actual gameplay in a situation where we'd want this to take effect. - // This is the same running into 2 walls where the angle between them is 60. - int i, j; - const float threshold = hsDegreesToRadians(240.f); - int numContacts = fContactNormals.GetCount() + fPrevSlidingNormals.GetCount(); - if (numContacts >= 2) + if (fGroundHit || fFalseGround) + fTimeInAir = 0.0f; + else { - // For extra fun... PhysX will actually report some collisions every other frame, as though - // we're bouncing back and forth between the two (or more) objects blocking us. So it's not - // enough to look at this frame's collisions, we have to check previous frames too. - hsTArray fCollisionAngles; - fCollisionAngles.SetCount(numContacts); - int angleIdx = 0; - for (i = 0; i < fContactNormals.GetCount(); i++, angleIdx++) - { - fCollisionAngles[angleIdx] = atan2(fContactNormals[i].fY, fContactNormals[i].fX); - } - for (i = 0; i < fPrevSlidingNormals.GetCount(); i++, angleIdx++) - { - fCollisionAngles[angleIdx] = atan2(fPrevSlidingNormals[i].fY, fPrevSlidingNormals[i].fX); - } - // numContacts is rarely larger than 6, so let's do a simple bubble sort. - for (i = 0; i < numContacts; i++) - { - for (j = i + 1; j < numContacts; j++) - { - if (fCollisionAngles[i] > fCollisionAngles[j]) - { - float tempAngle = fCollisionAngles[i]; - fCollisionAngles[i] = fCollisionAngles[j]; - fCollisionAngles[j] = tempAngle; - } - } - } - // sorted, now we check. - for (i = 1; i < numContacts; i++) - { - if (fCollisionAngles[i] - fCollisionAngles[i - 1] >= threshold) - break; - } - if (i == numContacts) + fTimeInAir += delSecs; + if (fHeadHit) { - // We got to the end. Check the last with the first and make your decision. - if (!(fCollisionAngles[0] - fCollisionAngles[numContacts - 1] >= (threshold - 2 * M_PI))) - fFalseGround = true; + // If we're airborne and hit our head, override achieved velocity to avoid being shoved sideways + hsVector3 velocity = fController->GetLinearVelocity(); + hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); + + achievedVelocity.fX = velocity.fX; + achievedVelocity.fY = velocity.fY; + if (achievedVelocity.fZ > 0.0f) + achievedVelocity.fZ = 0.0f; + + fController->OverrideAchievedLinearVelocity(achievedVelocity); } } + + hsVector3 zeroVelocity(0.f, 0.f, 0.f); + fController->SetLinearVelocity(zeroVelocity); + + if (!fHitGroundInThisAge && IsOnGround()) + fHitGroundInThisAge = true; + + if (fClearImpact) + { + fImpactTime = 0.0f; + fImpactVelocity.Set(0.0f, 0.0f, 0.0f); + } + + if (IsOnGround()) + fClearImpact = true; + else + { + fImpactTime = fTimeInAir; + fImpactVelocity = fController->GetAchievedLinearVelocity(); + // convert orientation from subworld to avatar-local coordinates + fImpactVelocity = (hsVector3)fController->GetLocalRotation().Rotate(&fImpactVelocity); + fClearImpact = false; + } } -void plWalkingStrategy::Update(float delSecs) + +void plWalkingStrategy::AddContactNormals(hsVector3& vec) { - //Update Should Only be Called from a PhysicalControllerCore - hsAssert(fCore,"Running Update: but have no Core"); - float AngularVelocity=fCore->GetAngularVelocity(); - hsVector3 LinearVelocity=fCore->GetLinearVelocity(); + float dot = vec * kAvatarUp; + if (dot >= 0.5f) + fGroundHit = true; + else + fSlidingNormals.Append(vec); +} - if (!fCore->IsEnabled() || fCore->IsKinematic()) +void plWalkingStrategy::Reset(bool newAge) +{ + plMovementStrategy::Reset(newAge); + fImpactVelocity.Set(0.0f, 0.0f, 0.0f); + fImpactTime = 0.0f; + if (newAge) { - fCore->UpdateSubstepNonPhysical(); - return; + fTimeInAir = 0.0f; + fClearImpact = true; + fHitGroundInThisAge = false; + fSlidingNormals.SetCount(0); } - fCore->CheckAndHandleAnyStateChanges(); - if (!fGroundHit && !fFalseGround) - fTimeInAir += delSecs; - else - fTimeInAir = 0.f; - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) +} + +void plWalkingStrategy::RecalcVelocity(double timeNow, float elapsed, bool useAnim) +{ + if (fControlledFlight != 0) { - fCore->MoveActorToSim(); - if (AngularVelocity != 0.f) - { - float deltaAngle=AngularVelocity*delSecs; - fCore->IncrementAngle( deltaAngle); - } - // We can't only send updates when the physical position changes because the - // world relative position may be changing all the time if we're in a subworld. - fCore->UpdateWorldRelativePos(); - fCore->SendCorrectionMessages(); - bool headhit=fHitHead; - fHitHead=false; - hsVector3 AchievedLinearVelocity; - AchievedLinearVelocity = fCore->DisplacementLastStep(); - AchievedLinearVelocity=AchievedLinearVelocity/delSecs; - - /*if we hit our head the sweep api might try to - move us laterally to go as high as we requested kind of like autostep, to top it off the - way the NxCharacter and the sweep api work as a whole NxControllerHitReport::OnShapeHit - wont be called regarding the head blow. - if we are airborne: with out this we will gain large amounts of velocity in the x y plane - and on account of fAchievedLinearVelocity being used in the next step we will fly sideways - */ - if(headhit&&!(fGroundHit||fFalseGround)) - { - //we have hit our head and we don't have anything beneath our feet - //not really friction just a way to make it seem more realistic keep between 0 and 1 - float headFriction=0.0f; - AchievedLinearVelocity.fX=(1.0f-headFriction)*LinearVelocity.fX; - AchievedLinearVelocity.fY=(1.0f-headFriction)*LinearVelocity.fY; - //only clamping when hitting head and going upwards, if going down leave it be - // this should only occur when going down stairwells with low ceilings like in cleft - //kitchen area - if(AchievedLinearVelocity.fZ>0.0f) - { - AchievedLinearVelocity.fZ=0.0f; - } - fCore->OverrideAchievedVelocity(AchievedLinearVelocity); - } - fCore->OverrideAchievedVelocity(AchievedLinearVelocity); - // Apply angular velocity + if (IsOnGround()) + fControlledFlightTime = fTimeInAir; + + if (fControlledFlightTime > kControlledFlightThreshold) + EnableControlledFlight(false); } - LinearVelocity.Set(0.f, 0.f, 0.f); - AngularVelocity = 0.f; - fCore->SetVelocities(LinearVelocity,AngularVelocity); + plAnimatedMovementStrategy::RecalcVelocity(timeNow, elapsed, useAnim); +} +bool plWalkingStrategy::EnableControlledFlight(bool status) +{ + if (status) + { + if (fControlledFlight == 0) + fControlledFlightTime = 0.0f; + + ++fControlledFlight; + } + else + fControlledFlight = max(--fControlledFlight, 0); + + return status; +} + +plPhysical* plWalkingStrategy::GetPushingPhysical() const { return fController->GetPushingPhysical(); } +bool plWalkingStrategy::GetFacingPushingPhysical() const { return fController->GetFacingPushingPhysical(); } + +const float plWalkingStrategy::kAirTimeThreshold = 0.1f; +const float plWalkingStrategy::kControlledFlightThreshold = 1.0f; + + +// Swim Strategy +plSwimStrategy::plSwimStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller) + : plAnimatedMovementStrategy(rootApp, controller), + fBuoyancy(0.0f), + fSurfaceHeight(0.0f), + fCurrentRegion(nil), + fOnGround(false), + fHadContacts(false) +{ } +void plSwimStrategy::Apply(float delSecs) +{ + hsVector3 velocity = fController->GetLinearVelocity(); + hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); + + IAdjustBuoyancy(); + + //trying to dampen the oscillations + float retardent = 0.0f; + static float finalBobSpeed = 0.5f; + if ((achievedVelocity.fZ > finalBobSpeed) || (achievedVelocity.fZ < -finalBobSpeed)) + retardent = achievedVelocity.fZ * -0.90f; + + float zacc = (1.0f - fBuoyancy) * kGravity + retardent; + velocity.fZ += (zacc * delSecs); + + velocity.fZ += achievedVelocity.fZ; -void plWalkingStrategy::IAddContactNormals(hsVector3& vec) -{ - //TODO: ADD in functionality to Adjust walkable slope for controller, also apply that in here + // Water Current + if (fCurrentRegion != nil) + { + float angCurrent = 0.0f; + hsVector3 linCurrent(0.0f, 0.0f, 0.0f); + fCurrentRegion->GetCurrent(fController, linCurrent, angCurrent, delSecs); + + if (hsABS(angCurrent) > 0.0001f) + fController->IncrementAngle(angCurrent * delSecs); + + velocity += linCurrent; + + if (velocity.fZ > fCurrentRegion->fMaxUpwardVel) + velocity.fZ = fCurrentRegion->fMaxUpwardVel; + } + + if (velocity.fZ < kTerminalVelocity) + velocity.fZ = kTerminalVelocity; + + // Convert to displacement vector + hsVector3 displacement = velocity * delSecs; + + // Reset vars and move controller // + fController->SetPushingPhysical(nil); + fController->SetFacingPushingPhysical(false); + fHadContacts = fOnGround = false; + + unsigned int collideResults = 0; + unsigned int collideFlags = 1<IsSeeking()) + collideFlags |= (1<Move(displacement, collideFlags, collideResults); + + if ((collideResults & kBottom) || (collideResults & kSides)) + fHadContacts = true; +} + +void plSwimStrategy::AddContactNormals(hsVector3& vec) +{ float dot = vec * kAvatarUp; - if ( dot >= kSLOPELIMIT ) fGroundHit=true; - else plMovementStrategySimulationInterface::IAddContactNormals(vec); + if (dot >= kSlopeLimit) + fOnGround = true; } -//swimming strategy -plSwimStrategy::plSwimStrategy(plPhysicalControllerCore* core) - :plMovementStrategy(core) - ,fOnGround(false) - ,fHadContacts(false) - ,fBuoyancy(0.f) - ,fSurfaceHeight(0.0f) - ,fCurrentRegion(nil) +void plSwimStrategy::SetSurface(plSwimRegionInterface *region, float surfaceHeight) { - fPreferedControllerHeight=kSWIMHEIGHT; - fPreferedControllerWidth=kSWIMRADIUS; - fCore->SetMovementSimulationInterface(this); + fCurrentRegion = region; + fSurfaceHeight = surfaceHeight; } void plSwimStrategy::IAdjustBuoyancy() { @@ -558,383 +682,148 @@ void plSwimStrategy::IAdjustBuoyancy() return; } - hsMatrix44 l2w, w2l; hsPoint3 posSim; - fCore->GetPositionSim(posSim); - float depth = fSurfaceHeight - posSim.fZ; - //this isn't a smooth transition but hopefully it won't be too obvious - if(depth<=0.0)//all the away above water + fController->GetPositionSim(posSim); + float depth = fSurfaceHeight - posSim.fZ; + + // this isn't a smooth transition but hopefully it won't be too obvious + if (depth <= 0.0) //all the away above water fBuoyancy = 0.f; // Same as being above ground. Plain old gravity. - else if(depth >= 5.0f) fBuoyancy=3.0f;//completely Submereged - else fBuoyancy =(depth/surfaceDepth ); - + else if (depth >= 5.0f) + fBuoyancy = 3.0f; //completely Submereged + else + fBuoyancy = depth / surfaceDepth; } -void plSwimStrategy::Apply(float delSecs) -{ - hsAssert(fCore,"PlSwimStrategy::Apply No Core shouldn't be Applying"); - uint32_t collideFlags = - 1<IsSeeking()) - { - collideFlags|=(1<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.fZMove(displacement,collideFlags,colFlags); - if((colFlags&kBottom)||(colFlags&kSides))fHadContacts=true; - float angvel=fCore->GetAngularVelocity(); - fCore->SetAngularVelocity(angvel +angCurrent); - } + +// Dynamic Walking Strategy +plDynamicWalkingStrategy::plDynamicWalkingStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller) + : plWalkingStrategy(rootApp, controller) +{ } -void plSwimStrategy::Update(float delSecs) + +void plDynamicWalkingStrategy::Apply(float delSecs) { - hsAssert(fCore,"Running Update: but have no Core"); - float AngularVelocity=fCore->GetAngularVelocity(); - hsVector3 LinearVelocity=fCore->GetLinearVelocity(); - if (!fCore->IsEnabled() || fCore->IsKinematic()) - { - fCore->UpdateSubstepNonPhysical(); - return; - } - fCore->CheckAndHandleAnyStateChanges(); - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) - { - fCore->MoveActorToSim(); + hsVector3 velocity = fController->GetLinearVelocity(); + hsVector3 achievedVelocity = fController->GetAchievedLinearVelocity(); - if (AngularVelocity != 0.f) - { - float deltaAngle=AngularVelocity*delSecs; - fCore->IncrementAngle( deltaAngle); - } - fCore->UpdateWorldRelativePos(); - fCore->SendCorrectionMessages(); - } - LinearVelocity.Set(0.f, 0.f, 0.f); - AngularVelocity = 0.f; - fCore->SetVelocities(LinearVelocity,AngularVelocity); -} -void plSwimStrategy::IAddContactNormals(hsVector3& vec) -{ - //TODO: ADD in functionality to Adjust walkable slope for controller, also apply that in here - float dot = vec * kAvatarUp; - if ( dot >= kSLOPELIMIT ) + // Add in gravity if the avatar's z velocity isn't being set explicitly + if (hsABS(velocity.fZ) < 0.001f) { - fOnGround=true; - // fHadContacts=true; + // 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 = achievedVelocity.fZ; + if (IsOnGround()) + prevZVel = hsMinimum(prevZVel, 0.f); + + velocity.fZ = prevZVel + (kGravity * delSecs); } - else plMovementStrategySimulationInterface::IAddContactNormals(vec); -} -void plSwimStrategy::SetSurface(plSwimRegionInterface *region, float surfaceHeight) -{ - fCurrentRegion=region; - fSurfaceHeight=surfaceHeight; + + if (velocity.fZ < kTerminalVelocity) + velocity.fZ = kTerminalVelocity; + + fController->SetPushingPhysical(nil); + fController->SetFacingPushingPhysical(false); + fGroundHit = fFalseGround = false; + + float groundZVelocity; + if (ICheckForGround(groundZVelocity)) + velocity.fZ += groundZVelocity; + + fController->SetLinearVelocitySim(velocity); } -void plRidingAnimatedPhysicalStrategy::Apply(float delSecs) + +bool plDynamicWalkingStrategy::ICheckForGround(float& zVelocity) { - 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; + std::vector groundHits; + uint32_t collideFlags = 1<GetPositionSim(startPos); + hsPoint3 endPos = startPos; - - fCore->SetPushingPhysical(nil); - fCore->SetFacingPushingPhysical( false); - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - hsPoint3 startPos, desiredDestination, endPos; - fCore->GetPositionSim(startPos); - uint32_t collideFlags = - 1< 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) + // Set sweep length + startPos.fZ += 0.05f; + endPos.fZ -= 0.05f; + + int possiblePlatformCount = fController->SweepControllerPath(startPos, endPos, true, true, collideFlags, groundHits); + if (possiblePlatformCount) { - - std::multiset::iterator curRecord; + zVelocity = -FLT_MAX; - for(curRecord = GroundHitRecords.begin(); curRecord != GroundHitRecords.end(); curRecord++) + std::vector::iterator curRecord; + for (curRecord = groundHits.begin(); curRecord != groundHits.end(); ++curRecord) { - bool groundlike=false; - if((curRecord->locHit.fZ - startPos.fZ)<= .2) groundlike= true; - if(groundlike) + if (curRecord->ObjHit != nil) { - if(curRecord->ObjHit !=nil) - { - hsVector3 vel; - curRecord->ObjHit->GetLinearVelocitySim(vel); - if(vel.fZ > maxPlatformVel) - { - maxPlatformVel= vel.fZ; - } - } - platformCount ++; + hsVector3 objVelocity; + curRecord->ObjHit->GetLinearVelocitySim(objVelocity); + if (objVelocity.fZ > zVelocity) + zVelocity = objVelocity.fZ; + 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); - - } + return fGroundHit; } -bool plRidingAnimatedPhysicalStrategy::ICheckMove(const hsPoint3& startPos, const hsPoint3& desiredPos) + + +////////////////////////////////////////////////////////////////////////// + +/* +Purpose: + +ANGLE_RAD_2D returns the angle in radians swept out between two rays in 2D. + +Discussion: + +Except for the zero angle case, it should be true that + +ANGLE_RAD_2D(X1,Y1,X2,Y2,X3,Y3) ++ ANGLE_RAD_2D(X3,Y3,X2,Y2,X1,Y1) = 2 * PI + +Modified: + +19 April 1999 + +Author: + +John Burkardt + +Parameters: + +Input, float X1, Y1, X2, Y2, X3, Y3, define the rays +( X1-X2, Y1-Y2 ) and ( X3-X2, Y3-Y2 ) which in turn define the +angle, counterclockwise from ( X1-X2, Y1-Y2 ). + +Output, float ANGLE_RAD_2D, the angle swept out by the rays, measured +in radians. 0 <= ANGLE_DEG_2D < 2 PI. If either ray has zero length, +then ANGLE_RAD_2D is set to 0. +*/ + +static float AngleRad2d( float x1, float y1, float x3, float y3 ) { - //returns false if it believes the end result can't be obtained by pure application of velocity (collides into somthing that it can't climb up) - //used as a way to check if it needs to hack getting there like in jumping - - uint32_t collideFlags = - 1<IsSeeking()) - { - collideFlags|=(1< DynamicHits; - int NumberOfHits=fCore->SweepControllerPath(startPos, desiredPos, true, true, collideFlags, DynamicHits); - - hsPoint3 stepFromPoint; - hsVector3 movementdir(&startPos, &desiredPos); - movementdir.Normalize(); - if(NumberOfHits) - { - hsPoint3 initBottomPos; - fCore->GetPositionSim(initBottomPos); - std::multiset< plControllerSweepRecord >::iterator cur; - hsVector3 testLength(desiredPos - startPos); - bool freeMove=true; - for(cur = DynamicHits.begin(); cur != DynamicHits.end(); cur++) - { - if(movementdir.InnerProduct(cur->Norm)>0.01f) - { - hsVector3 topOfBottomHemAtTimeT=hsVector3(initBottomPos + testLength * cur->TimeHit ); - topOfBottomHemAtTimeT.fZ = topOfBottomHemAtTimeT.fZ + fCore->GetControllerWidth(); - if(cur->locHit.fZ <= (topOfBottomHemAtTimeT.fZ -.5f)) - { - hitBottomOfCapsule=true; - hsVector3 norm= hsVector3(-1*(cur->locHit-topOfBottomHemAtTimeT)); - norm.Normalize(); - IAddContactNormals(norm); - } - else - { - return false; - } - } + float value; + float x; + float y; - } - return true; + x = ( x1 ) * ( x3 ) + ( y1 ) * ( y3 ); + y = ( x1 ) * ( y3 ) - ( y1 ) * ( x3 ); + + if ( x == 0.0 && y == 0.0 ) { + value = 0.0; } else { - return true; - } - -} -void plRidingAnimatedPhysicalStrategy::Update(float delSecs) -{ - if (!fCore->IsEnabled() || fCore->IsKinematic()) - { - fCore->UpdateSubstepNonPhysical(); - return; - } - fCore->CheckAndHandleAnyStateChanges(); -} -void plRidingAnimatedPhysicalStrategy::PostStep(float delSecs) -{ - if(!(!fCore->IsEnabled() || fCore->IsKinematic())) - { - if (!fGroundHit && !fFalseGround) - fTimeInAir += delSecs; - else - fTimeInAir = 0.f; - hsVector3 AchievedLinearVelocity, LinearVelocity; - AchievedLinearVelocity = fCore->GetLinearVelocity(); - float AngularVelocity=fCore->GetAngularVelocity(); - fCore->OverrideAchievedVelocity(AchievedLinearVelocity); - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) + value = atan2 ( y, x ); + + if ( value < 0.0 ) { - fCore->UpdateControllerAndPhysicalRep(); - if (AngularVelocity != 0.f) - { - float deltaAngle=AngularVelocity*delSecs; - fCore->IncrementAngle( deltaAngle); - } - fCore->UpdateWorldRelativePos(); - fCore->SendCorrectionMessages(); + value = (float)(value + TWO_PI); } - LinearVelocity.Set(0.f, 0.f, 0.f); - AngularVelocity = 0.f; - fCore->SetVelocities(LinearVelocity, AngularVelocity); } - fStartJump = false; + return value; } + diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h index bb2c13cd..b0c3f9a7 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h @@ -41,310 +41,286 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #ifndef PLPHYSICALCONTROLLERCORE_H #define PLPHYSICALCONTROLLERCORE_H + #include "hsGeometry3.h" #include "hsMatrix44.h" +#include "hsQuat.h" #include "hsTemplates.h" #include "pnKeyedObject/plKey.h" #include "plPhysical/plSimDefs.h" -#include "pnMessage/plMessage.h" - -#include "hsQuat.h" -#define PHYSX_ONLY_TRIGGER_FROM_KINEMATIC 1 -#define kSLOPELIMIT (cosf(hsDegreesToRadians(45.f))) +#include class plCoordinateInterface; class plPhysical; -class plPXPhysical; +class plMovementStrategy; +class plAGApplicator; class plSwimRegionInterface; -//Replacement for for plPhysicalController stripped out some walk specific code -//plPhysicalControllerCore needs to have movement strategies registered to it these will then -//be called by the controller during the simulation steps. The Strategies need to at least have an -// Apply and Update definition. Everything else should be movement specific. I hope to come back and -//and refactor when I have time this in the future. -enum plControllerCollisionFlags -{ - kSides=1, - kTop= (1<<1), - kBottom=(1<<2), -}; -class plMovementStrategySimulationInterface -{ -public: +#define kSlopeLimit (cosf(hsDegreesToRadians(55.f))) - virtual void Apply(float delSecs)=0; - virtual void Update(float delSecs)=0; - //most strategies don't require this. Only the ones that require behavior like a physical or need - //something after the sim step. this used to be taken care of by Update, but this was moved to take care of - //some of the frame lag - virtual void PostStep(float delSecs){}; - virtual void IAddContactNormals(hsVector3& vec){fContactNormals.Append(vec);} - virtual void AddOnTopOfObject(plPhysical* phys){ fOnTopOf.Append(phys);} - virtual void LeaveAge() - { - fContactNormals.SetCount(0); - fOnTopOf.SetCount(0); - } -protected: - hsTArray fContactNormals; - hsTArray fOnTopOf; +enum plControllerCollisionFlags +{ + kSides = 1, + kTop = (1 << 1), + kBottom = (1 << 2) }; -class plControllerSweepRecord +struct plControllerSweepRecord { -public: plPhysical *ObjHit; - hsPoint3 locHit;//World space - float TimeHit;//Normalized between 0 and 1 - hsVector3 Norm; + hsPoint3 Point; + hsVector3 Normal; }; -bool operator<(const plControllerSweepRecord left, const plControllerSweepRecord right); + class plPhysicalControllerCore { public: - virtual ~plPhysicalControllerCore(); - virtual void Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults)=0; - virtual void SetMovementSimulationInterface(plMovementStrategySimulationInterface* strat){fMovementInterface=strat;} - virtual void Apply(float delSecs); - virtual void Update(float delSecs); - virtual void PostStep(float delSecs); - // A disabled avatar doesn't move or accumulate air time if he's off the ground. + plPhysicalControllerCore(plKey ownerSceneObject, float height, float radius); + virtual ~plPhysicalControllerCore() { } + + // An ArmatureMod has its own idea about when physics should be enabled/disabled. + // Use plArmatureModBase::EnablePhysics() instead. virtual void Enable(bool enable) = 0; - virtual bool IsEnabled() {return fEnabled;} - virtual plKey GetSubworld() {return fWorldKey;} + virtual bool IsEnabled() { return fEnabled; } + + // Subworld + virtual plKey GetSubworld() { return fWorldKey; } virtual void SetSubworld(plKey world) = 0; - virtual const plCoordinateInterface* GetSubworldCI() const = 0; + virtual const plCoordinateInterface* GetSubworldCI(); + // For the avatar SDL only virtual void GetState(hsPoint3& pos, float& zRot) = 0; virtual void SetState(const hsPoint3& pos, float zRot) = 0; - // kinematic stuff .... should be just for when playing a behavior... - virtual void Kinematic(bool state) = 0; - virtual bool IsKinematic() = 0; - virtual void GetKinematicPosition(hsPoint3& pos) = 0; - virtual const hsMatrix44& GetPrevSubworldW2L() = 0; - //when seeking no longer want to interact with exclusion regions - virtual void SetSeek(bool seek){fSeeking=seek;} - virtual bool IsSeeking(){return fSeeking;} - static plPhysicalControllerCore* Create(plKey ownerSO, float height, float radius); - virtual plMovementStrategySimulationInterface* GetMovementInterface(){return fMovementInterface;} - plPhysicalControllerCore(plKey ownerSceneObject, float height, float radius); - virtual plKey GetOwner(){return fOwner;}; - // Set the LOS DB this avatar will be in (only one) - virtual void SetLOSDB(plSimDefs::plLOSDB losDB) { fLOSDB = losDB; } ; - virtual plSimDefs::plLOSDB GetLOSDB() {return fLOSDB ; } - virtual const hsMatrix44& GetLastGlobalLoc()=0; - virtual void SetKinematicLoc(const hsMatrix44& l2w)=0; - virtual void SetGlobalLoc(const hsMatrix44& l2w)=0; - virtual bool IsEnabledChanged(){return fEnableChanged;} - virtual void HandleEnableChanged()=0; - virtual bool IsKinematicChanged(){return fKinematicChanged;} - virtual void GetPositionSim(hsPoint3& pos)=0; - virtual void HandleKinematicChanged()=0; - virtual bool IsKinematicEnableNextUpdate(){return fKinematicEnableNextUpdate;} - virtual void HandleKinematicEnableNextUpdate()=0; - virtual void MoveKinematicToController(hsPoint3& pos)=0; - virtual void UpdateControllerAndPhysicalRep()=0; - virtual void CheckAndHandleAnyStateChanges(); - virtual void UpdateSubstepNonPhysical(); - virtual const hsPoint3& GetLocalPosition()=0; - const hsQuat& GetLocalRotation() { return fLocalRotation; } - virtual void MoveActorToSim(); - - virtual void OverrideAchievedVelocity(hsVector3 newAchievedVel) - {//because of things like superjumps this is needed I'd rather not, but can't help it - fAchievedLinearVelocity=newAchievedVel; - } - //any clean up for the controller should go here - virtual void LeaveAge()=0; - hsVector3 DisplacementLastStep(){return fDisplacementThisStep;} - hsVector3 MeanVelocityForLastStep() - { - hsVector3 vel=fDisplacementThisStep; - return vel/fSimLength; - } - void SendCorrectionMessages(); + + // The LOS DB this avatar is in (only one) + virtual plSimDefs::plLOSDB GetLOSDB() { return fLOSDB; } + virtual void SetLOSDB(plSimDefs::plLOSDB losDB) { fLOSDB = losDB; } + + // Movement strategy + virtual void SetMovementStrategy(plMovementStrategy* strategy) = 0; + + // Global location + virtual const hsMatrix44& GetLastGlobalLoc() { return fLastGlobalLoc; } + virtual void SetGlobalLoc(const hsMatrix44& l2w) = 0; + + // Local sim position + virtual void GetPositionSim(hsPoint3& pos) = 0; + + // Move kinematic controller + virtual void Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults) = 0; + + // Set linear velocity on dynamic controller + virtual void SetLinearVelocitySim(const hsVector3& linearVel) = 0; + + // Sweep the controller path from startPos through endPos + virtual int SweepControllerPath(const hsPoint3& startPos,const hsPoint3& endPos, bool vsDynamics, bool vsStatics, + uint32_t& vsSimGroups, std::vector& hits) = 0; + + // any clean up for the controller should go here + virtual void LeaveAge() = 0; + + // Local rotation + const hsQuat& GetLocalRotation() const { return fLocalRotation; } void IncrementAngle(float deltaAngle); - void UpdateWorldRelativePos(); - virtual void SetLinearVelocity(const hsVector3& linearVel){fLinearVelocity=linearVel;} - //should actually be a 3 vector but everywhere else it is assumed to be just around Z - virtual void SetAngularVelocity(const float angvel){ fAngularVelocity=angvel;} - virtual void SetVelocities(const hsVector3& linearVel, float angVel) - { - fLinearVelocity=linearVel; - fAngularVelocity=angVel; - } - - virtual const hsVector3& GetLinearVelocity() ; - virtual float GetAngularVelocity(){return fAngularVelocity;} - virtual const hsVector3& GetAchievedLinearVelocity()const {return fAchievedLinearVelocity;} - plPhysical* GetPushingPhysical(); - bool GetFacingPushingPhysical(); - virtual void SetPushingPhysical(plPhysical* pl){fPushingPhysical=pl;} - virtual void SetFacingPushingPhysical(bool ans){fFacingPushingPhysical=ans;} - //To be Used during runtime conversions, say to switch a tall controller to a ball for swimming - virtual void SetControllerDimensions(float radius, float height)=0; - virtual float GetControllerWidth(){return fRadius;} - virtual float GetControllerHeight(){return fHeight;} - virtual void ResetAchievedLinearVelocity() - { - fAchievedLinearVelocity.Set(0.f,0.f,0.f); - } - virtual int SweepControllerPath(const hsPoint3& startPos,const hsPoint3& endPos, bool vsDynamics, bool vsStatics, uint32_t& vsSimGroups, std::multiset< plControllerSweepRecord >& WhatWasHitOut)=0; - //this should only be used to force a move it could place your head into a wall and that would be good - virtual float GetHeight() {return fHeight;} - virtual float GetRadius() {return fRadius;} - //Wether the avatar thing has mass and forces things down or not, and changes the way things move - //This is an attempt fix things like riding on an animated physical - virtual void BehaveLikeAnimatedPhysical(bool actLikeAnAnimatedPhys)=0; - virtual bool BehavingLikeAnAnimatedPhysical()=0; + + // Linear velocity + void SetLinearVelocity(const hsVector3& linearVel) { fLinearVelocity = linearVel; } + const hsVector3& GetLinearVelocity() const { return fLinearVelocity; } + + // Acheived linear velocity + const hsVector3& GetAchievedLinearVelocity() const { return fAchievedLinearVelocity; } + void OverrideAchievedLinearVelocity(const hsVector3& linearVel) { fAchievedLinearVelocity = linearVel; } + void ResetAchievedLinearVelocity() { fAchievedLinearVelocity.Set(0.f, 0.f, 0.f); } + + // SceneObject + plKey GetOwner() { return fOwner; } + + // When seeking no longer want to interact with exclude regions + void SetSeek(bool seek) { fSeeking = seek; } + bool IsSeeking() const { return fSeeking; } + + // Pushing physical + plPhysical* GetPushingPhysical() const { return fPushingPhysical; } + void SetPushingPhysical(plPhysical* phys) { fPushingPhysical = phys; } + bool GetFacingPushingPhysical() const { return fFacingPushingPhysical; } + void SetFacingPushingPhysical(bool facing) { fFacingPushingPhysical = facing; } + + // Controller dimensions + float GetRadius() const { return fRadius; } + float GetHeight() const { return fHeight; } + + // Create a new controller instance - Implemented in the physics system + static plPhysicalControllerCore* Create(plKey ownerSO, float height, float radius, bool human); + protected: - + virtual void IHandleEnableChanged() = 0; + + void IApply(float delSecs); + void IUpdate(int numSubSteps, float alpha); + void IUpdateNonPhysical(float alpha); + + void ISendCorrectionMessages(bool dirtySynch = false); + plKey fOwner; + plKey fWorldKey; + float fHeight; float fRadius; - plKey fWorldKey; + plSimDefs::plLOSDB fLOSDB; - bool fSeeking; - bool fEnabled; - bool fEnableChanged; - bool fKinematic; - bool fKinematicEnableNextUpdate; - bool fKinematicChanged; - plMovementStrategySimulationInterface* fMovementInterface; - hsMatrix44 fLastGlobalLoc; - hsPoint3 fLocalPosition; + + plMovementStrategy* fMovementStrategy; + + float fSimLength; + hsQuat fLocalRotation; + hsPoint3 fLocalPosition; + hsPoint3 fLastLocalPosition; + + hsMatrix44 fLastGlobalLoc; hsMatrix44 fPrevSubworldW2L; - hsVector3 fDisplacementThisStep; - float fSimLength; - - //physical properties + hsVector3 fLinearVelocity; - float fAngularVelocity; hsVector3 fAchievedLinearVelocity; + plPhysical* fPushingPhysical; bool fFacingPushingPhysical; - bool fNeedsResize; + + bool fSeeking; + bool fEnabled; + bool fEnableChanged; }; -class plMovementStrategy: public plMovementStrategySimulationInterface +class plMovementStrategy { public: - virtual void SetControllerCore(plPhysicalControllerCore* core) - { - fCore=core; - fCore->SetMovementSimulationInterface(this); - } - virtual void RefreshConnectionToControllerCore() - { - fCore->SetMovementSimulationInterface(this); - //fCore->SetControllerDimensions(fPreferedControllerWidth,fPreferedControllerHeight); - fCore->BehaveLikeAnimatedPhysical(this->IRequireBehaviourLikeAnAnimatedPhysical()); - } - plMovementStrategy(plPhysicalControllerCore* core); - //should actually be a 3 vector but everywhere else it is assumed to be just around Z - virtual void SetLinearAcceleration(const hsVector3& accel){fLinearAcceleration=accel;} - virtual const hsVector3& GetLinearAcceleration()const{return fLinearAcceleration;} - //should actually be a 3 vector but everywhere else it is assumed to be just around Z - virtual void ResetAchievedLinearVelocity() - { - hsVector3 AchievedLinearVelocity(0.f,0.f,0.f); - if(fCore)fCore->OverrideAchievedVelocity(AchievedLinearVelocity); - } -//proxy functions for Controller Core - virtual float GetAirTime() const { return fTimeInAir; } - virtual void ResetAirTime() { fTimeInAir = 0.f; } - + plMovementStrategy(plPhysicalControllerCore* controller); + virtual ~plMovementStrategy() { } + + virtual void Apply(float delSecs) = 0; + virtual void Update(float delSecs) { } + + virtual void AddContactNormals(hsVector3& vec) { } + virtual void Reset(bool newAge); + virtual bool IsKinematic() { return true; } + protected: - virtual bool IRequireBehaviourLikeAnAnimatedPhysical()=0; - virtual void IApplyKinematic(); - plPhysicalControllerCore* fCore; - hsVector3 fLinearAcceleration; - float fAngularAcceleration; - plKey fOwner; - static const float kAirTimeThreshold; - float fTimeInAir; - float fPreferedControllerWidth; - float fPreferedControllerHeight; - + plPhysicalControllerCore* fController; +}; +class plAnimatedMovementStrategy : public plMovementStrategy +{ +public: + plAnimatedMovementStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plAnimatedMovementStrategy() { } + + virtual void RecalcVelocity(double timeNow, float elapsed, bool useAnim = true); + void SetTurnStrength(float val) { fTurnStr = val; } + float GetTurnStrength() const { return fTurnStr; } + +private: + void IRecalcLinearVelocity(float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat); + void IRecalcAngularVelocity(float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat); + + plAGApplicator* fRootApp; + hsVector3 fAnimLinearVel; + float fAnimAngularVel; + float fTurnStr; }; -class plWalkingStrategy: public plMovementStrategy +class plWalkingStrategy : public plAnimatedMovementStrategy { public: - plWalkingStrategy(plPhysicalControllerCore* core):plMovementStrategy(core) - { - fGroundHit=false; - fFalseGround=false; - fHitHead=false; - fCore->SetMovementSimulationInterface(this); - fPreferedControllerWidth=core->GetControllerWidth(); - fPreferedControllerHeight=core->GetControllerHeight(); - fOnTopOfAnimatedPhysLastFrame=false; - } - virtual ~plWalkingStrategy(){}; + plWalkingStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plWalkingStrategy() { } + virtual void Apply(float delSecs); - virtual void Update(float delSecs); + virtual void Update(float delSecs); + + virtual void AddContactNormals(hsVector3& vec); + virtual void Reset(bool newAge); + virtual void RecalcVelocity(double timeNow, float elapsed, bool useAnim = true); + + bool HitGroundInThisAge() const { return fHitGroundInThisAge; } bool IsOnGround() const { return fTimeInAir < kAirTimeThreshold || fFalseGround; } - bool IsOnFalseGround() const { return fFalseGround && !fGroundHit; } - void GroundHit() { fGroundHit = true; } - virtual void IAddContactNormals(hsVector3& vec); - virtual void StartJump(){}; - + + float GetAirTime() const { return fTimeInAir; } + void ResetAirTime() { fTimeInAir = 0.0f; } + + float GetImpactTime() const { return fImpactTime; } + const hsVector3& GetImpactVelocity() const { return fImpactVelocity; } + + bool EnableControlledFlight(bool status); + bool IsControlledFlight() const { return fControlledFlight != 0; } + + plPhysical* GetPushingPhysical() const; + bool GetFacingPushingPhysical() const; protected: - - void ICheckForFalseGround(); + static const float kAirTimeThreshold; + static const float kControlledFlightThreshold; + + hsTArray fSlidingNormals; + + hsVector3 fImpactVelocity; + float fImpactTime; + + float fTimeInAir; + + float fControlledFlightTime; + int fControlledFlight; + bool fGroundHit; bool fFalseGround; - bool fHitHead; - bool fOnTopOfAnimatedPhysLastFrame; - hsTArray fPrevSlidingNormals; - virtual bool IRequireBehaviourLikeAnAnimatedPhysical(){return true;} + bool fHeadHit; + bool fSliding; + bool fClearImpact; + bool fHitGroundInThisAge; }; -class plSwimStrategy: public plMovementStrategy + +class plSwimStrategy : public plAnimatedMovementStrategy { public: - plSwimStrategy(plPhysicalControllerCore *core); - virtual ~plSwimStrategy(){}; - void SetSurface(plSwimRegionInterface *region, float surfaceHeight); + plSwimStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plSwimStrategy() { } + virtual void Apply(float delSecs); - virtual void Update(float delSecs); - float GetBuoyancy() { return fBuoyancy; } - bool IsOnGround() { return fOnGround; } - bool HadContacts() { return fHadContacts; } - virtual void IAddContactNormals(hsVector3& vec); + + virtual void AddContactNormals(hsVector3& vec); + + void SetSurface(plSwimRegionInterface* region, float surfaceHeight); + + float GetBuoyancy() const { return fBuoyancy; } + bool IsOnGround() const { return fOnGround; } + bool HadContacts() const { return fHadContacts; } + protected: - virtual bool IRequireBehaviourLikeAnAnimatedPhysical(){return true;} -private: void IAdjustBuoyancy(); + float fBuoyancy; - bool fOnGround; - bool fHadContacts; float fSurfaceHeight; + plSwimRegionInterface *fCurrentRegion; + + bool fOnGround; + bool fHadContacts; }; -class plRidingAnimatedPhysicalStrategy : public plWalkingStrategy + +class plDynamicWalkingStrategy : public plWalkingStrategy { public: - plRidingAnimatedPhysicalStrategy(plPhysicalControllerCore *core ) : - fNeedVelocityOverride(false),fStartJump(false),plWalkingStrategy(core){}; - virtual ~plRidingAnimatedPhysicalStrategy(){}; + plDynamicWalkingStrategy(plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plDynamicWalkingStrategy() { } + virtual void Apply(float delSecs); - virtual void Update(float delSecs); - virtual void PostStep(float delSecs); - bool IsOnGround() const { return fTimeInAir < kAirTimeThreshold || fFalseGround; } - bool IsOnFalseGround() const { return fFalseGround && !fGroundHit; } - void GroundHit() { fGroundHit = true; } - virtual void StartJump(){fStartJump = true;} + + virtual bool IsKinematic() { return false; } + protected: - virtual bool IRequireBehaviourLikeAnAnimatedPhysical(){return false;} - bool ICheckMove(const hsPoint3& startPos, const hsPoint3& desiredPos); - bool fNeedVelocityOverride; - hsVector3 fOverrideVelocity; - bool fStartJump; + bool ICheckForGround(float& zVelocity); }; + #endif// PLPHYSICALCONTROLLERCORE_H + diff --git a/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp b/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp index 798e6c62..586c8301 100644 --- a/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp +++ b/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp @@ -70,7 +70,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvatar/plArmatureMod.h" #include "plAvatar/plAvBrain.h" #include "plAvatar/plAvatarMgr.h" -#include "plAvatar/plAvCallbackAction.h" +#include "plAvatar/plPhysicalControllerCore.h" #include "plModifier/plInterfaceInfoModifier.h" #include "pnModifier/plLogicModBase.h" #include "plVault/plVault.h" diff --git a/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp b/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp index 9de08fbc..85cb8b2b 100644 --- a/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp +++ b/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp @@ -54,7 +54,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plMessage/plAvatarMsg.h" #include "plPhysical.h" #include "plPhysical/plSimDefs.h" -#include "plAvatar/plAvCallbackAction.h" #include "plAvatar/plAvBrainGeneric.h" diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp index 43ad60a0..27964d8f 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp @@ -94,8 +94,7 @@ private: } else { - bool isController; - plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(hitActor,&isController); + plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(hitActor); if (controller) { objKey = controller->GetOwner(); diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp index 32d6ba18..55258d08 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp @@ -138,14 +138,10 @@ plPXPhysical::plPXPhysical() , fSceneNode(nil) , fWorldKey(nil) , fSndGroup(nil) - , fWorldHull(nil) - , fSaveTriangles(nil) - , fHullNumberPlanes(0) , fMass(0.f) , fWeWereHit(false) , fHitForce(0,0,0) , fHitPos(0,0,0) - , fInsideConvexHull(false) { } @@ -190,11 +186,6 @@ plPXPhysical::~plPXPhysical() plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); } - if (fWorldHull) - delete [] fWorldHull; - if (fSaveTriangles) - delete [] fSaveTriangles; - delete fProxyGen; // remove sdl modifier @@ -206,155 +197,6 @@ plPXPhysical::~plPXPhysical() delete fSDLMod; } -static void MakeBoxFromHull(NxConvexMesh* convexMesh, NxBoxShapeDesc& box) -{ - NxConvexMeshDesc desc; - convexMesh->saveToDesc(desc); - - float minX, minY, minZ, maxX, maxY, maxZ; - minX = minY = minZ = FLT_MAX; - maxX = maxY = maxZ = -FLT_MAX; - - for (int i = 0; i < desc.numVertices; i++) - { - float* point = (float*)(((char*)desc.points) + desc.pointStrideBytes*i); - float x = point[0]; - float y = point[1]; - float z = point[2]; - - minX = hsMinimum(minX, x); - minY = hsMinimum(minY, y); - minZ = hsMinimum(minZ, z); - maxX = hsMaximum(maxX, x); - maxY = hsMaximum(maxY, y); - maxZ = hsMaximum(maxZ, z); - } - - float xWidth = maxX - minX; - float yWidth = maxY - minY; - float zWidth = maxZ - minZ; - box.dimensions.x = xWidth / 2; - box.dimensions.y = yWidth / 2; - box.dimensions.z = zWidth / 2; - - //hsMatrix44 mat; - //box.localPose.getRowMajor44(&mat.fMap[0][0]); - hsPoint3 trans(minX + (xWidth / 2), minY + (yWidth / 2), minZ + (zWidth / 2)); - //mat.SetTranslate(&trans); - //box.localPose.setRowMajor44(&mat.fMap[0][0]); - - hsMatrix44 boxL2W; - boxL2W.Reset(); - boxL2W.SetTranslate(&trans); - plPXConvert::Matrix(boxL2W, box.localPose); - -} - -void plPXPhysical::IMakeHull(NxConvexMesh* convexMesh, hsMatrix44 l2w) -{ - NxConvexMeshDesc desc; - convexMesh->saveToDesc(desc); - - // make sure there are some triangles to work with - if (desc.numTriangles == 0) - return; - - // get rid of any we may have already had - if (fSaveTriangles) - delete [] fSaveTriangles; - - fHullNumberPlanes = desc.numTriangles; - fSaveTriangles = new hsPoint3[fHullNumberPlanes*3]; - - for (int i = 0; i < desc.numTriangles; i++) - { - uint32_t* triangle = (uint32_t*)(((char*)desc.triangles) + desc.triangleStrideBytes*i); - float* vertex1 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[0]); - float* vertex2 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[1]); - float* vertex3 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[2]); - hsPoint3 pt1(vertex1[0],vertex1[1],vertex1[2]); - hsPoint3 pt2(vertex2[0],vertex2[1],vertex2[2]); - hsPoint3 pt3(vertex3[0],vertex3[1],vertex3[2]); - - fSaveTriangles[(i*3)+0] = pt1; - fSaveTriangles[(i*3)+1] = pt2; - fSaveTriangles[(i*3)+2] = pt3; - } -} - -void plPXPhysical::ISetHullToWorldWTriangles() -{ - // if we have a detector hull and the world hasn't been updated - if (fWorldHull == nil) - { - fWorldHull = new hsPlane3[fHullNumberPlanes]; - // use the local2world from the physics engine so that it matches the transform of the positions from the triggerees - hsMatrix44 l2w; - plPXConvert::Matrix(fActor->getGlobalPose(), l2w); - int i; - for( i = 0; i < fHullNumberPlanes; i++ ) - { - hsPoint3 pt1 = fSaveTriangles[i*3]; - hsPoint3 pt2 = fSaveTriangles[(i*3)+1]; - hsPoint3 pt3 = fSaveTriangles[(i*3)+2]; - - // local to world translation - pt1 = l2w * pt1; - pt2 = l2w * pt2; - pt3 = l2w * pt3; - - hsPlane3 plane(&pt1, &pt2, &pt3); - fWorldHull[i] = plane; - } - } -} - - -bool plPXPhysical::IsObjectInsideHull(const hsPoint3& pos) -{ - if (fSaveTriangles) - { - ISetHullToWorldWTriangles(); - int i; - for( i = 0; i < fHullNumberPlanes; i++ ) - { - if (!ITestPlane(pos, fWorldHull[i])) - return false; - } - return true; - } - return false; -} - -bool plPXPhysical::Should_I_Trigger(bool enter, hsPoint3& pos) -{ - // see if we are inside the detector hull, if so, then don't trigger - bool trigger = false; - bool inside = IsObjectInsideHull(pos); - if ( !inside) - { - trigger = true; - fInsideConvexHull = enter; - } - else - { - // catch those rare cases on slow machines that miss the collision before avatar penetrated the face - if (enter && !fInsideConvexHull) - { -#ifdef PHYSX_SAVE_TRIGGERS_WORKAROUND - trigger = true; - fInsideConvexHull = enter; - DetectorLogSpecial("**>Saved a missing enter collision: %s",GetObjectKey()->GetName().c_str()); -#else - DetectorLogSpecial("**>Could have saved a missing enter collision: %s",GetObjectKey()->GetName().c_str()); -#endif PHYSX_SAVE_TRIGGERS_WORKAROUND - } - } - - return trigger; -} - - bool plPXPhysical::Init(PhysRecipe& recipe) { bool startAsleep = false; @@ -388,29 +230,6 @@ bool plPXPhysical::Init(PhysRecipe& recipe) } break; case plSimDefs::kHullBounds: - // FIXME PHYSX - Remove when hull detection is fixed - // If this is read time (ie, meshStream is nil), turn the convex hull - // into a box. That way the data won't have to change when convex hulls - // actually work right. - if (fGroup == plSimDefs::kGroupDetector && recipe.meshStream == nil) - { -#ifdef USE_BOXES_FOR_DETECTOR_HULLS - MakeBoxFromHull(recipe.convexMesh, boxDesc); - plSimulationMgr::GetInstance()->GetSDK()->releaseConvexMesh(*recipe.convexMesh); - boxDesc.group = fGroup; - actorDesc.shapes.push_back(&boxDesc); -#else -#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND - // make a hull of planes for testing IsInside - IMakeHull(recipe.convexMesh,recipe.l2s); -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND - convexShapeDesc.meshData = recipe.convexMesh; - convexShapeDesc.userData = recipe.meshStream; - convexShapeDesc.group = fGroup; - actorDesc.shapes.pushBack(&convexShapeDesc); -#endif // USE_BOXES_FOR_DETECTOR_HULLS - } - else { convexShapeDesc.meshData = recipe.convexMesh; convexShapeDesc.userData = recipe.meshStream; @@ -669,9 +488,6 @@ void plPXPhysical::IEnable(bool enable) { fActor->clearActorFlag(NX_AF_DISABLE_COLLISION); - // PHYSX FIXME - after re-enabling a possible detector, we need to check to see if any avatar is already in the PhysX turdy hull detector region - plSimulationMgr::GetInstance()->UpdateAvatarInDetector(fWorldKey, this); - if (fActor->isDynamic()) fActor->clearBodyFlag(NX_BF_FROZEN); else diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h index 693fbc97..07f407e4 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h @@ -156,11 +156,6 @@ public: virtual uint16_t GetAllLOSDBs() { return fLOSDBs; } virtual bool IsInLOSDB(uint16_t flag) { return hsCheckBits(fLOSDBs, flag); } - virtual bool DoDetectorHullWorkaround() { return fSaveTriangles ? true : false; } - virtual bool Should_I_Trigger(bool enter, hsPoint3& pos); - virtual bool IsObjectInsideHull(const hsPoint3& pos); - virtual void SetInsideConvexHull(bool inside) { fInsideConvexHull = inside; } - virtual plKey GetWorldKey() const { return fWorldKey; } virtual plPhysicalSndGroup* GetSoundGroup() const { return fSndGroup; } @@ -201,14 +196,6 @@ protected: /** Handle messages about our references. */ bool HandleRefMsg(plGenRefMsg * refM); - ///////////////////////////////////////////////////////////// - // - // WORLDS, SUBWORLDS && CONTEXTS - // - ///////////////////////////////////////////////////////////// - - void IConvertGroups(uint32_t memberOf, uint32_t reportsOn, uint32_t collideWith); - /** See if the object is in a valid, non-overlapping position. A valid overlap is one which is approved by the collision masking code, i.e. my memberOf has no intersection with your @@ -236,8 +223,6 @@ protected: // Enable/disable collisions and dynamic movement void IEnable(bool enable); - void IMakeHull(NxConvexMesh* convexMesh, hsMatrix44 l2w); - NxActor* fActor; plKey fWorldKey; // either a subworld or nil @@ -251,24 +236,6 @@ protected: plKey fObjectKey; // the key to our scene object plKey fSceneNode; // the room we're in - // PHYSX FIXME - need to create a plasma hull so that we can determine if inside - hsPlane3* fWorldHull; - uint32_t fHullNumberPlanes; - hsPoint3* fSaveTriangles; - bool fInsideConvexHull; - void ISetHullToWorldWTriangles(); - inline bool ITestPlane(const hsPoint3 &pos, const hsPlane3 &plane) - { - float dis = plane.fN.InnerProduct(pos); - dis += plane.fD; - if (dis == 0.f) - return false; - if( dis >= 0.f ) - return false; - - return true; - } - // we need to remember the last matrices we sent to the coordinate interface // so that we can recognize them when we send them back and not reapply them, // which would reactivate our body. inelegant but effective diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp deleted file mode 100644 index 7e8e4667..00000000 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp +++ /dev/null @@ -1,1280 +0,0 @@ -/*==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 "plPXPhysicalController.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 "plSurface/hsGMaterial.h" -#include "plSurface/plLayerInterface.h"" -#include "plMessage/plCollideMsg.h" - - -#include -#include -#include -#include - -#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 -#include "plPipeline/plDebugText.h" -bool plPXPhysicalController::fDebugDisplay = false; -#endif // PLASMA_EXTERNAL_RELEASE - -static ControllerManager gControllerMgr; -static std::vector gControllers; -static bool gRebuildCache = false; - -// KLUDGE: From plPXPhysical.cpp -bool CompareMatrices(const hsMatrix44 &matA, const hsMatrix44 &matB, float tolerance); - -plPhysicalController* plPhysicalController::Create(plKey ownerSO, hsScalar height, hsScalar width) -{ - hsScalar radius = width / 2.f; - //hsScalar realHeight = height - width; - hsScalar realHeight = height - radius + kPhysicalHeightFudge; - return new plPXPhysicalController(ownerSO, radius, realHeight); -} - -////////////////////////////////////////////////////////////////////////// - -plPXPhysicalController* plPXPhysicalController::FindController(NxController* controller) -{ - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalController* ac = gControllers[i]; - if (ac->fController == controller) - return ac; - } - return nil; -} - -plPXPhysicalController* plPXPhysicalController::GetController(NxActor& actor, bool* isController) -{ - *isController = false; - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalController* ac = gControllers[i]; - if (ac->fController && ac->fController->getActor() == &actor) - { - *isController = true; - return ac; - } - if ( ac->fKinematicActor == &actor) - { - return ac; - } - } - - return nil; -} -void plPXPhysicalController::GetWorldSpaceCapsule(NxCapsule& cap) -{ - if(fController){ - int numshapes=fController->getActor()->getNbShapes(); - if (numshapes==1) - {//there should only be one shape on a controller - NxShape* const *shapes=fController->getActor()->getShapes(); - //and since it is a capsule controller it better be a capsule; - NxCapsuleShape *capShape = shapes[0]->isCapsule(); - if(capShape) capShape->getWorldCapsule(cap); - - } - } - -} -bool plPXPhysicalController::AnyControllersInThisWorld(plKey world) -{ - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalController* ac = gControllers[i]; - if (ac->GetSubworld() == world) - return true; - } - return false; -} - -int plPXPhysicalController::NumControllers() -{ - return gControllers.size(); -} -int plPXPhysicalController::GetControllersInThisSubWorld(plKey world, int maxToReturn,plPXPhysicalController** bufferout) -{ - int i=0; - for (int j=0;jGetSubworld()==world) - { - if(iGetSubworld()==world)i++; - } - return i; -} -void plPXPhysicalController::Update(bool prestep, hsScalar delSecs) -{ - // Apparently the user data field of the controllers is broken -// uint32_t 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++) - { - plPXPhysicalController* ac = gControllers[i]; - - hsAssert(ac, "Bad avatar controller"); - if (prestep) - { - if (gRebuildCache) - ac->fController->reportSceneChanged(); - ac->IApply(delSecs); - } - else - { - gControllerMgr.updateControllers(); - ac->ISendUpdates(delSecs); - - if (ac->GetSubworldCI()) - ac->fPrevSubworldW2L = ac->GetSubworldCI()->GetWorldToLocal(); - else - { - if (!ac->fPrevSubworldW2L.IsIdentity()) - ac->fPrevSubworldW2L.Reset(); - } - } - } - - gRebuildCache = false; -} - -void plPXPhysicalController::RebuildCache() -{ - gRebuildCache = true; -} -void plPXPhysicalController::IInformDetectors(bool entering) -{ - 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) - { - plCollideMsg* msg = new plCollideMsg; - - msg->fOtherKey = fOwner; - msg->fEntering = entering; - msg->AddReceiver(physical->GetKey()); - msg->Send(); - } - } - } - - - } - -} - -////////////////////////////////////////////////////////////////////////// - - -class PXControllerHitReport : public NxUserControllerHitReport -{ -public: - virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit) - { - plPXPhysicalController* ac = plPXPhysicalController::FindController(hit.controller); - - NxActor& actor = hit.shape->getActor(); - plPXPhysical* phys = (plPXPhysical*)actor.userData; - - static hsScalar SlopeLimit = kSLOPELIMIT; - hsVector3 normal = plPXConvert::Vector(hit.worldNormal); - hsScalar dot = normal * kAvatarUp; - if ( dot < SlopeLimit ) - ac->AddSlidingNormal(normal); - else - ac->GroundHit(); - -#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. - if (actor.isDynamic() ) - { - 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 = 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); - - hsVector3 dir = plPXConvert::Vector(hit.dir); - // We only allow horizontal pushes. Vertical pushes when we stand on - // dynamic objects creates useless stress on the solver. - if (dir.fZ < 0) - { - dir.fZ = 0; - dir.Normalize(); - } - - if (!dir.IsEmpty()) - { - static hsScalar kForceScale = 5.f; - //static hsScalar kForceScale = 4.f; - NxF32 coeff = actor.getMass() * hit.length * kForceScale; - hsPoint3 pos((hsScalar)hit.worldPos.x, (hsScalar)hit.worldPos.y, (hsScalar)hit.worldPos.z); - phys->SetHitForce((dir*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->fPushingPhysical = phys; - ac->fFacingPushingPhysical = (inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false); - } - - return NX_ACTION_NONE; - } - - virtual NxControllerAction onControllerHit(const NxControllersHit& hit) - { - return NX_ACTION_NONE; - } - -} gMyReport; - - -////////////////////////////////////////////////////////////////////////// - -const hsScalar plPXPhysicalController::kAirTimeThreshold = .1f; // seconds - -plPXPhysicalController::plPXPhysicalController(plKey ownerSO, hsScalar radius, hsScalar height) - : fOwner(ownerSO) - , fWorldKey(nil) - , fRadius(radius) - , fHeight(height) - , fController(nil) - , fLinearVelocity(0, 0, 0) - , fAngularVelocity(0) - , fAchievedLinearVelocity(0, 0, 0) - , fLocalPosition(0, 0, 0) - , fLocalRotation(0, 0, 0, 1) - , fEnable(true) - , fEnableChanged(false) - , fLOSDB(plSimDefs::kLOSDBNone) - , fGroundHit(false) - , fFalseGround(false) - , fTimeInAir(0) - , fPushingPhysical(nil) - , fFacingPushingPhysical(false) - , fProxyGen(nil) - , fKinematicActor(nil) - , fKinematic(false) - , fKinematicChanged(false) - , fKinematicEnableNextUpdate(false) - , fHitHead(false) -{ - gControllers.push_back(this); - fLastGlobalLoc.Reset(); - ICreateController(); - Enable(false); -} - -plPXPhysicalController::~plPXPhysicalController() -{ - IDeleteController(); - - for (int i = 0; i < gControllers.size(); i++) - { - if (gControllers[i] == this) - { - gControllers.erase(gControllers.begin()+i); - break; - } - } - - delete fProxyGen; -} - -// WARNING: If this is an armatureMod, it'll have its own idea about when -// physics should be enabled/disabled. Use plArmatureModBase::EnablePhysics() instead. -void plPXPhysicalController::Enable(bool enable) -{ - if (fEnable != enable) - { - fEnable = enable; - if (fEnable) - fEnableChanged = true; - else - { - // See ISendUpdates for why we don't re-enable right away - fController->setCollision(fEnable); - } - } -} - -void plPXPhysicalController::AddSlidingNormal(hsVector3 vec) -{ - // We get lots of duplicates, so check. - int i; - for (i = 0; i < fSlidingNormals.GetCount(); i++) - { - if (hsABS(fSlidingNormals[i].fX - vec.fX) <= .01 && - hsABS(fSlidingNormals[i].fY - vec.fY) <= .01 && - hsABS(fSlidingNormals[i].fZ - vec.fZ) <= .01) - { - return; - } - } - fSlidingNormals.Append(vec); -} - - -void plPXPhysicalController::IGetPositionSim(hsPoint3& pos) const -{ - const NxExtendedVec3& nxPos = fController->getPosition(); - pos.Set(hsScalar(nxPos.x), hsScalar(nxPos.y), hsScalar(nxPos.z) - kPhysZOffset); -} - -void plPXPhysicalController::SetSubworld(plKey world) -{ - if (fWorldKey != world) - { - bool wasEnabled = fEnable; -#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 - - IDeleteController(); - fWorldKey = world; - ICreateController(); - if (wasEnabled) - Enable(false); - // need to disable the kinematic also so that it doesn't trip over random detector regions - fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); - hsMatrix44 globalLoc = fLastGlobalLoc; - if (GetSubworldCI()) - fPrevSubworldW2L = GetSubworldCI()->GetWorldToLocal(); - ISetGlobalLoc(globalLoc); - // we need to let the re-enable code put thing in order... so that 0,0,0 is not triggered and ISendUpdates do the enable and update detectors - if (wasEnabled) - Enable(true); - // and then re-enable the kinematic on the next update (ISendUpdates) - fKinematicEnableNextUpdate = true; - plPXPhysicalController::RebuildCache(); - } -} - -const plCoordinateInterface* plPXPhysicalController::GetSubworldCI() const -{ - if (fWorldKey) - { - plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); - if (so) - return so->GetCoordinateInterface(); - } - return nil; -} - -void plPXPhysicalController::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 plPXPhysicalController::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(); - } -} - -void plPXPhysicalController::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(); -} - -void plPXPhysicalController::IMatchKinematicToController() -{ - if ( fKinematicActor) - { - NxExtendedVec3 cPos = fController->getPosition(); - 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 plPXPhysicalController::IMoveKinematicToController(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 (fEnable || fKinematic) - { - 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 plPXPhysicalController::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 (fEnable || fKinematic) - fKinematicActor->moveGlobalPosition(plPXConvert::Point(kPos)); - else - fKinematicActor->setGlobalPosition(plPXConvert::Point(kPos)); -} - - -void plPXPhysicalController::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 plPXPhysicalController::IsKinematic() -{ - if (fKinematicActor) - { -#ifdef PHYSX_KINEMATIC_IS_DISABLED - if (!fKinematicActor->readActorFlag(NX_AF_DISABLE_COLLISION)) - return true; -#else - return fKinematic; -#endif - } - return false; -} - -void plPXPhysicalController::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 plPXPhysicalController::IApply(hsScalar delSecs) -{ - /*static const uint32_t collideFlags = - 1<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, fLastGlobalLoc, .0001f)) - { - ISetKinematicLoc(l2w); - } - } - // then jump out - return; - } - - if (!fEnable) - return; - - bool gotGroundHit = fGroundHit; - fGroundHit = false; - - fPushingPhysical = nil; - fFacingPushingPhysical = 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, fLastGlobalLoc, .0001f)) - ISetGlobalLoc(l2w); - - // Convert our avatar relative velocity to subworld relative - if (!fLinearVelocity.IsEmpty()) - { - fLinearVelocity = l2w * fLinearVelocity; - - const plCoordinateInterface* subworldCI = GetSubworldCI(); - if (subworldCI) - fLinearVelocity = subworldCI->GetWorldToLocal() * fLinearVelocity; - } - - // 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(fLinearVelocity.fZ) < 0.001f) - { - static const float kGravity = -32.f; - - // 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. - hsScalar prevZVel = fAchievedLinearVelocity.fZ; - if (IsOnGround()) - prevZVel = hsMinimum(prevZVel, 0.f); - - hsScalar 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. - hsScalar maxGrav = -.001f / delSecs; - if (grav > maxGrav) - grav = maxGrav; - - fLinearVelocity.fZ = prevZVel + grav; - - // Technically this is nonsensical and wrong, capping our velocity to - // an accelleration constant. But no one seems to really mind. - if (fLinearVelocity.fZ < kGravity) - fLinearVelocity.fZ = kGravity; - } - - // If we're airborne and the velocity isn't set, use the velocity from - // the last frame so we maintain momentum. - if (!IsOnGround() && fLinearVelocity.fX == 0.f && fLinearVelocity.fY == 0.f) - { - fLinearVelocity.fX = fAchievedLinearVelocity.fX; - fLinearVelocity.fY = fAchievedLinearVelocity.fY; - } - - if (!IsOnGround() || IsOnFalseGround()) - { - // We're not on solid ground, so we should be sliding against whatever - // we're hitting (like a rock cliff). Each vector in fSlidingNormals is - // the surface normal of a collision that's too steep to be ground, so - // we project our current velocity onto that plane and slide along the - // wall. - // - // Also, sometimes PhysX reports a bunch of collisions from the wall, - // but nothing from underneath (when there should be). So if we're not - // touching ground, we offset the avatar in the direction of the - // surface normal(s). This doesn't fix the issue 100%, but it's a hell - // of a lot better than nothing, and suitable duct tape until a future - // PhysX revision fixes the issue. - // - // Yes, there's room for optimization here if we care. - hsVector3 offset(0.f, 0.f, 0.f); - for (i = 0; i < fSlidingNormals.GetCount(); i++) - { - offset += fSlidingNormals[i]; - - hsVector3 velNorm = fLinearVelocity; - if (velNorm.MagnitudeSquared() > 0) - velNorm.Normalize(); - - if (velNorm * fSlidingNormals[i] < 0) - { - hsVector3 proj = (velNorm % fSlidingNormals[i]) % fSlidingNormals[i]; - if (velNorm * proj < 0) - proj *= -1.f; - - fLinearVelocity = fLinearVelocity.Magnitude() * proj; - } - } - if (offset.MagnitudeSquared() > 0) - { - // 5 ft/sec is roughly the speed we walk backwards. - // The higher the value, the less likely you'll trip - // the bug, and this seems reasonable. - offset.Normalize(); - fLinearVelocity += offset * 5; - } - } - - // Scale the velocity to our actual step size (by default it's feet/sec) - NxVec3 vel(fLinearVelocity.fX * delSecs, fLinearVelocity.fY * delSecs, fLinearVelocity.fZ * delSecs); - NxU32 colFlags = 0; - - fGroundHit = false; - fFalseGround = false; - fSlidingNormals.Swap(fPrevSlidingNormals); - fSlidingNormals.SetCount(0); - -#ifndef PLASMA_EXTERNAL_RELEASE - fDbgCollisionInfo.SetCount(0); -#endif // PLASMA_EXTERNAL_RELEASE - - fController->move(vel, collideFlags, 0.0001, colFlags); - - - ICheckForFalseGround(); - /*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&NXCC_COLLISION_DOWN )==NXCC_COLLISION_DOWN )&&(fGroundHit==false)) - { - fFalseGround=true; - } - /* - The top sphere half was hit, but the ControllerHit Report doesn't know - In IUpdate fHitHead will be used to keep from gaining unrealistic velocity in the x&y Direction - */ - if(colFlags&NXCC_COLLISION_UP) - { - fHitHead=true; - } - -#ifndef PLASMA_EXTERNAL_RELEASE - if (fDebugDisplay) - IDrawDebugDisplay(); -#endif // PLASMA_EXTERNAL_RELEASE - } -} - -void plPXPhysicalController::ISendUpdates(hsScalar delSecs) -{ - if (!fEnable || fKinematic) - { - // When we're in non-phys or a behavior we can't go through the rest of the function - // so we need to get out early, but we need to update the current position if we're - // in a subworld. - - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - const plCoordinateInterface* ci = GetSubworldCI(); - if (ci && so) - { - const hsMatrix44& soL2W = so->GetCoordinateInterface()->GetLocalToWorld(); - const hsMatrix44& ciL2W = ci->GetLocalToWorld(); - - hsMatrix44 l2w = fPrevSubworldW2L * soL2W; - l2w = ciL2W * l2w; - - hsMatrix44 w2l; - l2w.GetInverse(&w2l); - - ((plCoordinateInterface*)so->GetCoordinateInterface())->SetTransform(l2w, w2l); - ((plCoordinateInterface*)so->GetCoordinateInterface())->FlushTransform(); - - ISetGlobalLoc(l2w); - } - - return; - } - - // PhysX loves to cache stuff. However, it doesn't like to update it (see - // the RebuildCache crap above). Say the avatar is disabled and sitting at - // point 0,0,0. We warp him to some other position and enable him. If you - // do the enable before the sim step is done, regardless of whether you move - // him first, he will send out a penetration with any detector at 0,0,0. As - // far as I can tell there's no way around this, and I tried a lot of things. - // The only solution I found is to move the avatar, run the sim step, then - // enable him. This means he won't trigger any detectors at his new position - // until the next frame, but hopefully that won't be too noticeable. - if (fEnableChanged) - { - fEnableChanged = false; - fController->setCollision(fEnable); -#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); - } - if (fKinematicChanged) - { - fKinematicChanged = false; - fController->setCollision(true); -#ifdef PHYSX_KINEMATIC_IS_DISABLED - fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); -#endif // PHYSX_KINEMATIC_IS_DISABLED - } - if (fKinematicEnableNextUpdate) - { - fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); - fKinematicEnableNextUpdate = false; - } - - if (!fGroundHit && !fFalseGround) - fTimeInAir += delSecs; - else - fTimeInAir = 0.f; - - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) - { - // Get the current position of the physical - hsPoint3 curLocalPos; - IGetPositionSim(curLocalPos); - IMoveKinematicToController(curLocalPos); - - { - const hsMatrix44& w2l = so->GetCoordinateInterface()->GetLocalToWorld(); - fAchievedLinearVelocity = hsVector3(curLocalPos - fLocalPosition); - fAchievedLinearVelocity /= delSecs; - } - /*if we hit our head the sweep api might try to - move us laterally to go as high as we requested kind of like autostep, to top it off the - way the NxCharacter and the sweep api work as a whole NxControllerHitReport::OnShapeHit - wont be called regarding the head blow. - if we are airborne: with out this we will gain large amounts of velocity in the x y plane - and on account of fAchievedLinearVelocity being used in the next step we will fly sideways - */ - bool headhit=fHitHead; - fHitHead=false; - if(headhit&&!(fGroundHit||fFalseGround)) - { - //we have hit our head and we don't have anything beneath our feet - //not really friction just a way to make it seem more realistic keep between 0 and 1 - hsScalar headFriction=0.0; - fAchievedLinearVelocity.fX=(1.0-headFriction)*fLinearVelocity.fX; - fAchievedLinearVelocity.fY=(1.0-headFriction)*fLinearVelocity.fY; - //only clamping when hitting head and going upwards, if going down leave it be - // this should only occur when going down stairwells with low ceilings like in cleft - //kitchen area - if(fAchievedLinearVelocity.fZ>0.0) - { - fAchievedLinearVelocity.fZ=0.0; - } - - } - - // Apply angular velocity - if (fAngularVelocity != 0.f) - { - hsScalar angle; - hsVector3 axis; - fLocalRotation.NormalizeIfNeeded(); - fLocalRotation.GetAngleAxis(&angle, &axis); - - // adjust it (quaternions are weird...) - if (axis.fZ < 0) - angle = (2*hsScalarPI) - angle; // axis is backwards, so reverse the angle too - - angle += fAngularVelocity * delSecs; - - // make sure we wrap around - if (angle < 0) - angle = (2*hsScalarPI) + angle; // angle is -, so this works like a subtract - if (angle >= (2*hsScalarPI)) - angle = angle - (2*hsScalarPI); - - // and set the new angle - fLocalRotation.SetAngleAxis(angle, hsVector3(0,0,1)); - } - - // We can't only send updates when the physical position changes because the - // world relative position may be changing all the time if we're in a subworld. -// if (curLocalPos != fLocalPosition || fAngularVelocity != 0.f) - { - fLocalPosition = curLocalPos; - - // Apply rotation and translation - fLocalRotation.MakeMatrix(&fLastGlobalLoc); - fLastGlobalLoc.SetTranslate(&fLocalPosition); - - // Localize to global coords if in a subworld - const plCoordinateInterface* ci = GetSubworldCI(); - if (ci) - { - const hsMatrix44& l2w = ci->GetLocalToWorld(); - fLastGlobalLoc = l2w * fLastGlobalLoc; - } - - plCorrectionMsg* corrMsg = new plCorrectionMsg; - corrMsg->fLocalToWorld = fLastGlobalLoc; - corrMsg->fLocalToWorld.GetInverse(&corrMsg->fWorldToLocal); - corrMsg->fDirtySynch = true; - - hsMatrix44 w2l; - fLastGlobalLoc.GetInverse(&w2l); - //if (fProxyGen) - // fProxyGen->SetTransform(fLastGlobalLoc, w2l); - - // Send the new position to the plArmatureMod and the scene object - const plArmatureMod* armMod = plArmatureMod::ConvertNoRef(so->GetModifierByType(plArmatureMod::Index())); - if (armMod) - corrMsg->AddReceiver(armMod->GetKey()); - corrMsg->AddReceiver(fOwner); - - corrMsg->Send(); - } - } - - fLinearVelocity.Set(0, 0, 0); - fAngularVelocity = 0; -} - -void plPXPhysicalController::ICheckForFalseGround() -{ - if (fGroundHit) - return; // Already collided with "real" ground. - - // We need to check for the case where the avatar hasn't collided with "ground", but is colliding - // with a few other objects so that he's not actually falling (wedged in between some slopes). - // We do this by answering the following question (in 2d top-down space): "If you sort the contact - // normals by angle, is there a large enough gap between normals?" - // - // If you think in terms of geometry, this means a collection of surfaces are all pushing on you. - // If they're pushing from all sides, you have nowhere to go, and you won't fall. There needs to be - // a gap, so that you're pushed out and have somewhere to fall. This is the same as finding a gap - // larger than 180 degrees between sorted normals. - // - // The problem is that on top of that, the avatar needs enough force to shove him out that gap (he - // has to overcome friction). I deal with that by making the threshold (360 - (180 - 60) = 240). I've - // seen up to 220 reached in actual gameplay in a situation where we'd want this to take effect. - // This is the same running into 2 walls where the angle between them is 60. - int i, j; - const hsScalar threshold = hsScalarDegToRad(240); - int numContacts = fSlidingNormals.GetCount() + fPrevSlidingNormals.GetCount(); - if (numContacts >= 2) - { - // For extra fun... PhysX will actually report some collisions every other frame, as though - // we're bouncing back and forth between the two (or more) objects blocking us. So it's not - // enough to look at this frame's collisions, we have to check previous frames too. - hsTArray fCollisionAngles; - fCollisionAngles.SetCount(numContacts); - int angleIdx = 0; - for (i = 0; i < fSlidingNormals.GetCount(); i++, angleIdx++) - { - fCollisionAngles[angleIdx] = hsATan2(fSlidingNormals[i].fY, fSlidingNormals[i].fX); - } - for (i = 0; i < fPrevSlidingNormals.GetCount(); i++, angleIdx++) - { - fCollisionAngles[angleIdx] = hsATan2(fPrevSlidingNormals[i].fY, fPrevSlidingNormals[i].fX); - } - - // numContacts is rarely larger than 6, so let's do a simple bubble sort. - for (i = 0; i < numContacts; i++) - { - for (j = i + 1; j < numContacts; j++) - { - if (fCollisionAngles[i] > fCollisionAngles[j]) - { - hsScalar tempAngle = fCollisionAngles[i]; - fCollisionAngles[i] = fCollisionAngles[j]; - fCollisionAngles[j] = tempAngle; - } - } - } - - // sorted, now we check. - for (i = 1; i < numContacts; i++) - { - if (fCollisionAngles[i] - fCollisionAngles[i - 1] >= threshold) - break; - } - - if (i == numContacts) - { - // We got to the end. Check the last with the first and make your decision. - if (!(fCollisionAngles[0] - fCollisionAngles[numContacts - 1] >= (threshold - 2 * hsScalarPI))) - fFalseGround = true; - } - } -} - -void plPXPhysicalController::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; - actorDesc.shapes.pushBack(&capDesc); - NxBodyDesc bodyDesc; - bodyDesc.mass = 1.f; - actorDesc.body = &bodyDesc; - bodyDesc.flags |= NX_BF_KINEMATIC; - 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 = new plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); - fProxyGen->Init(this); -} - -void plPXPhysicalController::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); - } -} - -// Make a visible object that can be viewed by users for debugging purposes. -plDrawableSpans* plPXPhysicalController::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) -{ - plDrawableSpans* myDraw = addTo; - - bool 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) - { - bool 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 - -void plPXPhysicalController::IDrawDebugDisplay() -{ - plDebugText &debugTxt = plDebugText::Instance(); - char strBuf[ 2048 ]; - int lineHeight = debugTxt.GetFontSize() + 4; - uint32_t 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 diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h deleted file mode 100644 index be03fabe..00000000 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h +++ /dev/null @@ -1,211 +0,0 @@ -/*==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==*/ -#ifndef plPXPhysicalController_h_inc -#define plPXPhysicalController_h_inc - -#include "plAvatar/plAvCallbackAction.h" -#include "hsQuat.h" - -#define PHYSX_ONLY_TRIGGER_FROM_KINEMATIC 1 - -class NxController; -class NxCapsuleController; -class NxActor; -class plCoordinateInterface; -class plPhysicalProxy; -class plDrawableSpans; -class hsGMaterial; -class NxCapsule; -#ifndef PLASMA_EXTERNAL_RELEASE -class plDbgCollisionInfo -{ -public: - plSceneObject *fSO; - hsVector3 fNormal; - bool fOverlap; -}; -#endif // PLASMA_EXTERNAL_RELEASE - -class plPXPhysicalController : public plPhysicalController -{ -public: - plPXPhysicalController(plKey ownerSO, hsScalar height, hsScalar radius); - virtual ~plPXPhysicalController(); - - virtual void Enable(bool enable); - virtual bool IsEnabled() const { return fEnable; } - - virtual void SetLOSDB(plSimDefs::plLOSDB losDB) { fLOSDB = losDB; } - plSimDefs::plLOSDB GetLOSDB() const { return fLOSDB; } - - virtual void SetVelocities(const hsVector3& linearVel, hsScalar angVel) - { - fLinearVelocity = linearVel; - fAngularVelocity = angVel; - } - - virtual const hsVector3& GetLinearVelocity() const { return fAchievedLinearVelocity; } - virtual void ResetAchievedLinearVelocity() { fAchievedLinearVelocity.Set(0.f, 0.f, 0.f); } - - virtual plKey GetSubworld() const { return fWorldKey; } - virtual void SetSubworld(plKey world); - - virtual bool IsOnGround() const { return fTimeInAir < kAirTimeThreshold || fFalseGround; } - virtual bool IsOnFalseGround() const { return fFalseGround && !fGroundHit; } - virtual void GroundHit() { fGroundHit = true; } - virtual hsScalar GetAirTime() const { return fTimeInAir; } - virtual void ResetAirTime() { fTimeInAir = 0.f; } - virtual void AddSlidingNormal(hsVector3 vec); - virtual hsTArray* GetSlidingNormals() { return &fSlidingNormals; } - - virtual plPhysical* GetPushingPhysical() const { return fPushingPhysical; } - virtual bool GetFacingPushingPhysical() const { return fFacingPushingPhysical; } - - virtual const plCoordinateInterface* GetSubworldCI() const; - - virtual void GetState(hsPoint3& pos, float& zRot); - virtual void SetState(const hsPoint3& pos, float zRot); - - plKey GetOwner() const { return fOwner; } - - // Called by the simulation mgr each frame - static void Update(bool prestep, hsScalar delSecs); - // Used by the LOS mgr to find the controller for an actor it hit - static plPXPhysicalController* GetController(NxActor& actor, bool* isController); - // test to see if there are any controllers (i.e. avatars) in this subworld - static bool plPXPhysicalController::AnyControllersInThisWorld(plKey world); - static int plPXPhysicalController::NumControllers(); - static int plPXPhysicalController::GetControllersInThisSubWorld(plKey world, int maxToReturn, - plPXPhysicalController** bufferout); - static int plPXPhysicalController::GetNumberOfControllersInThisSubWorld(plKey world); - // Call this if a static physical in the scene has changed (unloaded, - // collision enabled/disabled, etc) - static void RebuildCache(); - - virtual void GetPositionSim(hsPoint3& pos) const { IGetPositionSim(pos); } - - virtual void Kinematic(bool state); - virtual bool IsKinematic(); - virtual void GetKinematicPosition(hsPoint3& pos); - - virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); - - virtual const hsMatrix44& GetPrevSubworldW2L() { return fPrevSubworldW2L; } - - virtual void SetSeek(bool seek){fSeeking=seek;} - virtual void GetWorldSpaceCapsule(NxCapsule& cap); -#ifndef PLASMA_EXTERNAL_RELEASE - static bool fDebugDisplay; -#endif // PLASMA_EXTERNAL_RELEASE - -protected: - static const hsScalar kAirTimeThreshold; - - friend class PXControllerHitReport; - static plPXPhysicalController* FindController(NxController* controller); - - void IApply(hsScalar delSecs); - void ISendUpdates(hsScalar delSecs); - void ICheckForFalseGround(); - void ISetGlobalLoc(const hsMatrix44& l2w); - void IMatchKinematicToController(); - void IMoveKinematicToController(hsPoint3& pos); - void ISetKinematicLoc(const hsMatrix44& l2w); - void IGetPositionSim(hsPoint3& pos) const; - - void ICreateController(); - void IDeleteController(); - - void IInformDetectors(bool entering); - - plKey fOwner; - plKey fWorldKey; - hsScalar fRadius, fHeight; - NxCapsuleController* fController; - - // this is the kinematic actor for triggering things when the avatar is collision-less during behaviors - NxActor* fKinematicActor; - - hsVector3 fLinearVelocity; - hsScalar fAngularVelocity; - - hsVector3 fAchievedLinearVelocity; - - // The global position and rotation of the avatar last time we set it (so we - // can detect if someone else moves him) - hsMatrix44 fLastGlobalLoc; - // - hsPoint3 fLocalPosition; - hsQuat fLocalRotation; - - hsMatrix44 fPrevSubworldW2L; - - bool fEnable; - bool fEnableChanged; - plSimDefs::plLOSDB fLOSDB; - - bool fKinematic; - bool fKinematicChanged; - bool fKinematicEnableNextUpdate; - - bool fGroundHit; - bool fFalseGround; - hsScalar fTimeInAir; - hsTArray fSlidingNormals; - hsTArray fPrevSlidingNormals; - -#ifndef PLASMA_EXTERNAL_RELEASE - hsTArray fDbgCollisionInfo; - void IDrawDebugDisplay(); -#endif // PLASMA_EXTERNAL_RELEASE - - plPhysical* fPushingPhysical; - bool fFacingPushingPhysical; - - plPhysicalProxy* fProxyGen; // visual proxy for debugging - - bool fHitHead; - - bool fSeeking; -}; - -#endif // plPXPhysicalController_h_inc diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp index cdb1b968..ef595a72 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp @@ -40,13 +40,11 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #include "plPXPhysicalControllerCore.h" -#include "plgDispatch.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" @@ -54,130 +52,100 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pnMessage/plSetNetGroupIDMsg.h" #include "plMessage/plCollideMsg.h" #include "plModifier/plDetectorLog.h" -//#include "NxVecExtendedVec3.h" + +#include "plSurface/hsGMaterial.h" // For our proxy +#include "plSurface/plLayerInterface.h" // For our proxy #include "NxPhysics.h" -#include "ControllerManager.h" #include "NxCapsuleController.h" #include "NxCapsuleShape.h" +#include "ControllerManager.h" -#include "plSurface/hsGMaterial.h" -#include "plSurface/plLayerInterface.h" - - -#ifndef PLASMA_EXTERNAL_RELEASE -#include "plPipeline/plDebugText.h" -#endif - -#define kPhysxSkinWidth 0.1f -#define kPhysZOffset ((fRadius + (fHeight / 2)) + kPhysxSkinWidth) -#define kPhysicalHeightFudge 0.0f -#define STEP_OFFSET 1.f -#define kAvatarMass 160.f - +static ControllerManager gControllerMgr; +static std::vector gControllers; +static bool gRebuildCache = false; #ifndef PLASMA_EXTERNAL_RELEASE bool plPXPhysicalControllerCore::fDebugDisplay = false; #endif // PLASMA_EXTERNAL_RELEASE int plPXPhysicalControllerCore::fPXControllersMax = 0; -static ControllerManager gControllerMgr; -static std::vector gControllers; -static bool gRebuildCache=false; +#define kCCTSkinWidth 0.1f +#define kCCTStepOffset 0.6f +#define kCCTZOffset ((fRadius + (fHeight / 2)) + kCCTSkinWidth) +#define kPhysHeightCorrection 0.8f +#define kPhysZOffset ((kCCTZOffset + (kPhysHeightCorrection / 2)) - 0.05f) +#define kAvatarMass 200.0f -class PXControllerHitReportWalk : public NxUserControllerHitReport +static class PXControllerHitReport : public NxUserControllerHitReport { public: virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit) { - plPXPhysicalControllerCore* ac = plPXPhysicalControllerCore::FindController(hit.controller); + plPXPhysicalControllerCore* controller = (plPXPhysicalControllerCore*)hit.controller->getUserData(); NxActor& actor = hit.shape->getActor(); plPXPhysical* phys = (plPXPhysical*)actor.userData; 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); + + NxCapsule capsule; + controller->GetWorldSpaceCapsule(capsule); + info.fOverlap = hit.shape->checkOverlapCapsule(capsule); + + controller->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((float)hit.worldPos.x, (float)hit.worldPos.y, (float)hit.worldPos.z); - NxExtendedVec3 controllerPos=hit.controller->getPosition(); - hsVector3 bottomOfTheCapsule((float)controllerPos.x,(float)controllerPos.y,(float)controllerPos.z); - bottomOfTheCapsule.fZ=bottomOfTheCapsule.fZ-(ac->fHeight/2.0f + ac->fRadius); - if (actor.isDynamic() && phys ) + if (actor.isDynamic()) { - if((hit.worldPos.z- bottomOfTheCapsule.fZ)<=ac->fRadius)//bottom hemisphere + if (!actor.readBodyFlag(NX_BF_KINEMATIC) && !actor.readBodyFlag(NX_BF_FROZEN)) { - // If this is an animated physical, we can stand on it - if (phys->GetProperty(plSimulationInterface::kPhysAnim)) + // Don't apply force when standing on top of an object. + if (normal.fZ < 0.85f) { - if(normal.fZ>=0) + hsVector3 velocity = controller->GetLinearVelocity(); + velocity.fZ = 0.0f; + float length = velocity.Magnitude(); + if (length > 0) { - //we consider this ground - ac->fMovementInterface->AddOnTopOfObject(phys); + // Only allow horizontal pushes for now + NxVec3 hitDir = hit.worldPos - hit.controller->getPosition(); + hitDir.z = 0.0f; + hitDir.normalize(); + + // Get controller speed along the hitDir + float cctProj = velocity.fX * hitDir.x + velocity.fY * hitDir.y; + length = length + cctProj / 2.0f; + + // Get hit actors speed along the hitDir + float hitProj = actor.getLinearVelocity().dot(hitDir); + if (hitProj > 0) + length -= hitProj; + + length *= kAvatarMass; + + hsPoint3 pos((float)hit.worldPos.x, (float)hit.worldPos.y, (float)hit.worldPos.z); + phys->SetHitForce(plPXConvert::Vector(hitDir * length), pos); + controller->AddDynamicHit(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 = 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); - - // Now, let's impart some force, fool. - // Note: This is a VERY simple implementation. The old one was too hacky and caused weird stuff to happen. - hsVector3 acceleration = (ac->GetLinearVelocity() - plPXConvert::Vector(actor.getLinearVelocity())); - hsVector3 force2impart = acceleration * kAvatarMass; - // Bad things happen if we impart force directly on top of the actor, so let's allow the avatar to run - // over those physicals and not break them. This is mostly an issue with stuff smaller than step size. - if (!force2impart.IsEmpty() && normal.fZ < .90f) - phys->SetHitForce(force2impart, pos); - } } else // else if the avatar hit a static { + controller->fMovementStrategy->AddContactNormals(normal); return NX_ACTION_NONE; } - if (phys->GetProperty(plSimulationInterface::kAvAnimPushable)) + if (phys && phys->GetProperty(plSimulationInterface::kAvAnimPushable)) { - hsQuat inverseRotation = ac->fLocalRotation.Inverse(); + hsQuat inverseRotation = controller->fLocalRotation.Inverse(); hsVector3 normal = plPXConvert::Vector(hit.worldNormal); - ac->SetPushingPhysical( phys); - ac->SetFacingPushingPhysical((inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false)); + controller->SetPushingPhysical(phys); + controller->SetFacingPushingPhysical((inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false)); } return NX_ACTION_NONE; } @@ -185,161 +153,35 @@ public: { return NX_ACTION_NONE; } +} gControllerHitReport; -} gMyReport; - - -plPhysicalControllerCore* plPhysicalControllerCore::Create(plKey ownerSO, float height, float width) +plPhysicalControllerCore* plPhysicalControllerCore::Create(plKey ownerSO, float height, float width, bool human) { - // Test to see how many controller there already is - if ( !plPXPhysicalControllerCore::fPXControllersMax || plPXPhysicalControllerCore::NumControllers() < plPXPhysicalControllerCore::fPXControllersMax ) + if (!plPXPhysicalControllerCore::fPXControllersMax || gControllers.size() < plPXPhysicalControllerCore::fPXControllersMax) { - float radius = width / 2.f; - float realHeight = height - width + kPhysicalHeightFudge; - return new plPXPhysicalControllerCore(ownerSO, realHeight,radius); + float radius = width / 2.0f; + float realHeight = height - width; + return new plPXPhysicalControllerCore(ownerSO, realHeight, radius, human); } 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, float height, float radius) - : plPhysicalControllerCore(ownerSO,height,radius) - , fController(nil) - , fProxyGen(nil) - , fKinematicActor(nil) - ,fPreferedRadius(radius) - ,fPreferedHeight(height) - , fBehavingLikeAnimatedPhys(true) +plPXPhysicalControllerCore::plPXPhysicalControllerCore(plKey ownerSO, float height, float radius, bool human) + : plPhysicalControllerCore(ownerSO, height, radius), + fController(nil), + fActor(nil), + fProxyGen(nil), + fKinematicCCT(true), + fHuman(human) { - fLocalPosition.Set(0, 0, 0); - fLocalRotation.Set(0, 0, 0, 1); + ICreateController(fLocalPosition); + fActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); 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(); - for (int i = 0; i < gControllers.size(); i++) + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) { if (gControllers[i] == this) { @@ -347,445 +189,109 @@ plPXPhysicalControllerCore::~plPXPhysicalControllerCore() break; } } - delete fProxyGen; -} -void plPXPhysicalControllerCore::IMatchKinematicToController() -{ - if ( fKinematicActor) - { - NxExtendedVec3 cPos = fController->getPosition(); - 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 ); - if (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) - fKinematicActor->moveGlobalPosition(kinPos); - else - 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 (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) - { - 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 (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) - { - 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); - } - } - } -} + IDeleteController(); -void plPXPhysicalControllerCore::ISetKinematicLoc(const hsMatrix44& l2w) -{ - hsPoint3 kPos; - // Update our subworld position and rotation - const plCoordinateInterface* subworldCI = GetSubworldCI(); - if (subworldCI) + // Release any queued messages we may have + int numMsgs = fQueuedCollideMsgs.size(); + if (numMsgs) { - const hsMatrix44& w2s = subworldCI->GetWorldToLocal(); - hsMatrix44 l2s = w2s * l2w; + for (int i = 0; i < numMsgs; ++i) + delete fQueuedCollideMsgs[i]; - l2s.GetTranslate(&kPos); - } - else - { - l2w.GetTranslate(&kPos); + fQueuedCollideMsgs.clear(); } - hsMatrix44 w2l; - l2w.GetInverse(&w2l); - if (fProxyGen) - fProxyGen->SetTransform(l2w, w2l); - - // add z offset - kPos.fZ += kPhysZOffset; - // Update the physical position of kinematic - if (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) - 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(float(nxPos.x), float(nxPos.y), float(nxPos.z) - kPhysZOffset); - } - else - { - NxVec3 Pos = fKinematicActor->getGlobalPosition(); - pos.Set(float(Pos.x), float(Pos.y), float(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 = -10000; // No one should be building down there... (-2000 is kickable limit) - 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 = kAvatarMass; - 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 - // PhysX HACK TURD: Sometimes, using the ***wrong*** function is desirable... - // Changing this to moveGlobalPose will cause camera regions to fire before linking in. - fKinematicActor->setGlobalPose(actor->getGlobalPose()); - - // the proxy for the debug display - //hsAssert(!fProxyGen, "Already have proxy gen, double read?"); - - hsColorRGBA physColor; - float 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 = 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 = kAvatarMass; - 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; - float 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 = 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); - } + delete fProxyGen; } -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 = new plCollideMsg; - msg->fOtherKey = fOwner; - msg->fEntering = entering; - msg->AddReceiver(physical->GetObjectKey()); - if(!deferUntilNextSim) - { - DetectorLog("Sending an %s msg to %s" , entering? "entering":"exit", physical->GetObjectKey()->GetName().c_str()); - msg->Send(); - } - else - { - DetectorLog("Queuing an %s msg to %s, which will be sent after the client update" , entering? "entering":"exit", physical->GetObjectKey()->GetName().c_str()); - plgDispatch::Dispatch()->MsgQueue(msg); - } - } - } - } - } - DetectorLog("Done informing from plPXPhysicalControllerCore::IInformDetectors"); - } -} -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) + { + // Defer until the next physics update. fEnableChanged = true; + } else { - // See ISendUpdates for why we don't re-enable right away - fController->setCollision(fEnabled); + if (!fKinematicCCT) + { + // Dynamic controllers are forced kinematic + fActor->raiseBodyFlag(NX_BF_KINEMATIC); + NxShape* shape = fActor->getShapes()[0]; + shape->setGroup(plSimDefs::kGroupAvatarKinematic); + } } } } 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!"); + + // Inform detectors in the old world that we are leaving + IInformDetectors(false); IDeleteController(); - SimLog("Deleted old controller"); + + // We need our real global location here, not the interpolated location + fLocalRotation.MakeMatrix(&fLastGlobalLoc); + fLastGlobalLoc.SetTranslate(&fLocalPosition); + if (fWorldKey) + { + hsMatrix44 prevSubL2W; + fPrevSubworldW2L.GetInverse(&prevSubL2W); + fLastGlobalLoc = prevSubL2W * fLastGlobalLoc; + } + // Update our scene object so the change isn't wiped out + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + hsMatrix44 globalLocInv; + fLastGlobalLoc.GetInverse(&globalLocInv); + so->SetTransform(fLastGlobalLoc, globalLocInv); + so->FlushTransform(); + } + + // Update Local Position and rotation 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; + fPrevSubworldW2L = subworldCI->GetWorldToLocal(); + hsMatrix44 l2s = fPrevSubworldW2L * fLastGlobalLoc; l2s.GetTranslate(&fLocalPosition); fLocalRotation.SetFromMatrix44(l2s); } else { + fPrevSubworldW2L.Reset(); 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); + + fLastLocalPosition = fLocalPosition; + + // Create new controller + ICreateController(fLocalPosition); 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 * M_PI) - zRot; // axis is backwards, so reverse the angle too + zRot = (2 * float(M_PI)) - 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()); @@ -811,361 +317,542 @@ void plPXPhysicalControllerCore::SetState(const hsPoint3& pos, float zRot) so->FlushTransform(); } } -// kinematic stuff .... should be just for when playing a behavior... -void plPXPhysicalControllerCore::Kinematic(bool state) + +void plPXPhysicalControllerCore::SetMovementStrategy(plMovementStrategy* strategy) +{ + if (fKinematicCCT != strategy->IsKinematic()) + { + IDeleteController(); + fKinematicCCT = !fKinematicCCT; + ICreateController(fLocalPosition); + } + + fMovementStrategy = strategy; +} + +void plPXPhysicalControllerCore::SetGlobalLoc(const hsMatrix44& l2w) { - if (fKinematic != state) + fLastGlobalLoc = l2w; + + // Update our local position and rotation + hsPoint3 prevPosition = fLocalPosition; + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) { - fKinematic = state; - if (fKinematic) + hsMatrix44 l2s = fPrevSubworldW2L * l2w; + + l2s.GetTranslate(&fLocalPosition); + fLocalRotation.SetFromMatrix44(l2s); + } + else + { + l2w.GetTranslate(&fLocalPosition); + fLocalRotation.SetFromMatrix44(l2w); + } + + fLastLocalPosition = fLocalPosition; + + if (fProxyGen) + { + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + fProxyGen->SetTransform(l2w, w2l); + } + + // Update the physical position + if (fKinematicCCT) + { + hsVector3 disp(&fLocalPosition, &prevPosition); + if (disp.Magnitude() > 2.f) { - // 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 + // Teleport the underlying actor most of the way + disp.Normalize(); + disp *= 0.001f; + + hsPoint3 teleportPos = fLocalPosition - disp; + NxVec3 pos(teleportPos.fX, teleportPos.fY, teleportPos.fZ + kPhysZOffset); + fActor->setGlobalPosition(pos); } + + NxExtendedVec3 extPos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kCCTZOffset); + fController->setPosition(extPos); + } + else + { + NxVec3 pos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kPhysZOffset); + if (fActor->readBodyFlag(NX_BF_KINEMATIC)) + fActor->moveGlobalPosition(pos); else - { - fKinematicChanged = true; - } + fActor->setGlobalPosition(pos); } } -bool plPXPhysicalControllerCore::IsKinematic() -{ - return fKinematic; -} -void plPXPhysicalControllerCore::GetKinematicPosition(hsPoint3& pos) + +void plPXPhysicalControllerCore::GetPositionSim(hsPoint3& pos) { - pos.Set(-1,-1,-1); - if ( fKinematicActor ) + if (fKinematicCCT) { - NxVec3 klPos = fKinematicActor->getGlobalPosition(); - pos.Set(float(klPos.x), float(klPos.y), float(klPos.z) - kPhysZOffset); + const NxExtendedVec3& extPos = fController->getPosition(); + pos.Set((float)extPos.x, (float)extPos.y, (float)extPos.z - kCCTZOffset); } -} -void plPXPhysicalControllerCore::UpdatePoststep( float delSecs) -{ - // Apparently the user data field of the controllers is broken -// uint32_t 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++) + else { - 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(); - } + NxVec3 nxPos = fActor->getGlobalPosition(); + pos.Set(nxPos.x, nxPos.y, nxPos.z - kPhysZOffset); } } -void plPXPhysicalControllerCore::UpdatePrestep(float delSecs) + +void plPXPhysicalControllerCore::Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults) { - for (int i = 0; i < gControllers.size(); i++) - { - plPXPhysicalControllerCore* ac = gControllers[i]; + NxU32 colFlags = 0; - hsAssert(ac, "Bad avatar controller"); - //FIXME -#ifndef PLASMA_EXTERNAL_RELEASE - ac->fDbgCollisionInfo.SetCount(0); -#endif // PLASMA_EXTERNAL_RELEASE + fController->move(plPXConvert::Vector(displacement), collideWith, 0.00001f, colFlags); - 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(); - ac->Apply(delSecs); - } -#ifndef PLASMA_EXTERNAL_RELEASE - if (fDebugDisplay) - ac->IDrawDebugDisplay(); -#endif // PLASMA_EXTERNAL_RELEASE - } - gRebuildCache = false; + collisionResults = 0; + if (colFlags & NXCC_COLLISION_DOWN) + collisionResults |= kBottom; + if (colFlags & NXCC_COLLISION_UP) + collisionResults |= kTop; + if (colFlags & NXCC_COLLISION_SIDES) + collisionResults |= kSides; } -void plPXPhysicalControllerCore::UpdatePostSimStep(float delSecs) + +void plPXPhysicalControllerCore::SetLinearVelocitySim(const hsVector3& linearVel) { - for (int i = 0; i < gControllers.size(); i++) + if (!fKinematicCCT) { - plPXPhysicalControllerCore* ac = gControllers[i]; - hsAssert(ac, "Bad avatar controller"); - ac->PostStep(delSecs); - + NxVec3 vel = plPXConvert::Vector(linearVel); + fActor->setLinearVelocity(vel); } } -void plPXPhysicalControllerCore::HandleEnableChanged() + +int plPXPhysicalControllerCore::SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, bool vsDynamics, bool vsStatics, + uint32_t& vsSimGroups, std::vector& hits) { - fEnableChanged = false; - if(this->fBehavingLikeAnimatedPhys) - { - fController->setCollision(fEnabled); - } - else + NxSweepQueryHit queryHit[10]; + + unsigned int flags = NX_SF_ALL_HITS; + if (vsDynamics) + flags |= NX_SF_DYNAMICS; + if (vsStatics) + flags |= NX_SF_STATICS; + + NxVec3 vec; + vec.x = endPos.fX - startPos.fX; + vec.y = endPos.fY - startPos.fY; + vec.z = endPos.fZ - startPos.fZ; + + NxShape* shape = fActor->getShapes()[0]; + NxCapsuleShape* capShape = shape->isCapsule(); + float radius = capShape->getRadius(); + float height = capShape->getHeight(); + + NxCapsule capsule; + capsule.p0 = plPXConvert::Point(startPos); + capsule.p0.z = capsule.p0.z + radius; + capsule.radius = radius; + capsule.p1 = capsule.p0; + capsule.p1.z = capsule.p1.z + height; + + NxScene *scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + int numHits = scene->linearCapsuleSweep(capsule, vec, flags, nil, 10, queryHit, nil, vsSimGroups); + if (numHits) + { + for (int i = 0; i < numHits; ++i) { - fController->setCollision(false); + plControllerSweepRecord currentHit; + currentHit.ObjHit = (plPhysical*)queryHit[i].hitShape->getActor().userData; + if (currentHit.ObjHit) + { + currentHit.Point = plPXConvert::Point(queryHit[i].point); + currentHit.Normal = plPXConvert::Vector(queryHit[i].normal); + hits.push_back(currentHit); + } } -#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,fEnabled); -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND - //IInformDetectors(true); + } + + return hits.size(); } -void plPXPhysicalControllerCore::HandleKinematicChanged() +void plPXPhysicalControllerCore::LeaveAge() { - 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 + SetPushingPhysical(nil); + if (fWorldKey) + SetSubworld(nil); + + // Disable all collisions + fActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); } -void plPXPhysicalControllerCore::HandleKinematicEnableNextUpdate() + +void plPXPhysicalControllerCore::GetWorldSpaceCapsule(NxCapsule& cap) const { - fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); - fKinematicEnableNextUpdate = false; + NxShape* shape = fActor->getShapes()[0]; + NxCapsuleShape* capShape = shape->isCapsule(); + capShape->getWorldCapsule(cap); } -void plPXPhysicalControllerCore::IHandleResize() + +plDrawableSpans* plPXPhysicalControllerCore::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) { + // FIXME + plDrawableSpans* myDraw = addTo; + bool 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) + { + bool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); + + myDraw = plDrawableGenerator::GenerateConicalDrawable(fRadius*10, fHeight*10, + mat, so->GetLocalToWorld(), blended, + nil, &idx, myDraw); + } +*/ + return myDraw; +} - uint32_t collideFlags = - 1<GetScene(this->fWorldKey); -// NxShape** response=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) + + fDynamicHits.push_back(phys); +} + +void plPXPhysicalControllerCore::Apply(float delSecs) +{ + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) { - fHeight=fPreferedHeight; - fRadius=fPreferedRadius; - fController->setRadius(fRadius); - fController->setHeight(fHeight); - - fNeedsResize=false; + controller = gControllers[i]; + if (gRebuildCache && controller->fController) + controller->fController->reportSceneChanged(); + +#ifndef PLASMA_EXTERNAL_RELEASE + controller->fDbgCollisionInfo.SetCount(0); +#endif + + controller->IDispatchQueuedMsgs(); + controller->IApply(delSecs); + controller->IProcessDynamicHits(); } -// delete[] response; + gRebuildCache = false; } -void plPXPhysicalControllerCore::SetControllerDimensions(float radius, float height) +void plPXPhysicalControllerCore::Update(int numSubSteps, float alpha) { - fNeedsResize=false; - if(fRadius!=radius) + gControllerMgr.updateControllers(); + + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) { - fNeedsResize=true; + controller = gControllers[i]; + + controller->IUpdate(numSubSteps, alpha); + +#ifndef PLASMA_EXTERNAL_RELEASE + if (fDebugDisplay) + controller->IDrawDebugDisplay(); +#endif } - if(fHeight!=height) +} +void plPXPhysicalControllerCore::UpdateNonPhysical(float alpha) +{ + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) { - fNeedsResize=true; + controller = gControllers[i]; + controller->IUpdateNonPhysical(alpha); } - fPreferedRadius=radius; - fPreferedHeight=height; } -void plPXPhysicalControllerCore::LeaveAge() +void plPXPhysicalControllerCore::RebuildCache() { gRebuildCache = true; } + +plPXPhysicalControllerCore* plPXPhysicalControllerCore::GetController(NxActor& actor) { - SetPushingPhysical(nil); - if(fWorldKey) this->SetSubworld(nil); - this->fMovementInterface->LeaveAge(); + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) + { + controller = gControllers[i]; + if (controller->fActor == &actor) + return controller; + } + + return nil; } -int plPXPhysicalControllerCore::SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, bool vsDynamics, bool vsStatics, - uint32_t& 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; +bool plPXPhysicalControllerCore::AnyControllersInThisWorld(plKey world) +{ + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) + { + controller = gControllers[i]; + if (controller->GetSubworld() == world) + return true; + } - 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 false; +} +int plPXPhysicalControllerCore::GetNumberOfControllersInThisSubWorld(plKey world) +{ + int count = 0; + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) + { + controller = gControllers[i]; + if (controller->GetSubworld() == world) + ++count; } - return HitsReturned; + return count; } -void plPXPhysicalControllerCore::BehaveLikeAnimatedPhysical(bool actLikeAnAnimatedPhys) +int plPXPhysicalControllerCore::GetControllersInThisSubWorld(plKey world, int maxToReturn, plPXPhysicalControllerCore** bufferout) { - hsAssert(fKinematicActor, "Changing behavior, but plPXPhysicalControllerCore has no Kinematic actor associated with it"); - if(fBehavingLikeAnimatedPhys!=actLikeAnAnimatedPhys) + int count = 0; + plPXPhysicalControllerCore* controller; + int numControllers = gControllers.size(); + for (int i = 0; i < numControllers; ++i) { - fBehavingLikeAnimatedPhys=actLikeAnAnimatedPhys; - if(fKinematicActor) + controller = gControllers[i]; + if (controller->GetSubworld() == world) { - if(actLikeAnAnimatedPhys) + if (count < maxToReturn) { - //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); - - + bufferout[count] = controller; + ++count; } } } -} -bool plPXPhysicalControllerCore::BehavingLikeAnAnimatedPhysical() -{ - hsAssert(fKinematicActor, "plPXPhysicalControllerCore is missing a kinematic actor"); - return fBehavingLikeAnimatedPhys; + return count; } -void plPXPhysicalControllerCore::SetLinearVelocity(const hsVector3& linearVel) +int plPXPhysicalControllerCore::NumControllers() { return gControllers.size(); } + +void plPXPhysicalControllerCore::IHandleEnableChanged() { - plPhysicalControllerCore::SetLinearVelocity(linearVel); - if(fKinematicActor && !fBehavingLikeAnimatedPhys) + // Defered enable + fEnableChanged = false; + + if (!fKinematicCCT) { - NxVec3 vel= plPXConvert::Vector(linearVel); - fKinematicActor->setLinearVelocity(vel); + // Restore dynamic controller + fActor->clearBodyFlag(NX_BF_KINEMATIC); + NxShape* shape = fActor->getShapes()[0]; + shape->setGroup(plSimDefs::kGroupAvatar); } + + // Enable actor collisions + if (fActor->readActorFlag(NX_AF_DISABLE_COLLISION)) + fActor->clearActorFlag(NX_AF_DISABLE_COLLISION); } -void plPXPhysicalControllerCore::SetAngularVelocity(const float angvel) + +void plPXPhysicalControllerCore::IInformDetectors(bool entering) { - plPhysicalControllerCore::SetAngularVelocity(angvel); - if(fKinematicActor && !fBehavingLikeAnimatedPhys) + static const NxU32 kDetectorFlag = 1<GetScene(fWorldKey); + int numCollided = scene->overlapCapsuleShapes(capsule, NX_ALL_SHAPES, kNumShapes, shapes, NULL, kDetectorFlag, NULL, true); + for (int i = 0; i < numCollided; ++i) { - NxVec3 vel(0.0f, 0.0f, angvel); - fKinematicActor->setAngularVelocity(vel); + plPXPhysical* physical = (plPXPhysical*)shapes[i]->getActor().userData; + if (physical && physical->DoReportOn(plSimDefs::kGroupAvatar)) + { + plCollideMsg* msg = new plCollideMsg(); + msg->fOtherKey = fOwner; + msg->fEntering = entering; + msg->AddReceiver(physical->GetObjectKey()); + + // Queue until the next sim step + fQueuedCollideMsgs.push_back(msg); + } } -} -void plPXPhysicalControllerCore::SetVelocities(const hsVector3& linearVel, float angVel) -{ - SetLinearVelocity(linearVel); - SetAngularVelocity(angVel); + + DetectorLog("Done informing from plPXPhysicalControllerCore::IInformDetectors"); } -void plPXPhysicalControllerCore::IMatchControllerToKinematic() +void plPXPhysicalControllerCore::ICreateController(const hsPoint3& pos) { - NxExtendedVec3 newpos; - NxVec3 pos=fKinematicActor->getGlobalPosition(); - newpos.x=pos.x; - newpos.y=pos.y; - newpos.z=pos.z; - fController->setPosition(newpos); + NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + + if (fKinematicCCT) + { + // Use PhysX character controller + NxCapsuleControllerDesc desc; + desc.position.x = pos.fX; + desc.position.y = pos.fY; + desc.position.z = pos.fZ + kCCTZOffset; + desc.upDirection = NX_Z; + desc.slopeLimit = kSlopeLimit; + desc.skinWidth = kCCTSkinWidth; + desc.stepOffset = kCCTStepOffset; + desc.callback = &gControllerHitReport; + desc.userData = this; + desc.radius = fRadius; + desc.height = fHeight; + desc.interactionFlag = NXIF_INTERACTION_EXCLUDE; + fController = (NxCapsuleController*)gControllerMgr.createController(scene, desc); + fActor = fController->getActor(); + + // Set the actor group - Dynamics are in group 1 and will report on everything in group 0. + // We don't want notifications + fActor->setGroup(2); + + // Set the shape group. Not used by the NxController itself, + // But required for correct group interactions in the simulation. + NxShape* shape = fActor->getShapes()[0]; + shape->setGroup(plSimDefs::kGroupAvatarKinematic); + + // In PhysX 2, the kinematic actors scale factor isn't exposed. + // It is hardcoded at 0.8 which doesn't suit, so we have to manually adjust its dimensions. + float kineRadius = fRadius + kCCTSkinWidth; + float kineHeight = fHeight; + NxCapsuleShape* capShape = shape->isCapsule(); + if (fHuman) + { + kineHeight += kPhysHeightCorrection; + capShape->setLocalPosition(NxVec3(0.0f, (kPhysHeightCorrection / 2.0f), 0.0f)); + } + capShape->setDimensions(kineRadius, kineHeight); + } + else + { + // Use dynamic actor for the character controller + NxCapsuleShapeDesc capDesc; + capDesc.materialIndex = plSimulationMgr::GetInstance()->GetMaterialIdx(scene, 0.0f, 0.0f); + capDesc.radius = fRadius + kCCTSkinWidth; + capDesc.height = fHeight + kPhysHeightCorrection; + + NxBodyDesc bodyDesc; + bodyDesc.mass = kAvatarMass; + bodyDesc.flags = NX_BF_DISABLE_GRAVITY; + bodyDesc.flags |= NX_BF_FROZEN_ROT; + + if (fEnabled) + capDesc.group = plSimDefs::kGroupAvatar; + else + { + bodyDesc.flags |= NX_BF_KINEMATIC; + capDesc.group = plSimDefs::kGroupAvatarKinematic; + } + + NxActorDesc actorDesc; + actorDesc.shapes.pushBack(&capDesc); + actorDesc.body = &bodyDesc; + actorDesc.group = 2; + + actorDesc.globalPose.M.rotX(NxHalfPiF32); + actorDesc.globalPose.t.x = pos.fX; + actorDesc.globalPose.t.y = pos.fY; + actorDesc.globalPose.t.z = pos.fZ + kPhysZOffset; + + fActor = scene->createActor(actorDesc); + } + + fSeeking = false; + + // Create proxy for the debug display + /* FIXME + // the avatar proxy doesn't seem to work... not sure why? + hsColorRGBA physColor; + float opac = 1.0f; + + // local avatar is light purple and transparent + physColor.Set(.2f, .1f, .2f, 1.f); + opac = 0.8f; + + fProxyGen = new plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); + fProxyGen->Init(this); + */ } -const hsVector3& plPXPhysicalControllerCore::GetLinearVelocity() +void plPXPhysicalControllerCore::IDeleteController() { - if(BehavingLikeAnAnimatedPhysical()) - return fLinearVelocity; + if (fKinematicCCT) + { + gControllerMgr.releaseController(*fController); + fController = nil; + } else { - fLinearVelocity = plPXConvert::Vector(fKinematicActor->getLinearVelocity()); - return fLinearVelocity; + NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + scene->releaseActor(*fActor); } + + fActor = nil; + plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); } -// Make a visible object that can be viewed by users for debugging purposes. -plDrawableSpans* plPXPhysicalControllerCore::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +void plPXPhysicalControllerCore::IDispatchQueuedMsgs() { - plDrawableSpans* myDraw = addTo; - bool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); - float radius = fRadius; - myDraw = plDrawableGenerator::GenerateSphericalDrawable(fLocalPosition, radius, - mat, fLastGlobalLoc, blended, - nil, &idx, myDraw); + int numMsgs = fQueuedCollideMsgs.size(); + if (numMsgs) + { + plSimulationMgr* simMgr = plSimulationMgr::GetInstance(); + for (int i = 0; i < numMsgs; ++i) + simMgr->AddCollisionMsg(fQueuedCollideMsgs[i]); -/* - plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); - if (so) + fQueuedCollideMsgs.clear(); + } +} +void plPXPhysicalControllerCore::IProcessDynamicHits() +{ + int numHits = fDynamicHits.size(); + if (numHits) { - bool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); + plPXPhysical* phys; + plSimulationMgr* simMgr = plSimulationMgr::GetInstance(); + for (int i = 0; i < numHits; ++i) + { + phys = fDynamicHits[i]; - myDraw = plDrawableGenerator::GenerateConicalDrawable(fRadius*10, fHeight*10, - mat, so->GetLocalToWorld(), blended, - nil, &idx, myDraw); + // If this is the local avatar, we need to take ownership of this dynamic if we haven't already + if (fLOSDB == plSimDefs::kLOSDBLocalAvatar && !phys->IsLocallyOwned() && !phys->GetProperty(plSimulationInterface::kNoOwnershipChange)) + { + plSynchedObject* obj = plSynchedObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); + obj->SetNetGroupConstant(plNetGroup::kNetGroupLocalPhysicals); + + plSetNetGroupIDMsg* setNetGroupID = new plSetNetGroupIDMsg; + setNetGroupID->fId = plNetGroup::kNetGroupRemotePhysicals; + setNetGroupID->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce); + setNetGroupID->SetBCastFlag(plMessage::kLocalPropagate, false); + setNetGroupID->Send(obj->GetKey()); + } + + phys->ApplyHitForce(); + simMgr->ConsiderSynch(phys, nil); + } + fDynamicHits.clear(); } -*/ - 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_t scrnWidth, scrnHeight; + uint32_t scrnWidth, scrnHeight; debugTxt.GetScreenSize( &scrnWidth, &scrnHeight ); int y = 10; @@ -1185,10 +872,11 @@ void plPXPhysicalControllerCore::IDrawDebugDisplay() char *overlapStr = fDbgCollisionInfo[i].fOverlap ? "yes" : "no"; float angle = hsRadiansToDegrees(acos(normal * hsVector3(0, 0, 1))); sprintf(strBuf, " Obj: %s, Normal: (%.2f, %.2f, %.2f), Angle(%.1f), Overlap(%3s)", - fDbgCollisionInfo[i].fSO->GetKeyName().c_str(), + fDbgCollisionInfo[i].fSO->GetKeyName(), normal.fX, normal.fY, normal.fZ, angle, overlapStr); debugTxt.DrawString(x, y, strBuf); y += lineHeight; } } #endif PLASMA_EXTERNAL_RELEASE + diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h index a2dad48e..70ff44d1 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h @@ -40,22 +40,19 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com *==LICENSE==*/ #include "plAvatar/plPhysicalControllerCore.h" -#include "hsQuat.h" -#define PHYSX_ONLY_TRIGGER_FROM_KINEMATIC 1 -class NxController; class NxCapsuleController; class NxActor; -class plCoordinateInterface; +class NxCapsule; +class PXControllerHitReport; class plPhysicalProxy; class plDrawableSpans; class hsGMaterial; -class NxCapsule; class plSceneObject; -class PXControllerHitReportWalk; +class plPXPhysical; class plCollideMsg; -#ifndef PLASMA_EXTERNAL_RELEASE +#ifndef PLASMA_EXTERNAL_RELEASE class plDbgCollisionInfo { public: @@ -64,97 +61,113 @@ public: bool fOverlap; }; #endif // PLASMA_EXTERNAL_RELEASE + class plPXPhysicalControllerCore: public plPhysicalControllerCore { - friend class PXControllerHitReportWalk; public: - plPXPhysicalControllerCore(plKey ownerSO, float height, float radius); + plPXPhysicalControllerCore(plKey ownerSO, float height, float radius, bool human); ~plPXPhysicalControllerCore(); - //should actually be a 3 vector but everywhere else it is assumed to be just around Z - - inline virtual void Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults); - // A disabled avatar doesn't move or accumulate air time if he's off the ground. + + // An ArmatureMod has its own idea about when physics should be enabled/disabled. + // Use plArmatureModBase::EnablePhysics() instead. virtual void Enable(bool enable); - - virtual void SetSubworld(plKey world) ; - virtual const plCoordinateInterface* GetSubworldCI() const ; + + // Subworld + virtual void SetSubworld(plKey world); + // For the avatar SDL only virtual void GetState(hsPoint3& pos, float& zRot); virtual void SetState(const hsPoint3& pos, float zRot); - // kinematic stuff .... should be just for when playing a behavior... - virtual void Kinematic(bool state); - virtual bool IsKinematic(); - virtual void GetKinematicPosition(hsPoint3& pos); - virtual const hsMatrix44& GetPrevSubworldW2L(){ return fPrevSubworldW2L; } - //when seeking no longer want to interact with exclusion regions - virtual void GetWorldSpaceCapsule(NxCapsule& cap) const; - static void RebuildCache(); - virtual const hsMatrix44& GetLastGlobalLoc(){return fLastGlobalLoc;} - virtual void SetKinematicLoc(const hsMatrix44& l2w){ISetKinematicLoc(l2w);} - virtual void SetGlobalLoc(const hsMatrix44& l2w){ISetGlobalLoc(l2w);} - virtual void HandleEnableChanged(); - virtual void HandleKinematicChanged(); - virtual void HandleKinematicEnableNextUpdate(); - virtual void GetPositionSim(hsPoint3& pos){IGetPositionSim(pos);} - virtual void MoveKinematicToController(hsPoint3& pos); - virtual const hsPoint3& GetLocalPosition(){return fLocalPosition;} - virtual void SetControllerDimensions(float radius, float height); + + // Movement strategy + virtual void SetMovementStrategy(plMovementStrategy* strategy); + + // Global location + virtual void SetGlobalLoc(const hsMatrix44& l2w); + + // Local Sim Position + virtual void GetPositionSim(hsPoint3& pos); + + // Move kinematic controller + virtual void Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults); + + // Set linear velocity on dynamic controller + virtual void SetLinearVelocitySim(const hsVector3& linearVel); + + // Sweep the controller path from startPos through endPos + virtual int SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, bool vsDynamics, + bool vsStatics, uint32_t& vsSimGroups, std::vector& hits); + + // any clean up for the controller should go here virtual void LeaveAge(); - virtual void UpdateControllerAndPhysicalRep(); + + // Capsule + void GetWorldSpaceCapsule(NxCapsule& cap) const; + + // Create Proxy for debug rendering + plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); + + // Dynamic hits + void AddDynamicHit(plPXPhysical* phys); ////////////////////////////////////////// //Static Helper Functions //////////////////////////////////////// - // Used by the LOS mgr to find the controller for an actor it hit - static plPXPhysicalControllerCore* GetController(NxActor& actor, bool* isController); - // test to see if there are any controllers (i.e. avatars) in this subworld + + // Call pre-sim to apply movement to controllers + static void Apply(float delSecs); + + // Call post-sim to update controllers + static void Update(int numSubSteps, float alpha); + + // Update controllers when not performing a physics step + static void UpdateNonPhysical(float alpha); + + // Rebuild the controller cache, required when a static actor in the scene has changed. + static void RebuildCache(); + + // Returns the plPXPhysicalControllerCore associated with the given NxActor + static plPXPhysicalControllerCore* GetController(NxActor& actor); + + // Subworld controller queries static bool AnyControllersInThisWorld(plKey world); - static int NumControllers(); - static int GetControllersInThisSubWorld(plKey world, int maxToReturn, - plPXPhysicalControllerCore** bufferout); static int GetNumberOfControllersInThisSubWorld(plKey world); - static void UpdatePrestep(float delSecs); - static void UpdatePoststep(float delSecs); - static void UpdatePostSimStep(float delSecs); - virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); -#ifndef PLASMA_EXTERNAL_RELEASE - static bool fDebugDisplay; -#endif // PLASMA_EXTERNAL_RELEASE + static int GetControllersInThisSubWorld(plKey world, int maxToReturn, plPXPhysicalControllerCore** bufferout); + + // Controller count + static int NumControllers(); static void SetMaxNumberOfControllers(int max) { fPXControllersMax = max; } static int fPXControllersMax; - virtual int SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, bool vsDynamics, bool vsStatics, uint32_t& vsSimGroups, std::multiset< plControllerSweepRecord >& WhatWasHitOut); - virtual void BehaveLikeAnimatedPhysical(bool actLikeAnAnimatedPhys); - virtual bool BehavingLikeAnAnimatedPhysical(); - virtual const hsVector3& GetLinearVelocity(); - virtual void SetLinearVelocity(const hsVector3& linearVel); - //should actually be a 3 vector but everywhere else it is assumed to be just around Z - virtual void SetAngularVelocity(const float angvel); - virtual void SetVelocities(const hsVector3& linearVel, float angVel); +#ifndef PLASMA_EXTERNAL_RELEASE + static bool fDebugDisplay; +#endif protected: friend class PXControllerHitReport; - static plPXPhysicalControllerCore* FindController(NxController* controller); - void ISetGlobalLoc(const hsMatrix44& l2w); - void IMatchKinematicToController(); - void IMatchControllerToKinematic(); - void ISetKinematicLoc(const hsMatrix44& l2w); - void IGetPositionSim(hsPoint3& pos) const; - void ICreateController(); - void IDeleteController(); - void IInformDetectors(bool entering,bool deferUntilNextSim); + + virtual void IHandleEnableChanged(); + + void IInformDetectors(bool entering); + void ICreateController(const hsPoint3& pos); - NxActor* fKinematicActor; - NxCapsuleController* fController; + void IDeleteController(); + + void IDispatchQueuedMsgs(); + void IProcessDynamicHits(); + #ifndef PLASMA_EXTERNAL_RELEASE - hsTArray fDbgCollisionInfo; void IDrawDebugDisplay(); + hsTArray fDbgCollisionInfo; #endif - void IHandleResize(); - float fPreferedRadius; - float fPreferedHeight; - // The global position and rotation of the avatar last time we set it (so we - // can detect if someone else moves him) - plPhysicalProxy* fProxyGen; - bool fBehavingLikeAnimatedPhys; + + std::vector fQueuedCollideMsgs; + std::vector fDynamicHits; + + NxCapsuleController* fController; + NxActor* fActor; + + plPhysicalProxy* fProxyGen; + bool fKinematicCCT; + bool fHuman; }; diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp index 5a4b768e..629163d6 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp @@ -70,139 +70,43 @@ class SensorReport : public NxUserTriggerReport { virtual void onTrigger(NxShape& triggerShape, NxShape& otherShape, NxTriggerFlag status) { - // Get our trigger physical. This should definitely have a plPXPhysical - plPXPhysical* triggerPhys = (plPXPhysical*)triggerShape.getActor().userData; - bool doReport = false; - - // Get the triggerer. This may be an avatar, which doesn't have a - // plPXPhysical, so we have to extract the necessary info. plKey otherKey = nil; - hsPoint3 otherPos = plPXConvert::Point(otherShape.getGlobalPosition()); + bool doReport = false; - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("-->%s %s (status=%x) other@(%f,%f,%f)",triggerPhys->GetObjectKey()->GetName().c_str(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit",status,otherPos.fX,otherPos.fY,otherPos.fZ); + // Get our trigger physical. This should definitely have a plPXPhysical + plPXPhysical* triggerPhys = (plPXPhysical*)triggerShape.getActor().userData; + // Get the triggerer. If it doesn't have a plPXPhyscial, it's an avatar plPXPhysical* otherPhys = (plPXPhysical*)otherShape.getActor().userData; if (otherPhys) { otherKey = otherPhys->GetObjectKey(); doReport = triggerPhys->DoReportOn((plSimDefs::Group)otherPhys->GetGroup()); - if (!doReport) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s :failed group. US=%x OTHER=(%s)%x",triggerPhys->GetObjectKey()->GetName().c_str(),triggerPhys->GetGroup(),otherPhys->GetObjectKey()->GetName().c_str(),otherPhys->GetGroup()); - } } else { - bool isController; - plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(otherShape.getActor(),&isController); + plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(otherShape.getActor()); if (controller) { - if (isController) - { -#ifdef PHYSX_ONLY_TRIGGER_FROM_KINEMATIC - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s : ignoring controller events.",triggerPhys->GetObjectKey()->GetName().c_str()); - return; -#else // else if trigger on both controller and kinematic - // only suppress controller collision 'enters' when disabled but let 'exits' continue - // ...this is because there are detector regions that are on the edge on ladders that the exit gets missed. - if ( ( !controller->IsEnabled() /*&& (status & NX_TRIGGER_ON_ENTER)*/ ) || controller->IsKinematic() ) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s : controller is not enabled.",triggerPhys->GetObjectKey()->GetName().c_str()); - return; - } -#endif // PHYSX_ONLY_TRIGGER_FROM_KINEMATIC - } -#ifndef PHYSX_ONLY_TRIGGER_FROM_KINEMATIC // if triggering only kinematics, then all should trigger - else - { - // only suppress kinematic collision 'enters' when disabled but let 'exits' continue - // ...this is because there are detector regions that are on the edge on ladders that the exit gets missed. - if ( !controller->IsKinematic() /*&& (status & NX_TRIGGER_ON_ENTER) */ ) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s : kinematic is not enabled.",triggerPhys->GetObjectKey()->GetName().c_str()); - return; - } - } -#endif // PHYSX_ONLY_TRIGGER_FROM_KINEMATIC otherKey = controller->GetOwner(); doReport = triggerPhys->DoReportOn(plSimDefs::kGroupAvatar); - if (plSimulationMgr::fExtraProfile ) - { - if (!doReport) - { - DetectorLogRed("<--Kill collision %s :failed group. US=%x OTHER=(NotAvatar)",triggerPhys->GetObjectKey()->GetName().c_str(),triggerPhys->GetGroup()); - } - else - { - hsPoint3 avpos; - controller->GetPositionSim(avpos); - DetectorLogRed("-->Avatar at (%f,%f,%f)",avpos.fX,avpos.fY,avpos.fZ); - } - } } } if (doReport) { -#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND - if ( triggerPhys->DoDetectorHullWorkaround() ) + if (status & NX_TRIGGER_ON_ENTER) { - if (status & NX_TRIGGER_ON_ENTER && triggerPhys->Should_I_Trigger(status & NX_TRIGGER_ON_ENTER, otherPos) ) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("-->Send Collision (CH) %s %s",triggerPhys->GetObjectKey()->GetName().c_str(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); - plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, true); - } - else if (status & NX_TRIGGER_ON_ENTER) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s :failed Should I trigger",triggerPhys->GetObjectKey()->GetName().c_str()); - } - if (status & NX_TRIGGER_ON_LEAVE && triggerPhys->Should_I_Trigger(status & NX_TRIGGER_ON_ENTER, otherPos) ) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("-->Send Collision (CH) %s %s",triggerPhys->GetObjectKey()->GetName().c_str(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); - plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, false); - } - else if (status & NX_TRIGGER_ON_LEAVE) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s :failed Should I trigger",triggerPhys->GetObjectKey()->GetName().c_str()); - } - if (!(status & NX_TRIGGER_ON_ENTER) && !(status & NX_TRIGGER_ON_LEAVE) ) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s :failed event(CH)",triggerPhys->GetObjectKey()->GetName().c_str()); - } + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("-->Send Collision %s enter",triggerPhys->GetObjectKey()->GetName().c_str()); + plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, true); } - else + else if (status & NX_TRIGGER_ON_LEAVE) { -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND - if (status & NX_TRIGGER_ON_ENTER) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("-->Send Collision %s %s",triggerPhys->GetObjectKey()->GetName().c_str(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); - plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, true); - } - if (status & NX_TRIGGER_ON_LEAVE) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("-->Send Collision %s %s",triggerPhys->GetObjectKey()->GetName().c_str(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); - plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, false); - } - if (!(status & NX_TRIGGER_ON_ENTER) && !(status & NX_TRIGGER_ON_LEAVE) ) - { - if (plSimulationMgr::fExtraProfile) - DetectorLogRed("<--Kill collision %s :failed event",triggerPhys->GetObjectKey()->GetName().c_str()); - } -#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("-->Send Collision %s exit",triggerPhys->GetObjectKey()->GetName().c_str()); + plSimulationMgr::GetInstance()->AddCollisionMsg(triggerPhys->GetObjectKey(), otherKey, false); } -#endif // USE_PHYSX_CONVEXHULL_WORKAROUND } } } gSensorReport; @@ -289,8 +193,8 @@ class ErrorStream : public NxUserOutputStream // ///////////////////////////////////////////////////////////////// -#define kDefaultMaxDelta 0.1 // if the step is greater than .1 seconds, clamp to that -#define kDefaultStepSize 1.f / 60.f // default simulation freqency is 60hz +#define kDefaultMaxDelta (0.15) // if the step is greater than .15 seconds, clamp to that +#define kDefaultStepSize (1.f / 60.f) // default simulation freqency is 60hz ///////////////////////////////////////////////////////////////// // @@ -378,6 +282,8 @@ plSimulationMgr* plSimulationMgr::GetInstance() plSimulationMgr::plSimulationMgr() : fSuspended(true) + , fAccumulator(0.0f) + , fStepCount(0) , fLOSDispatch(new plLOSDispatch()) , fSoundMgr(new plPhysicsSoundMgr) , fLog(nil) @@ -470,6 +376,7 @@ NxScene* plSimulationMgr::GetScene(plKey world) scene->setGroupCollisionFlag(i, plSimDefs::kGroupDynamicBlocker, false); scene->setGroupCollisionFlag(i, plSimDefs::kGroupLOSOnly, false); scene->setGroupCollisionFlag(plSimDefs::kGroupLOSOnly, i, false); + scene->setGroupCollisionFlag(i, plSimDefs::kGroupAvatarKinematic, false); } scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupAvatar, false); scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupAvatarBlocker, true); @@ -477,7 +384,11 @@ NxScene* plSimulationMgr::GetScene(plKey world) scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupStatic, true); scene->setGroupCollisionFlag( plSimDefs::kGroupStatic, plSimDefs::kGroupAvatar, true); scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupDynamic, true); - + + // Kinematically controlled avatars interact with detectors and dynamics + scene->setGroupCollisionFlag(plSimDefs::kGroupAvatarKinematic, plSimDefs::kGroupDetector, true); + scene->setGroupCollisionFlag(plSimDefs::kGroupAvatarKinematic, plSimDefs::kGroupDynamic, true); + // The dynamics are in actor group 1, everything else is in 0. Request // a callback for whenever a dynamic touches something. scene->setActorGroupPairFlags(0, 1, NX_NOTIFY_ON_TOUCH); @@ -507,86 +418,6 @@ void plSimulationMgr::ReleaseScene(plKey world) } } -void plSimulationMgr::ISendCollisionMsg(plKey receiver, plKey hitter, bool entering) -{ - DetectorLogYellow("Collision: %s is inside %s. Sending an %s msg", hitter ? hitter->GetName().c_str() : "(nil)", - receiver->GetName().c_str(), entering ? "'enter'" : "'exit'"); - plCollideMsg* msg = new plCollideMsg; - msg->fOtherKey = hitter; - msg->fEntering = entering; - msg->AddReceiver(receiver); - msg->Send(); -} - -void plSimulationMgr::UpdateDetectorsInScene(plKey world, plKey avatar, hsPoint3& pos, bool entering) -{ - // search thru the actors in a scene looking for convex hull detectors and see if the avatar is inside it - // ... and then send appropiate collision message if needed - NxScene* scene = GetScene(world); - plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->ObjectIsLoaded()); - const plCoordinateInterface* ci = avObj->GetCoordinateInterface(); - hsPoint3 soPos = ci->GetWorldPos(); - if (scene) - { - uint32_t numActors = scene->getNbActors(); - NxActor** actors = scene->getActors(); - - for (int i = 0; i < numActors; i++) - { - plPXPhysical* physical = (plPXPhysical*)actors[i]->userData; - if (physical && physical->DoDetectorHullWorkaround()) - { - if ( physical->IsObjectInsideHull(pos) ) - { - physical->SetInsideConvexHull(entering); - // we are entering this world... say we entered this detector - ISendCollisionMsg(physical->GetObjectKey(), avatar, entering); - } - } - } - } -} - -void plSimulationMgr::UpdateAvatarInDetector(plKey world, plPXPhysical* detector) -{ - // search thru the actors in a scene looking for avatars that might be in the newly enabled detector region - // ... and then send appropiate collision message if needed - if ( detector->DoDetectorHullWorkaround() ) - { - NxScene* scene = GetScene(world); - if (scene) - { - uint32_t numActors = scene->getNbActors(); - NxActor** actors = scene->getActors(); - - for (int i = 0; i < numActors; i++) - { - if ( actors[i]->userData == nil ) - { - // we go a controller - bool isController; - plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(*actors[i],&isController); - if (controller && controller->IsEnabled()) - { - plKey avatar = controller->GetOwner(); - plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->ObjectIsLoaded()); - const plCoordinateInterface* ci; - if ( avObj && ( ci = avObj->GetCoordinateInterface() ) ) - { - if ( detector->IsObjectInsideHull(ci->GetWorldPos()) ) - { - detector->SetInsideConvexHull(true); - // we are entering this world... say we entered this detector - ISendCollisionMsg(detector->GetObjectKey(), avatar, true); - } - } - } - } - } - } - } -} - void plSimulationMgr::AddCollisionMsg(plKey hitee, plKey hitter, bool enter) { // First, make sure we have no dupes @@ -614,20 +445,41 @@ void plSimulationMgr::AddCollisionMsg(plKey hitee, plKey hitter, bool enter) pMsg->fEntering = enter; fCollideMsgs.push_back(pMsg); } +void plSimulationMgr::AddCollisionMsg(plCollideMsg* msg) +{ + fCollideMsgs.push_back(msg); +} void plSimulationMgr::Advance(float delSecs) { if (fSuspended) return; - plProfile_IncCount(StepLen, (int)(delSecs*1000)); + fAccumulator += delSecs; + if (fAccumulator < kDefaultStepSize) + { + // Not enough time has passed to perform a substep. + plPXPhysicalControllerCore::UpdateNonPhysical(fAccumulator / kDefaultStepSize); + return; + } + else if (fAccumulator > kDefaultMaxDelta) + { + if (fExtraProfile) + Log("Step clamped from %f to limit of %f", fAccumulator, kDefaultMaxDelta); + fAccumulator = kDefaultMaxDelta; + } -#ifndef PLASMA_EXTERNAL_RELASE - uint32_t stepTime = hsTimer::GetPrecTickCount(); -#endif + ++fStepCount; + + // Perform as many whole substeps as possible saving the remainder in our accumulator. + int numSubSteps = (int)(fAccumulator / kDefaultStepSize + 0.000001f); + float delta = numSubSteps * kDefaultStepSize; + fAccumulator -= delta; + + plProfile_IncCount(StepLen, (int)(delta*1000)); plProfile_BeginTiming(Step); - plPXPhysicalControllerCore::UpdatePrestep(delSecs); - plPXPhysicalControllerCore::UpdatePoststep( delSecs); + + plPXPhysicalControllerCore::Apply(delta); for (SceneMap::iterator it = fScenes.begin(); it != fScenes.end(); it++) { @@ -642,12 +494,13 @@ void plSimulationMgr::Advance(float delSecs) } if (do_advance) { - scene->simulate(delSecs); + scene->simulate(delta); scene->flushStream(); scene->fetchResults(NX_RIGID_BODY_FINISHED, true); } } - plPXPhysicalControllerCore::UpdatePostSimStep(delSecs); + + plPXPhysicalControllerCore::Update(numSubSteps, fAccumulator / kDefaultStepSize); plProfile_EndTiming(Step); #ifndef PLASMA_EXTERNAL_RELEASE @@ -730,9 +583,6 @@ void plSimulationMgr::ISendUpdates() plPXPhysical* physical = (plPXPhysical*)actors[i]->userData; if (physical) { - // apply any hit forces - physical->ApplyHitForce(); - if (physical->GetSceneNode()) { physical->SendNewLocation(); diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h index 2cf07d90..d8a38b50 100644 --- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h +++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h @@ -99,12 +99,11 @@ public: int GetMaterialIdx(NxScene* scene, float friction, float restitution); - // PHYSX FIXME - walk thru all the convex hull detector regions to see if we are in any... we're either coming or going - void UpdateDetectorsInScene(plKey world, plKey avatar, hsPoint3& pos, bool entering); - void UpdateAvatarInDetector(plKey world, plPXPhysical* detector); - + uint32_t GetStepCount() const { return fStepCount; } + //Fix to Move collision messages and their handling out of the simulation step void AddCollisionMsg(plKey hitee, plKey hitter, bool entering); + void AddCollisionMsg(plCollideMsg* msg); #ifndef PLASMA_EXTERNAL_RELEASE static bool fDisplayAwakeActors; @@ -121,9 +120,6 @@ protected: // Walk through the synchronization requests and send them as appropriate. void IProcessSynchs(); - // PHYSX FIXME send a collision message - should only be used with UpdateDetectorsInScene - void ISendCollisionMsg(plKey receiver, plKey hitter, bool entering); - NxPhysicsSDK* fSDK; plPhysicsSoundMgr* fSoundMgr; @@ -144,6 +140,9 @@ protected: // but nothing will move. bool fSuspended; + float fAccumulator; + uint32_t fStepCount; + // A utility class to keep track of a request for a physical synchronization. // These requests must pass a certain criteria (see the code for the latest) // before they are actually either sent over the network or rejected. diff --git a/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp b/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp index 43cfef3f..ca14aa0b 100644 --- a/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp +++ b/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp @@ -39,7 +39,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ -#include "plAvatar/plAvCallbackAction.h" #include "HeadSpin.h" #include "plCollisionDetector.h" @@ -65,11 +64,15 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvatar/plAvatarMgr.h" #include "plAvatar/plAvBrainHuman.h" #include "plAvatar/plAvBrainDrive.h" +#include "plAvatar/plPhysicalControllerCore.h" #include "plModifier/plDetectorLog.h" -#define USE_PHYSX_MULTIPLE_CAMREGION_ENTER 1 -#define USE_PHYSX_COLLISION_FLUTTER_WORKAROUND 1 +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND +#include "plPhysX/plSimulationMgr.h" +#endif + + plArmatureMod* plCollisionDetector::IGetAvatarModifier(plKey key) { @@ -240,58 +243,19 @@ plCameraRegionDetector::~plCameraRegionDetector() hsRefCnt_SafeUnRef(*it); } -void plCameraRegionDetector::ITrigger(plKey hitter, bool entering, bool immediate) +void plCameraRegionDetector::ISendTriggerMsg() { - if (fSavingSendMsg) - DetectorLogRed("%s: Stale messages on ITrigger. This should never happen!", GetKeyName().c_str()); - if (fIsInside && entering) - DetectorLogRed("%s: Duplicate enter! Did we miss an exit?", GetKeyName().c_str()); - else if (!fIsInside && !entering) - DetectorLogRed("%s: Duplicate exit! Did we miss an enter?", GetKeyName().c_str()); - - fSavingSendMsg = true; - fSavedMsgEnterFlag = entering; - if (entering) - { - DetectorLog("%s: Saving camera Entering volume - Evals=%d", GetKeyName().c_str(),fNumEvals); - fLastEnterEval = fNumEvals; - } - else - { - DetectorLog("%s: Saving camera Exiting volume - Evals=%d", GetKeyName().c_str(),fNumEvals); - fLastExitEval = fNumEvals; - } - - if (immediate) - ISendSavedTriggerMsgs(); -} - -void plCameraRegionDetector::ISendSavedTriggerMsgs() -{ - if (fSavingSendMsg) + for (plCameraMsgVec::iterator it = fMessages.begin(); it != fMessages.end(); ++it) { - for (size_t i = 0; i < fMessages.size(); ++i) - { - hsRefCnt_SafeRef(fMessages[i]); - if (fSavedMsgEnterFlag) - { - fMessages[i]->SetCmd(plCameraMsg::kEntering); - DetectorLog("Entering cameraRegion: %s - Evals=%d -msg %d of %d\n", GetKeyName().c_str(),fNumEvals,i+1,fMessages.size()); - fIsInside = true; - } - else - { - fMessages[i]->ClearCmd(plCameraMsg::kEntering); - DetectorLog("Exiting cameraRegion: %s - Evals=%d -msg %d of %d\n", GetKeyName().c_str(),fNumEvals,i+1,fMessages.size()); - fIsInside = false; - } - plgDispatch::MsgSend(fMessages[i]); - } + plCameraMsg* msg = *it; + if (fIsInside) + msg->SetCmd(plCameraMsg::kEntering); + else + msg->ClearCmd(plCameraMsg::kEntering); + msg->SendAndKeep(); } - fSavingSendMsg = false; } - bool plCameraRegionDetector::MsgReceive(plMessage* msg) { plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); @@ -300,8 +264,16 @@ bool plCameraRegionDetector::MsgReceive(plMessage* msg) // camera collisions are only for the local player if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pCollMsg->fOtherKey) return true; - // Fall through to plObjectInVolumeDetector, which will register us for plEvalMsg - // and handle it for us. (Hint: See ISendSavedTriggerMsgs) + + if (!fWaitingForEval) + IRegisterForEval(); + + fEntering = (pCollMsg->fEntering != 0); + + #ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + fLastStep = plSimulationMgr::GetInstance()->GetStepCount(); +#endif + return true; } return plObjectInVolumeDetector::MsgReceive(msg); @@ -327,56 +299,72 @@ void plCameraRegionDetector::Write(hsStream* stream, hsResMgr* mgr) } +void plCameraRegionDetector::IHandleEval(plEvalMsg*) +{ +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + if (plSimulationMgr::GetInstance()->GetStepCount() - fLastStep > 1) + { +#endif + if (fIsInside != fEntering) + { + fIsInside = fEntering; + DetectorLog("%s CameraRegion: %s", fIsInside ? "Entering" : "Exiting", GetKeyName().c_str()); + ISendTriggerMsg(); + } + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + fWaitingForEval = false; +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + } +#endif +} + ///////////////////////////////// ///////////////////////////////// ///////////////////////////////// ///////////////////////////////// // object-in-volume detector -void plObjectInVolumeDetector::ITrigger(plKey hitter, bool entering, bool immediate) +void plObjectInVolumeDetector::ITrigger(plKey hitter, bool entering) { - hsRefCnt_SafeUnRef(fSavedActivatorMsg); - fSavedActivatorMsg = new plActivatorMsg; - fSavedActivatorMsg->AddReceivers(fReceivers); - - if (fProxyKey) - fSavedActivatorMsg->fHiteeObj = fProxyKey; - else - fSavedActivatorMsg->fHiteeObj = GetTarget()->GetKey(); - - fSavedActivatorMsg->fHitterObj = hitter; - fSavedActivatorMsg->SetSender(GetKey()); - - if (entering) +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + for (bookKeepingList::iterator it = fCollisionList.begin(); it != fCollisionList.end(); ++it) { - DetectorLog("%s: Saving Entering volume - Evals=%d", GetKeyName().c_str(), fNumEvals); - fSavedActivatorMsg->SetTriggerType(plActivatorMsg::kVolumeEnter); - fLastEnterEval = fNumEvals; - } - else - { - DetectorLog("%s: Saving Exiting volume - Evals=%d", GetKeyName().c_str(), fNumEvals); - fSavedActivatorMsg->SetTriggerType(plActivatorMsg::kVolumeExit); - fLastExitEval = fNumEvals; + plCollisionBookKeepingInfo* collisionInfo = *it; + if (collisionInfo->fHitter == hitter) + { + collisionInfo->fEntering = entering; + collisionInfo->fLastStep = plSimulationMgr::GetInstance()->GetStepCount(); + return; + } } +#endif - if (immediate) - ISendSavedTriggerMsgs(); + plCollisionBookKeepingInfo* collisionInfo = new plCollisionBookKeepingInfo(hitter, entering); + fCollisionList.push_back(collisionInfo); +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + collisionInfo->fLastStep = plSimulationMgr::GetInstance()->GetStepCount(); +#endif } -void plObjectInVolumeDetector::ISendSavedTriggerMsgs() +void plObjectInVolumeDetector::IRegisterForEval() { - if (fSavedActivatorMsg) - { - if (fSavedActivatorMsg->fTriggerType == plActivatorMsg::kVolumeEnter) - DetectorLog("%s: Sending Entering volume - Evals=%d", GetKeyName().c_str(), fNumEvals); - else - DetectorLog("%s: Sending Exiting volume - Evals=%d", GetKeyName().c_str(), fNumEvals); + fWaitingForEval = true; + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} - // we're saving the message to be dispatched later... - plgDispatch::MsgSend(fSavedActivatorMsg); - } - fSavedActivatorMsg = nil; +void plObjectInVolumeDetector::ISendTriggerMsg(plKey hitter, bool entering) +{ + plActivatorMsg* activatorMsg = new plActivatorMsg(); + activatorMsg->SetSender(GetKey()); + activatorMsg->AddReceivers(fReceivers); + activatorMsg->fHiteeObj = fProxyKey ? fProxyKey : GetTarget()->GetKey(); + activatorMsg->fHitterObj = hitter; + if (entering) + activatorMsg->SetTriggerType(plActivatorMsg::kVolumeEnter); + else + activatorMsg->SetTriggerType(plActivatorMsg::kVolumeExit); + + plgDispatch::MsgSend(activatorMsg); } bool plObjectInVolumeDetector::MsgReceive(plMessage* msg) @@ -387,18 +375,17 @@ bool plObjectInVolumeDetector::MsgReceive(plMessage* msg) // If the avatar is disabled (flying around), don't trigger if (IIsDisabledAvatar(pCollMsg->fOtherKey)) return false; + + if (!fWaitingForEval) + IRegisterForEval(); + ITrigger(pCollMsg->fOtherKey, (pCollMsg->fEntering != 0)); - plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); return true; } plEvalMsg* pEvalMsg = plEvalMsg::ConvertNoRef(msg); if (pEvalMsg) - { - fNumEvals++; - ISendSavedTriggerMsgs(); - plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); - } + IHandleEval(pEvalMsg); plPlayerPageMsg* pageMsg = plPlayerPageMsg::ConvertNoRef(msg); if (pageMsg && pageMsg->fUnload) @@ -409,6 +396,48 @@ bool plObjectInVolumeDetector::MsgReceive(plMessage* msg) return plCollisionDetector::MsgReceive(msg); } +void plObjectInVolumeDetector::IHandleEval(plEvalMsg*) +{ + bookKeepingList::iterator it = fCollisionList.begin(); + while (it != fCollisionList.end()) + { + plCollisionBookKeepingInfo* collisionInfo = *it; +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + if (plSimulationMgr::GetInstance()->GetStepCount() - collisionInfo->fLastStep > 1) + { +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + ResidentSet::iterator j = fCurrentResidents.find(collisionInfo->fHitter); + bool wasInside = j != fCurrentResidents.end(); + if (collisionInfo->fEntering != wasInside) + { + if (collisionInfo->fEntering) + { + fCurrentResidents.insert(collisionInfo->fHitter); + DetectorLog("%s: Sending Volume Enter ActivatorMsg", GetKeyName().c_str()); + ISendTriggerMsg(collisionInfo->fHitter, true); + } + else + { + fCurrentResidents.erase(j); + DetectorLog("%s: Sending Volume Exit ActivatorMsg", GetKeyName().c_str()); + ISendTriggerMsg(collisionInfo->fHitter, false); + } + } + + delete collisionInfo; +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + it = fCollisionList.erase(it); + } + else + { + ++it; + } +#else + ++it; +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + } +} + void plObjectInVolumeDetector::SetTarget(plSceneObject* so) { plCollisionDetector::SetTarget(so); @@ -475,7 +504,7 @@ void plObjectInVolumeAndFacingDetector::ICheckForTrigger() // And are we walking towards it? plArmatureBrain* abrain = armMod->FindBrainByClass(plAvBrainHuman::Index()); //armMod->GetCurrentBrain(); plAvBrainHuman* brain = plAvBrainHuman::ConvertNoRef(abrain); - if (brain && brain->IsMovingForward() && brain->fCallbackAction->IsOnGround()) + if (brain && brain->IsMovingForward() && brain->fWalkingStrategy->IsOnGround()) movingForward = true; } else @@ -485,13 +514,13 @@ void plObjectInVolumeAndFacingDetector::ICheckForTrigger() { DetectorLog("%s: Trigger InVolume&Facing", GetKeyName().c_str()); fTriggered = true; - ITrigger(avatar->GetKey(), true, true); + ISendTriggerMsg(avatar->GetKey(), true); } else if (!facing && fTriggered) { DetectorLog("%s: Untrigger InVolume&Facing", GetKeyName().c_str()); fTriggered = false; - ITrigger(avatar->GetKey(), false, true); + ISendTriggerMsg(avatar->GetKey(), false); } } } @@ -525,7 +554,7 @@ bool plObjectInVolumeAndFacingDetector::MsgReceive(plMessage* msg) if (fTriggered) { fTriggered = false; - ITrigger(plNetClientApp::GetInstance()->GetLocalPlayerKey(), false, true); + ISendTriggerMsg(plNetClientApp::GetInstance()->GetLocalPlayerKey(), false); } } @@ -645,11 +674,11 @@ bool plPanicLinkRegion::MsgReceive(plMessage* msg) { if (avMod->IsLinkedIn()) { - hsPoint3 kinPos; + hsPoint3 pos; if (avMod->GetController()) { - avMod->GetController()->GetKinematicPosition(kinPos); - DetectorLogSpecial("Avatar is panic linking. KinPos at %f,%f,%f and is %s",kinPos.fX,kinPos.fY,kinPos.fZ,avMod->GetController()->IsEnabled() ? "enabled" : "disabled"); + avMod->GetController()->GetPositionSim(pos); + DetectorLogSpecial("Avatar is panic linking. Position %f,%f,%f and is %s", pos.fX, pos.fY, pos.fZ, avMod->GetController()->IsEnabled() ? "enabled" : "disabled"); } avMod->PanicLink(fPlayLinkOutAnim); } else diff --git a/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h b/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h index d350341f..237aa692 100644 --- a/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h +++ b/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h @@ -53,6 +53,8 @@ class plArmatureMod; class plActivatorMsg; class plEvalMsg; +#define USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + class plCollisionDetector : public plDetectorModifier { protected: @@ -92,23 +94,37 @@ public: class plObjectInVolumeDetector : public plCollisionDetector { protected: - virtual void ITrigger(plKey hitter, bool entering, bool immediate=false); - virtual void ISendSavedTriggerMsgs(); - - plActivatorMsg* fSavedActivatorMsg; - uint32_t fNumEvals; - uint32_t fLastEnterEval; - uint32_t fLastExitEval; + class plCollisionBookKeepingInfo + { + public: + plCollisionBookKeepingInfo(const plKey& key, bool entering) + : fHitter(key), fEntering(entering) { } + + plKey fHitter; +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + uint32_t fLastStep; +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + bool fEntering; + }; + + void ITrigger(plKey hitter, bool entering); + void ISendTriggerMsg(plKey hitter, bool entering); + void IRegisterForEval(); + virtual void IHandleEval(plEvalMsg*); + bool fWaitingForEval; + + typedef std::list bookKeepingList; + bookKeepingList fCollisionList; + typedef std::set ResidentSet; + ResidentSet fCurrentResidents; public: plObjectInVolumeDetector() - : plCollisionDetector(), fSavedActivatorMsg(nil), fNumEvals(0), fLastEnterEval(0), fLastExitEval(0) - { } + : plCollisionDetector(), fWaitingForEval(false) { } plObjectInVolumeDetector(int8_t type) - : plCollisionDetector(type), fSavedActivatorMsg(nil), fNumEvals(0), fLastEnterEval(0), fLastExitEval(0) - { } + : plCollisionDetector(type), fWaitingForEval(false) { } virtual ~plObjectInVolumeDetector() { } @@ -159,16 +175,17 @@ protected: typedef std::vector plCameraMsgVec; plCameraMsgVec fMessages; - bool fIsInside; - bool fSavingSendMsg; - bool fSavedMsgEnterFlag; - - virtual void ITrigger(plKey hitter, bool entering, bool immediate=false); - virtual void ISendSavedTriggerMsgs(); +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + uint32_t fLastStep; +#endif + bool fIsInside; + bool fEntering; + + void ISendTriggerMsg(); + virtual void IHandleEval(plEvalMsg*); public: plCameraRegionDetector() - : plObjectInVolumeDetector(), fIsInside(false), fSavingSendMsg(false) - { } + : plObjectInVolumeDetector(), fIsInside(false) { } ~plCameraRegionDetector(); virtual bool MsgReceive(plMessage* msg); diff --git a/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h b/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h index 4ece4ccc..c22edd7f 100644 --- a/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h +++ b/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h @@ -68,27 +68,10 @@ namespace plSimDefs kGroupLOSOnly, //kExcludeRegion setting up so only blocks avatars and only when not in seek mode kGroupExcludeRegion, + // A kinematic avatar only interacts with dynamics and detectors + kGroupAvatarKinematic, // Just for error checking - kGroupMax, - }; - - /** A taxonomy of action types. Crucial for doing things like making sure you don't - do things like attach duplicate actions. */ - enum ActionType - { - kUnknownAction = 0x01, // don't know the type (probably forgot to override GetType()) - kUnknownZAction = 0x02, // unknown type of z-order action - kAntiGravityAction = 0x03, // an action that counters gravity exactly - kUprightAction = 0x04, // an action that keeps an object upright by apply force - kPhysAnimAction = 0x05, // an action that parses keyframed animation into physical information - kConstraint = 0x06, // a general constraint. - kCallbackAction = 0x07, // an action that just hands us the physics "tick" - kPseudoPhysAction = 0x08, // replacement for the physAnim - kAntiGravAction = 0x09, // makes things float in the air - kBasicGroundAction = 0x0a, // for your basic walkAroundOnGround corrections - kHorizontalFreeze = 0x0b, // Let's you fall vertically, but otherwise keeps you in place (generic brains) - - kMaxAction = 0xffff // force 16-bit + kGroupMax }; /** Different types of line-of-sight requests. */