diff --git a/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj b/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj
index 2e8abad4..df4d769d 100644
--- a/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj
+++ b/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj
@@ -169,6 +169,7 @@
+
@@ -178,6 +179,7 @@
+
diff --git a/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj.filters b/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj.filters
index 6c960a03..72dd754e 100644
--- a/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj.filters
+++ b/Build/VS2010/Plasma/PubUtilLib/plPhysX/plPhysX.vcxproj.filters
@@ -26,6 +26,9 @@
Source Files
+
+ Source Files
+
@@ -49,5 +52,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/Sources/Plasma/CoreLib/HeadSpin.h b/Sources/Plasma/CoreLib/HeadSpin.h
index bfa616f0..e8ca8edd 100644
--- a/Sources/Plasma/CoreLib/HeadSpin.h
+++ b/Sources/Plasma/CoreLib/HeadSpin.h
@@ -48,4 +48,20 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "hsTypes.h"
#include "hsMalloc.h"
+#ifdef HAVE_OVERRIDE
+# define HS_OVERRIDE override
+# define HS_FINAL final
+#else
+# define HS_OVERRIDE
+# define HS_FINAL
+#endif
+
+#ifdef HAVE_NOEXCEPT
+# define HS_NOEXCEPT noexcept
+# define HS_NOEXCEPT_IF(cond) noexcept(cond)
+#else
+# define HS_NOEXCEPT throw()
+# define HS_NOEXCEPT_IF(cond)
+#endif
+
#endif
diff --git a/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp b/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp
index ad359e5c..7cec3bff 100644
--- a/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp
+++ b/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp
@@ -49,8 +49,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "../pnNetCommon/plNetApp.h"
#include "../plAvatar/plArmatureMod.h"
#include "../pnSceneObject/plSceneObject.h"
-
-bool plVolumeSensorConditionalObject::makeBriceHappyVar = true;
+#include "../pnMessage/plPlayerPageMsg.h"
+#include "../../NucleusLib/inc/plgDispatch.h"
plObjectInBoxConditionalObject::plObjectInBoxConditionalObject() :
fCurrentTrigger(nil)
@@ -129,12 +129,52 @@ fTrigNum(-1),
fType(0),
fFirst(false),
fTriggered(false),
-fIgnoreExtraEnters(true)
+fFlags(kIgnoreExtraEnters)
{
SetSatisfied(true);
}
+void plVolumeSensorConditionalObject::IgnoreExtraEnters(bool ignore)
+{
+ if (ignore)
+ fFlags |= kIgnoreExtraEnters;
+ else
+ fFlags &= ~kIgnoreExtraEnters;
+}
+
+void plVolumeSensorConditionalObject::NoServerArbitration(bool noArbitration)
+{
+ if (noArbitration) {
+ plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey());
+ fFlags |= kNoServerArbitration;
+ } else {
+ plgDispatch::Dispatch()->UnRegisterForExactType(plPlayerPageMsg::Index(), GetKey());
+ fFlags &= ~kNoServerArbitration;
+ }
+}
+
+bool plVolumeSensorConditionalObject::IIsLocal(const plKey& key) const
+{
+ if (key == plNetClientApp::GetInstance()->GetLocalPlayerKey())
+ return true;
+
+ const plSceneObject* hitter = plSceneObject::ConvertNoRef(key->ObjectIsLoaded());
+ if (hitter) {
+ for (size_t i = 0; i < hitter->GetNumModifiers(); ++i) {
+ const plArmatureMod* am = plArmatureMod::ConvertNoRef(hitter->GetModifier(i));
+ if (am && !am->IsLocalAI())
+ return false;
+ }
+ if (hitter->IsLocallyOwned() != plSynchedObject::kYes)
+ return false;
+ }
+
+ // Yes, I know that we're saying YES for not loaded objects. This matches the previous behavior.
+ return true;
+}
+
+
hsBool plVolumeSensorConditionalObject::MsgReceive(plMessage* msg)
{
plActivatorMsg* pActivateMsg = plActivatorMsg::ConvertNoRef(msg);
@@ -144,55 +184,40 @@ hsBool plVolumeSensorConditionalObject::MsgReceive(plMessage* msg)
if (!fLogicMod->HasFlag(plLogicModBase::kRequestingTrigger))
fLogicMod->GetNotify()->ClearEvents();
+ // Track the hittee for the NoArbitration case so we can trigger the exit volume on link out
+ fHittee = pActivateMsg->fHiteeObj;
+
+ // Track the enters/exits on all clients
if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeEnter)
{
- int i;
- for (i = 0; i < fInside.Count(); i++)
- {
- if (fInside[i] == pActivateMsg->fHitterObj)
- {
- if (fIgnoreExtraEnters)
- return false; // this is the "correct" way to handle this situation
- break; // this is for those special situations where, due to some physics oddity, we need to allow the avatar to enter without exiting
- }
- }
- if (i == fInside.Count())
- fInside.Append(pActivateMsg->fHitterObj);
- if (makeBriceHappyVar)
- {
- plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() );
- if( pObj )
- {
- //need to check for human vs quabish type things in here
- int i;
- for( i = 0; i < pObj->GetNumModifiers(); i++ )
- {
- if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i)))
- {
- if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj)
- {
- plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i)));
- if((am->IsLocalAI())==nil)
- {
- return false;
- }
- }
- }
- }
- plSynchedObject* syncObj = (plSynchedObject*)pObj;
- if (syncObj->IsLocallyOwned() != plSynchedObject::kYes)
- {
- return false;
- }
- }
+ auto it = fInside.find(pActivateMsg->fHitterObj);
+ if (it != fInside.end() && fFlags & kIgnoreExtraEnters) {
+ // This is normally what we should do. You're already inside the region,
+ // so we don't care about a dupe enter. However, PhysX is weird, so sometimes
+ // we might want to allow dupe enters.
+ return false;
}
+ fInside.insert(pActivateMsg->fHitterObj);
+
+ // From here on out, we only care about local avatars
+ if (!IIsLocal(pActivateMsg->fHitterObj))
+ return false;
if (fType == kTypeEnter)
{
fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false);
- fLogicMod->RequestTrigger(false);
+ if ((fFlags & kNoServerArbitration))
+ {
+ if (Satisfied())
+ fLogicMod->Trigger(false);
+ }
+ else
+ {
+ fLogicMod->RequestTrigger(false);
+ }
}
else
+ if (fType == kTypeExit && !(fFlags & kNoServerArbitration))
{
fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false);
fLogicMod->RequestUnTrigger();
@@ -202,56 +227,54 @@ hsBool plVolumeSensorConditionalObject::MsgReceive(plMessage* msg)
else
if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeExit)
{
- for (int i = 0; i < fInside.Count(); i++)
- {
- if (fInside[i] == pActivateMsg->fHitterObj)
+ auto it = fInside.find(pActivateMsg->fHitterObj);
+ if (it == fInside.end())
+ return false;
+ fInside.erase(it);
+
+ // From here on out, we only care about local avatars
+ if (!IIsLocal(pActivateMsg->fHitterObj))
+ return false;
+
+ if (fType == kTypeExit) {
+ fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false);
+ if (fFlags & kNoServerArbitration)
{
- fInside.Remove(i);
- if (makeBriceHappyVar)
- {
- //need to check for human vs quabish type things in here
- plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() );
- if( pObj )
- {
- int i;
- for( i = 0; i < pObj->GetNumModifiers(); i++ )
- {
- if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i)))
- {
- if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj)
- {
- plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i)));
- if((am->IsLocalAI())==nil)
- {
- return false;
- }
- }
- }
- }
- plSynchedObject* syncObj = (plSynchedObject*)pObj;
- if (syncObj->IsLocallyOwned() != plSynchedObject::kYes)
- {
- return false;
- }
- }
- }
- if (fType == kTypeExit)
- {
- fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false);
- fLogicMod->RequestTrigger(false);
-
- }
- else
- {
- fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false);
- fLogicMod->RequestUnTrigger();
- }
- return false;
+ if (Satisfied())
+ fLogicMod->Trigger(false);
+ }
+ else
+ {
+ fLogicMod->RequestTrigger(false);
}
}
+ else if (fType == kTypeEnter && !(fFlags & kNoServerArbitration))
+ {
+ fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false);
+ fLogicMod->RequestUnTrigger();
+ }
+ return true;
}
+ return true;
+ }
- return false;
+ plPlayerPageMsg* page = plPlayerPageMsg::ConvertNoRef(msg);
+ if (page && page->fUnload) {
+ hsAssert(fFlags & kNoServerArbitration, "WTF -- should only get here if the VSCO skips arbitration!");
+
+ auto it = fInside.find(page->fPlayer);
+ if (it != fInside.end()) {
+ fInside.erase(it);
+ if (fHittee && fType == kTypeExit) {
+ const plSceneObject* hitteeSO = plSceneObject::ConvertNoRef(fHittee->ObjectIsLoaded());
+ if (hitteeSO && hitteeSO->IsLocallyOwned() == plSynchedObject::kYes) {
+ fLogicMod->GetNotify()->AddCollisionEvent(false, page->fPlayer, fHittee, false);
+ if (Satisfied())
+ fLogicMod->Trigger(false);
+ }
+ }
+ }
+ return true;
}
return plConditionalObject::MsgReceive(msg);
}
@@ -260,20 +283,20 @@ hsBool plVolumeSensorConditionalObject::Satisfied()
{
if (fType == kTypeExit && fFirst && !fTriggered)
{
- if (fInside.Count())
+ if (!fInside.empty())
fTriggered = true;
return true;
}
if (fTriggered)
{
- if (fInside.Count() == 0)
+ if (fInside.empty())
fTriggered = false;
return false;
}
if (fTrigNum == -1)
return true;
- if (fInside.Count() == fTrigNum)
+ if (fInside.size() == fTrigNum)
return true;
else
return false;
@@ -293,156 +316,11 @@ void plVolumeSensorConditionalObject::Write(hsStream* stream, hsResMgr* mgr)
stream->WriteSwap32(fType);
stream->WriteBool(fFirst);
}
-#include "../pnMessage/plPlayerPageMsg.h"
-#include "../../NucleusLib/inc/plgDispatch.h"
-hsBool plVolumeSensorConditionalObjectNoArbitration::MsgReceive(plMessage* msg)
-{
- plActivatorMsg* pActivateMsg = plActivatorMsg::ConvertNoRef(msg);
- if (pActivateMsg)
- {
- // single player hack
- if (!fLogicMod->HasFlag(plLogicModBase::kRequestingTrigger))
- fLogicMod->GetNotify()->ClearEvents();
- fHittee= pActivateMsg->fHiteeObj;
- if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeEnter)
- {
- int i;
- for (i = 0; i < fInside.Count(); i++)
- {
- if (fInside[i] == pActivateMsg->fHitterObj)
- {
- if (fIgnoreExtraEnters)
- return false; // this is the "correct" way to handle this situation
- break; // this is for those special situations where, due to some physics oddity, we need to allow the avatar to enter without exiting
- }
- }
- if (i == fInside.Count())
- fInside.Append(pActivateMsg->fHitterObj);
- if (makeBriceHappyVar)
- {
- plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() );
- if( pObj )
- {
- //need to check for human vs quabish type things in here
- int i;
- for( i = 0; i < pObj->GetNumModifiers(); i++ )
- {
- if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i)))
- {
- if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj)
- {
- plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i)));
- if((am->IsLocalAI())==nil)
- {
- return false;
- }
- }
- }
- }
- plSynchedObject* syncObj = (plSynchedObject*)pObj;
- if (syncObj->IsLocallyOwned() != plSynchedObject::kYes)
- {
- return false;
- }
- }
- }
- if (fType == kTypeEnter)
- {
- fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false);
- //fLogicMod->RequestTrigger(false);
-
- if (!Satisfied())
- return false;
-
- fLogicMod->Trigger(false);
- }
-
- return false;
- }
- else
- if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeExit)
- {
- for (int i = 0; i < fInside.Count(); i++)
- {
- if (fInside[i] == pActivateMsg->fHitterObj)
- {
- fInside.Remove(i);
- if (makeBriceHappyVar)
- {
- //need to check for human vs quabish type things in here
- plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() );
- if( pObj )
- {
- int i;
- for( i = 0; i < pObj->GetNumModifiers(); i++ )
- {
- if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i)))
- {
- if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj)
- {
- plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i)));
- if((am->IsLocalAI())==nil)
- {
- return false;
- }
- }
- }
- }
- plSynchedObject* syncObj = (plSynchedObject*)pObj;
- if (syncObj->IsLocallyOwned() != plSynchedObject::kYes)
- {
- return false;
- }
- }
- }
- if (fType == kTypeExit)
- {
- fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false);
- //fLogicMod->RequestTrigger(false);
- if (!Satisfied())
- return false;
-
- fLogicMod->Trigger(false);
- }
- return false;
- }
- }
- }
-
- return false;
- }
-
- plPlayerPageMsg* page = plPlayerPageMsg::ConvertNoRef(msg);
- if(page && page->fUnload)
- {
- for(int j= 0; j< fInside.Count(); j++)
- {
- if(fInside[j] == page->fPlayer)
- {//this is the one inside
- if(fHittee)
- {
- plSceneObject *so = plSceneObject::ConvertNoRef(fHittee->ObjectIsLoaded());
- if(so && so->IsLocallyOwned())
- {
- if (fType == kTypeExit)
- {
- fLogicMod->GetNotify()->AddCollisionEvent(false, page->fPlayer, fHittee, false);
- //fLogicMod->RequestTrigger(false);
- if (!Satisfied())
- return false;
- fLogicMod->Trigger(false);
- }
- }
- }
- fInside.Remove(j);
- }
- }
- }
- return plConditionalObject::MsgReceive(msg);
-}
void plVolumeSensorConditionalObjectNoArbitration::Read(hsStream* stream, hsResMgr* mgr)
{
plVolumeSensorConditionalObject::Read(stream, mgr);
- plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey());
-}
\ No newline at end of file
+
+ // We must have a valid fpKey before we do this, hence why this is not in the constructor
+ NoServerArbitration(true);
+}
diff --git a/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h b/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h
index 9eab8eb4..27c1accc 100644
--- a/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h
+++ b/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h
@@ -43,8 +43,10 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#ifndef plObjectInBoxConditionalObject_inc
#define plObjectInBoxConditionalObject_inc
+#include
#include "../../NucleusLib/pnModifier/plConditionalObject.h"
#include "hsTemplates.h"
+#include
class plKey;
@@ -77,16 +79,25 @@ class plVolumeSensorConditionalObject : public plConditionalObject
protected:
- hsTArray fInside;
+ std::set fInside;
int fTrigNum;
int fType;
hsBool fFirst;
hsBool fTriggered;
- hsBool fIgnoreExtraEnters;
-public:
- static bool makeBriceHappyVar;
+ plKey fHittee;
+ uint32_t fFlags;
+
+ enum
+ {
+ /** */
+ kIgnoreExtraEnters = (1<<0),
+ kNoServerArbitration = (1<<1),
+ };
+ bool IIsLocal(const plKey& key) const;
+
+public:
enum
{
@@ -99,33 +110,30 @@ public:
CLASSNAME_REGISTER( plVolumeSensorConditionalObject );
GETINTERFACE_ANY( plVolumeSensorConditionalObject, plConditionalObject );
- virtual hsBool MsgReceive(plMessage* msg);
+ hsBool MsgReceive(plMessage* msg);
- void Evaluate(){;}
+ void Evaluate() {}
void Reset() { SetSatisfied(false); }
- virtual hsBool Satisfied();
+ hsBool Satisfied() HS_OVERRIDE;
void SetType(int i) { fType = i; }
void SetTrigNum(int i) { fTrigNum = i; }
void SetFirst(hsBool b) { fFirst = b; }
- void IgnoreExtraEnters(hsBool ignore = true) {fIgnoreExtraEnters = ignore;}
+ void IgnoreExtraEnters(bool ignore = true);
+ void NoServerArbitration(bool noArbitration = true);
- virtual void Read(hsStream* stream, hsResMgr* mgr);
- virtual void Write(hsStream* stream, hsResMgr* mgr);
+ void Read(hsStream* stream, hsResMgr* mgr) HS_OVERRIDE;
+ void Write(hsStream* stream, hsResMgr* mgr) HS_OVERRIDE;
};
class plVolumeSensorConditionalObjectNoArbitration : public plVolumeSensorConditionalObject
{
public:
- plVolumeSensorConditionalObjectNoArbitration ():plVolumeSensorConditionalObject(){;}
- ~plVolumeSensorConditionalObjectNoArbitration (){;}
- CLASSNAME_REGISTER( plVolumeSensorConditionalObjectNoArbitration );
- GETINTERFACE_ANY( plVolumeSensorConditionalObjectNoArbitration, plConditionalObject );
- virtual hsBool MsgReceive(plMessage* msg);
- virtual void Read(hsStream* stream, hsResMgr* mgr);
-protected:
- plKey fHittee;
+ CLASSNAME_REGISTER(plVolumeSensorConditionalObjectNoArbitration);
+ GETINTERFACE_ANY( plVolumeSensorConditionalObjectNoArbitration, plVolumeSensorConditionalObject);
+
+ virtual void Read(hsStream* stream, hsResMgr* mgr) HS_OVERRIDE;
};
diff --git a/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp b/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp
index b6c7fb28..6a140eb4 100644
--- a/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp
+++ b/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp
@@ -447,6 +447,15 @@ PF_CONSOLE_BASE_CMD( SampleCmd3, "int, ...", "Sample command #3" )
}
}
+PF_CONSOLE_CMD(Logic,
+ DelayArbitration,
+ "int millis",
+ "Simulates network delay for LogicMod arbitration")
+{
+ int ms = params[0];
+ plLogicModBase::SetArbitrationDelay(ms);
+}
+
#endif // LIMIT_CONSOLE_COMMANDS
diff --git a/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp b/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp
index 4147e9cb..719af55c 100644
--- a/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp
+++ b/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp
@@ -457,11 +457,3 @@ PF_CONSOLE_CMD( Game, SetLocalClientAsAdmin, "bool enable", "Makes chat messages
plgDispatch::MsgSend( msg );
}
#endif
-
-#include "../pfConditional/plObjectInBoxConditionalObject.h"
-
-PF_CONSOLE_CMD( Game, BreakVolumeSensors, "bool break", "reverts to old broken volume sensor logic" )
-{
- bool b = params[ 0 ];
- plVolumeSensorConditionalObject::makeBriceHappyVar = !b;
-}
diff --git a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp
index 1aa2d8f3..4d94af91 100644
--- a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp
+++ b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp
@@ -993,3 +993,17 @@ void pySceneObject::VolumeSensorIgnoreExtraEnters(bool ignore)
}
}
}
+
+void pySceneObject::VolumeSensorNoArbitration(bool noArbitration)
+{
+ if (fSceneObjects.Count() > 0) {
+ plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded());
+ if (obj) {
+ for (size_t i = 0; i < obj->GetNumModifiers(); ++i) {
+ plLogicModifier* logic = const_cast(plLogicModifier::ConvertNoRef(obj->GetModifier(i)));
+ if (logic)
+ logic->VolumeNoArbitration(noArbitration);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h
index 9f22a1b3..65988273 100644
--- a/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h
+++ b/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h
@@ -200,6 +200,9 @@ public:
// hack for garrison
void VolumeSensorIgnoreExtraEnters(bool ignore);
+
+ /** More SubWorld hacks */
+ void VolumeSensorNoArbitration(bool noArbitration);
};
#endif // _pySceneObject_h_
diff --git a/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp
index c379a1fe..8966dbc9 100644
--- a/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp
+++ b/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp
@@ -452,6 +452,17 @@ PYTHON_METHOD_DEFINITION(ptSceneobject, volumeSensorIgnoreExtraEnters, args)
PYTHON_RETURN_NONE;
}
+PYTHON_METHOD_DEFINITION(ptSceneobject, volumeSensorNoArbitration, args)
+{
+ bool noArbitration = true;
+ if (!PyArg_ParseTuple(args, "|b", &noArbitration)) {
+ PyErr_SetString(PyExc_TypeError, "volumeSensorNoArbitration expects an optional boolean");
+ PYTHON_RETURN_ERROR;
+ }
+ self->fThis->VolumeSensorNoArbitration(noArbitration);
+ PYTHON_RETURN_NONE;
+}
+
PYTHON_START_METHODS_TABLE(ptSceneobject)
PYTHON_METHOD(ptSceneobject, addKey, "Params: key\nMostly used internally.\n"
"Add another sceneobject ptKey"),
@@ -510,6 +521,7 @@ PYTHON_START_METHODS_TABLE(ptSceneobject)
PYTHON_METHOD(ptSceneobject, getSoundIndex, "Params: sndComponentName\nGet the index of the requested sound component"),
PYTHON_METHOD(ptSceneobject, volumeSensorIgnoreExtraEnters, "Params: ignore\nTells the volume sensor attached to this object to ignore extra enters (default), or not (hack for garrison)."),
+ PYTHON_METHOD(ptSceneobject, volumeSensorNoArbitration, "Params: noArbitration\nTells the volume sensor attached to this object whether or not to negotiate exclusive locks with the server."),
PYTHON_END_METHODS_TABLE;
PYTHON_GET_DEFINITION(ptSceneobject, draw)
diff --git a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h
index 6c2d9a51..75f0e728 100644
--- a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h
+++ b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h
@@ -367,6 +367,7 @@ CLASS_INDEX_LIST_START
CLASS_INDEX(plDynamicCamMap),
CLASS_INDEX(plRidingAnimatedPhysicalDetector),
CLASS_INDEX(plVolumeSensorConditionalObjectNoArbitration),
+ CLASS_INDEX(plPXSubWorld),
//---------------------------------------------------------
// Keyed objects above this line, unkeyed (such as messages) below..
//---------------------------------------------------------
diff --git a/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h b/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h
index 05c81c70..ffb6b6b5 100644
--- a/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h
+++ b/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h
@@ -57,6 +57,8 @@ class hsResMgr;
class plServerReplyMsg : public plMessage
{
int fType;
+ bool fWasDelayed;
+
public:
enum
@@ -67,10 +69,13 @@ public:
};
void SetType(int t) { fType = t; }
- int GetType() { return fType; }
+ int GetType() const { return fType; }
+
+ void SetWasDelayed(bool v) { fWasDelayed = v; }
+ bool GetWasDelayed() const { return fWasDelayed; }
- plServerReplyMsg() : fType(kUnInit) { }
- plServerReplyMsg(const plKey &s, const plKey &r, const double* t) : plMessage(s,r,t), fType(kUnInit) { }
+ plServerReplyMsg() : fType(kUnInit), fWasDelayed(false) { }
+ plServerReplyMsg(const plKey &s, const plKey &r, const double* t) : plMessage(s,r,t), fType(kUnInit), fWasDelayed(false) { }
CLASSNAME_REGISTER( plServerReplyMsg );
GETINTERFACE_ANY( plServerReplyMsg, plMessage );
diff --git a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp
index ab48b16c..deb05294 100644
--- a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp
+++ b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp
@@ -44,6 +44,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "plgDispatch.h"
#include "hsResMgr.h"
#include "hsTimer.h"
+#include "../pnTimer/plTimerCallbackManager.h"
#include "../pnSceneObject/plSceneObject.h"
#include "../pnNetCommon/plGenericVar.h"
#include "../pnNetCommon/plNetApp.h"
@@ -53,6 +54,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "../pnMessage/plEnableMsg.h"
#include "../pnMessage/plServerReplyMsg.h"
+uint32_t plLogicModBase::sArbitrationDelayMs = 0;
+
void plLogicModBase::ConsoleTrigger(plKey playerKey)
{
// Setup the event data in case this is a OneShot responder that needs it
@@ -138,31 +141,12 @@ hsBool plLogicModBase::MsgReceive(plMessage* msg)
plServerReplyMsg* pSMsg = plServerReplyMsg::ConvertNoRef(msg);
if (pSMsg)
{
- hsAssert(pSMsg->GetType() != plServerReplyMsg::kUnInit, "uninit server reply msg");
-
-#if 1
- char str[256];
- sprintf(str, "LM: LogicModifier %s recvd trigger request reply:%s, wasRequesting=%d, t=%f\n", GetKeyName(),
- pSMsg->GetType() == plServerReplyMsg::kDeny ? "denied" : "confirmed",
- HasFlag(kRequestingTrigger), hsTimer::GetSysSeconds());
- plNetClientApp::GetInstance()->DebugMsg(str);
-#endif
-
- if (pSMsg->GetType() == plServerReplyMsg::kDeny)
- {
- if (HasFlag(kRequestingTrigger))
- {
- plNetClientApp::GetInstance()->DebugMsg("\tLM: Denied, clearing requestingTrigger");
- ClearFlag(kRequestingTrigger);
- }
- else
- plNetClientApp::GetInstance()->DebugMsg("\tLM: Denied, but not requesting?");
- }
- else
- {
- hsBool netRequest=false; // we're triggering as a result of a local activation
- PreTrigger(netRequest);
- IUpdateSharedState(false /* untriggering */);
+ if (sArbitrationDelayMs == 0 || pSMsg->GetWasDelayed()) {
+ IHandleArbitration(pSMsg);
+ } else {
+ pSMsg->SetWasDelayed(true);
+ pSMsg->Ref(); // timer callback manager steals this reference
+ plgTimerCallbackMgr::NewTimer(static_cast(sArbitrationDelayMs) / 1000, pSMsg);
}
return true;
}
@@ -186,6 +170,28 @@ hsBool plLogicModBase::MsgReceive(plMessage* msg)
return plSingleModifier::MsgReceive(msg);
}
+void plLogicModBase::IHandleArbitration(plServerReplyMsg* pSMsg)
+{
+ hsAssert(pSMsg->GetType() != plServerReplyMsg::kUnInit, "uninit server reply msg");
+ plNetClientApp::GetInstance()->DebugMsg("LM: LogicModifier {} recvd trigger request reply:{}, wasRequesting={}, t={f}\n",
+ GetKeyName(),
+ pSMsg->GetType() == plServerReplyMsg::kDeny ? "denied" : "confirmed",
+ HasFlag(kRequestingTrigger), hsTimer::GetSysSeconds());
+
+ if (pSMsg->GetType() == plServerReplyMsg::kDeny) {
+ if (HasFlag(kRequestingTrigger)) {
+ plNetClientApp::GetInstance()->DebugMsg("\tLM: Denied, clearing requestingTrigger");
+ ClearFlag(kRequestingTrigger);
+ } else {
+ plNetClientApp::GetInstance()->DebugMsg("\tLM: Denied, but not requesting?");
+ }
+ } else {
+ bool netRequest=false; // we're triggering as a result of a local activation
+ PreTrigger(netRequest);
+ IUpdateSharedState(false /* untriggering */);
+ }
+}
+
void plLogicModBase::RequestTrigger(hsBool netRequest)
{
if (HasFlag(kTriggered))
diff --git a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h
index 6a9050ff..908f5924 100644
--- a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h
+++ b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h
@@ -43,6 +43,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#ifndef plLogicModBase_inc
#define plLogicModBase_inc
+#include
#include "plSingleModifier.h"
#include "../pnNetCommon/plSynchedValue.h"
#include "hsTemplates.h"
@@ -66,6 +67,8 @@ public:
};
protected:
+ static uint32_t sArbitrationDelayMs;
+
hsTArray fCommandList;
hsTArray fReceiverList;
UInt32 fCounterLimit;
@@ -77,6 +80,7 @@ protected:
virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) {return false;}
void IUpdateSharedState(bool triggered) const;
+ void IHandleArbitration(class plServerReplyMsg* msg);
hsBool IEvalCounter();
virtual void PreTrigger(hsBool netRequest);
virtual void Trigger(hsBool netRequest);
@@ -85,7 +89,7 @@ protected:
void CreateNotifyMsg();
public:
- friend plVolumeSensorConditionalObjectNoArbitration;
+ friend class plVolumeSensorConditionalObject;
plLogicModBase();
~plLogicModBase();
CLASSNAME_REGISTER( plLogicModBase );
@@ -121,6 +125,9 @@ public:
// for debug purposes only!
void ConsoleTrigger(plKey playerKey);
void ConsoleRequestTrigger();
+
+ /** Specifies an amount of time (in milliseconds) to delay processing server arbitration responses */
+ static void SetArbitrationDelay(uint32_t ms) { sArbitrationDelayMs = ms; }
};
diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp
index f69e6cba..90fc261c 100644
--- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp
+++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp
@@ -1978,7 +1978,7 @@ hsBool plArmatureMod::IsLocalAvatar()
return plAvatarMgr::GetInstance()->GetLocalAvatar() == this;
}
-hsBool plArmatureMod::IsLocalAI()
+hsBool plArmatureMod::IsLocalAI() const
{
plAvBrainCritter* ai = plAvBrainCritter::ConvertNoRef(FindBrainByClass(plAvBrainCritter::Index()));
if (ai)
diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h
index 26d1839c..b79f2105 100644
--- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h
+++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h
@@ -220,7 +220,7 @@ public:
void GetPositionAndRotationSim(hsPoint3* position, hsQuat* rotation);
hsBool IsLocalAvatar();
- hsBool IsLocalAI();
+ hsBool IsLocalAI() const;
virtual const plSceneObject *FindBone(const char * name) const;
virtual const plSceneObject *FindBone(UInt32 id) const; // use an id from an appropriate taxonomy, such as plAvBrainHuman::BoneID
virtual void AddBoneMapping(UInt32 id, const plSceneObject *bone);
diff --git a/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp b/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp
index ab6f15c0..8ec40186 100644
--- a/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp
+++ b/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp
@@ -279,3 +279,12 @@ void plLogicModifier::VolumeIgnoreExtraEnters(bool ignore /* = true */)
condition->IgnoreExtraEnters(ignore);
}
}
+
+void plLogicModifier::VolumeNoArbitration(bool noArbitration)
+{
+ for (size_t i = 0; i < fConditionList.Count(); ++i) {
+ plVolumeSensorConditionalObject* condition = plVolumeSensorConditionalObject::ConvertNoRef(fConditionList[i]);
+ if (condition)
+ condition->NoServerArbitration(noArbitration);
+ }
+}
diff --git a/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h b/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h
index 77f2b922..10952e18 100644
--- a/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h
+++ b/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h
@@ -70,6 +70,7 @@ public:
virtual void Reset(bool bCounterReset);
void VolumeIgnoreExtraEnters(bool ignore = true); // hack for garrison
+ void VolumeNoArbitration(bool noArbitration = true);
int fMyCursor;
};
diff --git a/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp b/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp
index 53b97ff5..22714384 100644
--- a/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp
+++ b/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp
@@ -127,6 +127,7 @@ plNetMsgScreener::Answer plNetMsgScreener::IAllowMessageType(Int16 classIndex, c
case CLASS_INDEX_SCOPED(plClothingMsg):
case CLASS_INDEX_SCOPED(plEnableMsg):
case CLASS_INDEX_SCOPED(plLinkToAgeMsg):
+ case CLASS_INDEX_SCOPED(plSubWorldMsg):
return kYes;
// definitely yes or no (based on whether sender is a CCR)
diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp
index 3745f067..ad8afe22 100644
--- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp
+++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp
@@ -61,6 +61,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "../pnKeyedObject/plKey.h"
#include "../pnMessage/plCorrectionMsg.h"
#include "../pnMessage/plNodeRefMsg.h"
+#include "../pnMessage/plObjRefMsg.h"
#include "../pnMessage/plSDLModifierMsg.h"
#include "../plMessage/plSimStateMsg.h"
#include "../plMessage/plSimInfluenceMsg.h"
@@ -72,6 +73,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "../plStatusLog/plStatusLog.h"
#include "plPXConvert.h"
#include "plPXPhysicalControllerCore.h"
+#include "plPXSubWorld.h"
#include "../plModifier/plDetectorLog.h"
@@ -84,25 +86,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#define LogActivate(func) if (fActor->isSleeping()) SimLog("%s activated by %s", GetKeyName(), func);
-PhysRecipe::PhysRecipe()
- : mass(0.f)
- , friction(0.f)
- , restitution(0.f)
- , bounds(plSimDefs::kBoundsMax)
- , group(plSimDefs::kGroupMax)
- , reportsOn(0)
- , objectKey(nil)
- , sceneNode(nil)
- , worldKey(nil)
- , convexMesh(nil)
- , triMesh(nil)
- , radius(0.f)
- , offset(0.f, 0.f, 0.f)
- , meshStream(nil)
-{
- l2s.Reset();
-}
-
plProfile_Extern(MaySendLocation);
plProfile_Extern(LocationsSent);
plProfile_Extern(PhysicsUpdates);
@@ -128,19 +111,15 @@ int plPXPhysical::fNumberAnimatedActivators = 0;
plPXPhysical::plPXPhysical()
: fSDLMod(nil)
, fActor(nil)
- , fBoundsType(plSimDefs::kBoundsMax)
, fLOSDBs(plSimDefs::kLOSDBNone)
, fGroup(plSimDefs::kGroupMax)
, fReportsOn(0)
, fLastSyncTime(0.0f)
, fProxyGen(nil)
- , 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)
@@ -354,15 +333,14 @@ hsBool plPXPhysical::Should_I_Trigger(hsBool enter, hsPoint3& pos)
}
-hsBool plPXPhysical::Init(PhysRecipe& recipe)
+hsBool plPXPhysical::Init()
{
hsBool startAsleep = false;
- fBoundsType = recipe.bounds;
- fGroup = recipe.group;
- fReportsOn = recipe.reportsOn;
- fObjectKey = recipe.objectKey;
- fSceneNode = recipe.sceneNode;
- fWorldKey = recipe.worldKey;
+
+ fGroup = fRecipe.group;
+ fReportsOn = fRecipe.reportsOn;
+ fObjectKey = fRecipe.objectKey;
+ fSceneNode = fRecipe.sceneNode;
NxActorDesc actorDesc;
NxSphereShapeDesc sphereDesc;
@@ -370,19 +348,19 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe)
NxTriangleMeshShapeDesc trimeshShapeDesc;
NxBoxShapeDesc boxDesc;
- plPXConvert::Matrix(recipe.l2s, actorDesc.globalPose);
+ plPXConvert::Matrix(fRecipe.l2s, actorDesc.globalPose);
- switch (fBoundsType)
+ switch (fRecipe.bounds)
{
case plSimDefs::kSphereBounds:
{
hsMatrix44 sphereL2W;
sphereL2W.Reset();
- sphereL2W.SetTranslate(&recipe.offset);
+ sphereL2W.SetTranslate(&fRecipe.offset);
- sphereDesc.radius = recipe.radius;
+ sphereDesc.radius = fRecipe.radius;
plPXConvert::Matrix(sphereL2W, sphereDesc.localPose);
- sphereDesc.group = fGroup;
+ sphereDesc.group = fRecipe.group;
actorDesc.shapes.pushBack(&sphereDesc);
}
break;
@@ -391,54 +369,54 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe)
// 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)
+ if (fRecipe.group == plSimDefs::kGroupDetector && fRecipe.meshStream == nil)
{
#ifdef USE_BOXES_FOR_DETECTOR_HULLS
- MakeBoxFromHull(recipe.convexMesh, boxDesc);
- plSimulationMgr::GetInstance()->GetSDK()->releaseConvexMesh(*recipe.convexMesh);
- boxDesc.group = fGroup;
+ MakeBoxFromHull(fRecipe.convexMesh, boxDesc);
+ plSimulationMgr::GetInstance()->GetSDK()->releaseConvexMesh(*fRecipe.convexMesh);
+ boxDesc.group = fRecipe.group;
actorDesc.shapes.push_back(&boxDesc);
#else
#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND
// make a hull of planes for testing IsInside
- IMakeHull(recipe.convexMesh,recipe.l2s);
+ IMakeHull(fRecipe.convexMesh,fRecipe.l2s);
#endif // USE_PHYSX_CONVEXHULL_WORKAROUND
- convexShapeDesc.meshData = recipe.convexMesh;
- convexShapeDesc.userData = recipe.meshStream;
- convexShapeDesc.group = fGroup;
+ convexShapeDesc.meshData = fRecipe.convexMesh;
+ convexShapeDesc.userData = fRecipe.meshStream;
+ convexShapeDesc.group = fRecipe.group;
actorDesc.shapes.pushBack(&convexShapeDesc);
#endif // USE_BOXES_FOR_DETECTOR_HULLS
}
else
{
- convexShapeDesc.meshData = recipe.convexMesh;
- convexShapeDesc.userData = recipe.meshStream;
- convexShapeDesc.group = fGroup;
+ convexShapeDesc.meshData = fRecipe.convexMesh;
+ convexShapeDesc.userData = fRecipe.meshStream;
+ convexShapeDesc.group = fRecipe.group;
actorDesc.shapes.pushBack(&convexShapeDesc);
}
break;
case plSimDefs::kBoxBounds:
{
- boxDesc.dimensions = plPXConvert::Point(recipe.bDimensions);
+ boxDesc.dimensions = plPXConvert::Point(fRecipe.bDimensions);
hsMatrix44 boxL2W;
boxL2W.Reset();
- boxL2W.SetTranslate(&recipe.bOffset);
+ boxL2W.SetTranslate(&fRecipe.bOffset);
plPXConvert::Matrix(boxL2W, boxDesc.localPose);
- boxDesc.group = fGroup;
+ boxDesc.group = fRecipe.group;
actorDesc.shapes.push_back(&boxDesc);
}
break;
case plSimDefs::kExplicitBounds:
case plSimDefs::kProxyBounds:
- if (fGroup == plSimDefs::kGroupDetector)
+ if (fRecipe.group == plSimDefs::kGroupDetector)
{
SimLog("Someone using an Exact on a detector region: %s", GetKeyName());
}
- trimeshShapeDesc.meshData = recipe.triMesh;
- trimeshShapeDesc.userData = recipe.meshStream;
- trimeshShapeDesc.group = fGroup;
+ trimeshShapeDesc.meshData = fRecipe.triMesh;
+ trimeshShapeDesc.userData = fRecipe.meshStream;
+ trimeshShapeDesc.group = fRecipe.group;
actorDesc.shapes.pushBack(&trimeshShapeDesc);
break;
default:
@@ -449,10 +427,9 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe)
// Now fill out the body, or dynamic part of the physical
NxBodyDesc bodyDesc;
- fMass = recipe.mass;
- if (recipe.mass != 0)
+ if (fRecipe.mass != 0)
{
- bodyDesc.mass = recipe.mass;
+ bodyDesc.mass = fRecipe.mass;
actorDesc.body = &bodyDesc;
if (GetProperty(plSimulationInterface::kPinned))
@@ -461,13 +438,13 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe)
startAsleep = true; // put it to sleep if they are going to be frozen
}
- if (fGroup != plSimDefs::kGroupDynamic || GetProperty(plSimulationInterface::kPhysAnim))
+ if (fRecipe.group != plSimDefs::kGroupDynamic || GetProperty(plSimulationInterface::kPhysAnim))
{
SetProperty(plSimulationInterface::kPassive, true);
// Even though the code for animated physicals and animated activators are the same
// keep these code snippets separated for fine tuning. Thanks.
- if (fGroup == plSimDefs::kGroupDynamic)
+ if (fRecipe.group == plSimDefs::kGroupDynamic)
{
// handle the animated physicals.... make kinematic for now.
fNumberAnimatedPhysicals++;
@@ -495,7 +472,7 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe)
// Put the dynamics into actor group 1. The actor groups are only used for
// deciding who we get contact reports for.
- if (fGroup == plSimDefs::kGroupDynamic)
+ if (fRecipe.group == plSimDefs::kGroupDynamic)
actorDesc.group = 1;
NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey);
@@ -512,7 +489,7 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe)
return false;
NxShape* shape = fActor->getShapes()[0];
- shape->setMaterial(plSimulationMgr::GetInstance()->GetMaterialIdx(scene, recipe.friction, recipe.restitution));
+ shape->setMaterial(plSimulationMgr::GetInstance()->GetMaterialIdx(scene, fRecipe.friction, fRecipe.restitution));
// Turn on the trigger flags for any detectors.
//
@@ -523,7 +500,7 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe)
// problems trying to calculate an intertial tensor. By letting it be
// created as a normal dynamic first, then setting the flags, we work around
// that problem.
- if (fGroup == plSimDefs::kGroupDetector)
+ if (fRecipe.group == plSimDefs::kGroupDetector)
{
shape->setFlag(NX_TRIGGER_ON_ENTER, true);
shape->setFlag(NX_TRIGGER_ON_LEAVE, true);
@@ -547,14 +524,8 @@ hsBool plPXPhysical::Init(PhysRecipe& recipe)
plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(fSceneNode, plRefMsg::kOnCreate, -1, plNodeRefMsg::kPhysical);
hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kActiveRef);
- if (fWorldKey)
- {
- plGenRefMsg* ref = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPhysRefWorld);
- hsgResMgr::ResMgr()->AddViaNotify(fWorldKey, ref, plRefFlags::kActiveRef);
- }
-
// only dynamic physicals without noSync need SDLs
- if ( fGroup == plSimDefs::kGroupDynamic && !fProps.IsBitSet(plSimulationInterface::kNoSynchronize) )
+ if ( fRecipe.group == plSimDefs::kGroupDynamic && !fProps.IsBitSet(plSimulationInterface::kNoSynchronize) )
{
// add SDL modifier
plSceneObject* sceneObj = plSceneObject::ConvertNoRef(fObjectKey->ObjectIsLoaded());
@@ -611,29 +582,66 @@ hsBool plPXPhysical::MsgReceive( plMessage* msg )
hsBool plPXPhysical::HandleRefMsg(plGenRefMsg* refMsg)
{
UInt8 refCtxt = refMsg->GetContext();
- plKey refKey = refMsg->GetRef()->GetKey();
- plKey ourKey = GetKey();
- PhysRefType refType = PhysRefType(refMsg->fType);
- const char* refKeyName = refKey ? refKey->GetName() : "MISSING";
+ switch (refMsg->fType) {
+ case kPhysRefWorld: {
+ switch (refCtxt) {
+ case plRefMsg::kOnCreate:
+ case plRefMsg::kOnRequest:
+ // PotS files specify a plHKSubWorld as the subworld key. For everything else,
+ // the subworlds are a plSceneObject key. For sanity purposes, we will allow
+ // references to the plPXSubWorld here. HOWEVER, we will need to grab the target
+ // and replace our reference...
+ if (plPXSubWorld* subWorldIface = plPXSubWorld::ConvertNoRef(refMsg->GetRef())) {
+ hsAssert(subWorldIface->GetOwnerKey(), "subworld owner is NULL?! Uh oh...");
+ plGenRefMsg* replaceRefMsg = new plGenRefMsg(GetKey(), plRefMsg::kOnReplace, 0, kPhysRefWorld);
+ hsgResMgr::ResMgr()->AddViaNotify(subWorldIface->GetOwnerKey(), replaceRefMsg, plRefFlags::kActiveRef);
+ }
+ // fall-thru is intentional :)
+ case plRefMsg::kOnReplace:
+ // loading into a subworld
+ if (plSceneObject* subSO = plSceneObject::ConvertNoRef(refMsg->GetRef())) {
+ fWorldKey = subSO->GetKey();
+
+ // Cyan produced files will never have plPXSubWorld as this is a H'uru-ism.
+ // Let us make a default one such that we can play with default subworlds at runtime.
+ // HAAAAAAAAAX!!!
+ plPXSubWorld* subWorldIface = plPXSubWorld::ConvertNoRef(subSO->GetGenericInterface(plPXSubWorld::Index()));
+ if (!subWorldIface) {
+ subWorldIface = new plPXSubWorld();
+
+ // This can be simplified if/when incorporating zrax' String Theory library
+ std::string strKeyName = subSO->GetKeyName();
+ strKeyName += "_DefSubWorld";
+ const char *cKeyName = strKeyName.c_str();
+
+ hsgResMgr::ResMgr()->NewKey(cKeyName,
+ subWorldIface, GetKey()->GetUoid().GetLocation());
+
+ plObjRefMsg* subIfaceRef = new plObjRefMsg(subSO->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface);
+ // this will send the reference immediately
+ hsgResMgr::ResMgr()->SendRef(subWorldIface, subIfaceRef, plRefFlags::kActiveRef);
+ }
- if (refType == kPhysRefWorld)
- {
- if (refCtxt == plRefMsg::kOnCreate || refCtxt == plRefMsg::kOnRequest)
- {
- // Cache the initial transform, since we assume the sceneobject already knows
- // that and doesn't need to be told again
- IGetTransformGlobal(fCachedLocal2World);
+ // Now, we can initialize the physical...
+ Init();
+ IGetTransformGlobal(fCachedLocal2World);
+ return true;
+ }
}
- if (refCtxt == plRefMsg::kOnDestroy)
- {
+ break;
+ case plRefMsg::kOnDestroy: {
// our world was deleted out from under us: move to the main world
+ // NOTE: this was not implemented even before the PXSubWorld rewrite.
+ // not going to bother!
// hsAssert(0, "Lost world");
}
+ break;
}
- else if (refType == kPhysRefSndGroup)
- {
- switch (refCtxt)
+ break;
+
+ case kPhysRefSndGroup: {
+ switch (refCtxt)
{
case plRefMsg::kOnCreate:
case plRefMsg::kOnRequest:
@@ -645,9 +653,11 @@ hsBool plPXPhysical::HandleRefMsg(plGenRefMsg* refMsg)
break;
}
}
- else
- {
+ break;
+
+ default:
hsAssert(0, "Unknown ref type, who sent us this?");
+ break;
}
return true;
@@ -878,7 +888,6 @@ void plPXPhysical::IGetTransformGlobal(hsMatrix44& l2w) const
{
plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded());
hsAssert(so, "Scene object not loaded while accessing subworld.");
- // We'll hit this at export time, when the ci isn't ready yet, so do a check
if (so->GetCoordinateInterface())
{
const hsMatrix44& s2w = so->GetCoordinateInterface()->GetLocalToWorld();
@@ -1026,23 +1035,22 @@ void plPXPhysical::Read(hsStream* stream, hsResMgr* mgr)
plPhysical::Read(stream, mgr);
ClearMatrix(fCachedLocal2World);
- PhysRecipe recipe;
- recipe.mass = stream->ReadSwapScalar();
- recipe.friction = stream->ReadSwapScalar();
- recipe.restitution = stream->ReadSwapScalar();
- recipe.bounds = (plSimDefs::Bounds)stream->ReadByte();
- recipe.group = (plSimDefs::Group)stream->ReadByte();
- recipe.reportsOn = stream->ReadSwap32();
+ fRecipe.mass = stream->ReadSwapScalar();
+ fRecipe.friction = stream->ReadSwapScalar();
+ fRecipe.restitution = stream->ReadSwapScalar();
+ fRecipe.bounds = (plSimDefs::Bounds)stream->ReadByte();
+ fRecipe.group = (plSimDefs::Group)stream->ReadByte();
+ fRecipe.reportsOn = stream->ReadSwap32();
fLOSDBs = stream->ReadSwap16();
//hack for swim regions currently they are labeled as static av blockers
if(fLOSDBs==plSimDefs::kLOSDBSwimRegion)
{
- recipe.group=plSimDefs::kGroupMax;
+ fRecipe.group=plSimDefs::kGroupMax;
}
//
- recipe.objectKey = mgr->ReadKey(stream);
- recipe.sceneNode = mgr->ReadKey(stream);
- recipe.worldKey = mgr->ReadKey(stream);
+ fRecipe.objectKey = mgr->ReadKey(stream);
+ fRecipe.sceneNode = mgr->ReadKey(stream);
+ fRecipe.worldKey = mgr->ReadKeyNotifyMe(stream, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPhysRefWorld), plRefFlags::kActiveRef);
mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPhysRefSndGroup), plRefFlags::kActiveRef);
@@ -1050,30 +1058,33 @@ void plPXPhysical::Read(hsStream* stream, hsResMgr* mgr)
hsQuat rot;
pos.Read(stream);
rot.Read(stream);
- rot.MakeMatrix(&recipe.l2s);
- recipe.l2s.SetTranslate(&pos);
+ rot.MakeMatrix(&fRecipe.l2s);
+ fRecipe.l2s.SetTranslate(&pos);
fProps.Read(stream);
- if (recipe.bounds == plSimDefs::kSphereBounds)
+ if (fRecipe.bounds == plSimDefs::kSphereBounds)
{
- recipe.radius = stream->ReadSwapScalar();
- recipe.offset.Read(stream);
+ fRecipe.radius = stream->ReadSwapScalar();
+ fRecipe.offset.Read(stream);
}
- else if (recipe.bounds == plSimDefs::kBoxBounds)
+ else if (fRecipe.bounds == plSimDefs::kBoxBounds)
{
- recipe.bDimensions.Read(stream);
- recipe.bOffset.Read(stream);
+ fRecipe.bDimensions.Read(stream);
+ fRecipe.bOffset.Read(stream);
}
else
{
- if (recipe.bounds == plSimDefs::kHullBounds)
- recipe.convexMesh = IReadHull(stream);
+ if (fRecipe.bounds == plSimDefs::kHullBounds)
+ fRecipe.convexMesh = IReadHull(stream);
else
- recipe.triMesh = IReadTriMesh(stream);
+ fRecipe.triMesh = IReadTriMesh(stream);
}
- Init(recipe);
+ // If we do not have a world, specified, we go ahead and init into the main world...
+ // This will been done in MsgReceive otherwise
+ if (!fRecipe.worldKey)
+ Init();
hsAssert(!fProxyGen, "Already have proxy gen, double read?");
@@ -1115,7 +1126,7 @@ void plPXPhysical::Read(hsStream* stream, hsResMgr* mgr)
// Statics are yellow
physColor.Set(1.f,0.8f,0.2f,1.f);
// if in a subworld... slightly transparent
- if(fWorldKey)
+ if(fRecipe.worldKey)
opac = 0.6f;
}
else
@@ -1241,7 +1252,7 @@ void plPXPhysical::Write(hsStream* stream, hsResMgr* mgr)
stream->WriteSwapScalar(fActor->getMass());
stream->WriteSwapScalar(friction);
stream->WriteSwapScalar(restitution);
- stream->WriteByte(fBoundsType);
+ stream->WriteByte(fRecipe.bounds);
stream->WriteByte(fGroup);
stream->WriteSwap32(fReportsOn);
stream->WriteSwap16(fLOSDBs);
@@ -1259,14 +1270,14 @@ void plPXPhysical::Write(hsStream* stream, hsResMgr* mgr)
fProps.Write(stream);
- if (fBoundsType == plSimDefs::kSphereBounds)
+ if (fRecipe.bounds == plSimDefs::kSphereBounds)
{
const NxSphereShape* sphereShape = shape->isSphere();
stream->WriteSwapScalar(sphereShape->getRadius());
hsPoint3 localPos = plPXConvert::Point(sphereShape->getLocalPosition());
localPos.Write(stream);
}
- else if (fBoundsType == plSimDefs::kBoxBounds)
+ else if (fRecipe.bounds == plSimDefs::kBoxBounds)
{
const NxBoxShape* boxShape = shape->isBox();
hsPoint3 dim = plPXConvert::Point(boxShape->getDimensions());
@@ -1276,7 +1287,7 @@ void plPXPhysical::Write(hsStream* stream, hsResMgr* mgr)
}
else
{
- if (fBoundsType == plSimDefs::kHullBounds)
+ if (fRecipe.bounds == plSimDefs::kHullBounds)
hsAssert(shape->isConvexMesh(), "Hull shape isn't a convex mesh");
else
hsAssert(shape->isTriangleMesh(), "Exact shape isn't a trimesh");
diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h
index 5c4ca8ae..54b99644 100644
--- a/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h
+++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h
@@ -47,6 +47,9 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "../plPhysical/plSimDefs.h"
#include "hsBitVector.h"
#include "hsUtils.h"
+#include
+#include
+#include
class NxActor;
class NxConvexMesh;
@@ -72,8 +75,6 @@ class NxCapsule;
class PhysRecipe
{
public:
- PhysRecipe();
-
hsScalar mass;
hsScalar friction;
hsScalar restitution;
@@ -100,6 +101,12 @@ public:
// For export time only. The original data used to create the mesh
hsVectorStream* meshStream;
+
+ PhysRecipe()
+ : mass(0.f), friction(0.f), restitution(0.f), bounds(plSimDefs::kBoundsMax),
+ group(plSimDefs::kGroupMax), reportsOn(0), convexMesh(nullptr), triMesh(nullptr),
+ radius(0.f), offset(0.f, 0.f, 0.f), meshStream(nullptr)
+ { }
};
class plPXPhysical : public plPhysical
@@ -120,7 +127,7 @@ public:
GETINTERFACE_ANY(plPXPhysical, plPhysical);
// Export time and internal use only
- hsBool Init(PhysRecipe& recipe);
+ hsBool Init();
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void Write(hsStream* s, hsResMgr* mgr);
@@ -191,7 +198,11 @@ public:
//this partially for exclude regions vs avatar capsule
virtual hsBool OverlapWithCapsule(NxCapsule& cap);
- virtual hsScalar GetMass() {return fMass;}
+ virtual hsScalar GetMass() {return fRecipe.mass;}
+
+ PhysRecipe& GetRecipe() { return fRecipe; }
+ const PhysRecipe& GetRecipe() const { return fRecipe; }
+
protected:
class NxConvexMesh* IReadHull(hsStream* s);
class NxTriangleMesh* IReadTriMesh(hsStream* s);
@@ -244,12 +255,11 @@ protected:
NxActor* fActor;
plKey fWorldKey; // either a subworld or nil
- plSimDefs::Bounds fBoundsType;
+ PhysRecipe fRecipe;
plSimDefs::Group fGroup;
UInt32 fReportsOn; // bit vector for groups we report interactions with
UInt16 fLOSDBs; // Which LOS databases we get put into
hsBitVector fProps; // plSimulationInterface::plSimulationProperties kept here
- float fMass;
plKey fObjectKey; // the key to our scene object
plKey fSceneNode; // the room we're in
diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.cpp
new file mode 100644
index 00000000..b295089a
--- /dev/null
+++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.cpp
@@ -0,0 +1,52 @@
+/*==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 "plPXSubWorld.h"
+
+void plPXSubWorld::Read(hsStream* s, hsResMgr* mgr)
+{
+ plObjInterface::Read(s, mgr);
+ fGravity.Read(s);
+}
+
+void plPXSubWorld::Write(hsStream* s, hsResMgr* mgr)
+{
+ plObjInterface::Write(s, mgr);
+ fGravity.Write(s);
+}
+
+void plPXSubWorld::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
+{
+ // All this magick is handled elsewhere... Not asserting due to call spam.
+}
\ No newline at end of file
diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.h b/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.h
new file mode 100644
index 00000000..77bf5fe0
--- /dev/null
+++ b/Sources/Plasma/PubUtilLib/plPhysX/plPXSubWorld.h
@@ -0,0 +1,67 @@
+/*==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 plPXSubWorld_h_inc
+#define plPXSubWorld_h_inc
+
+#include "../../NucleusLib/pnSceneObject/plObjInterface.h"
+#include "hsGeometry3.h"
+
+#define X_GRAVITY 0.f
+#define Y_GRAVITY 0.f
+#define Z_GRAVITY -32.174049f
+
+class plPXSubWorld : public plObjInterface
+{
+ hsVector3 fGravity;
+
+public:
+ plPXSubWorld() : fGravity(X_GRAVITY, Y_GRAVITY, Z_GRAVITY) { }
+ plPXSubWorld(const hsVector3& gravity) : fGravity(gravity) { }
+
+ CLASSNAME_REGISTER(plPXSubWorld);
+ GETINTERFACE_ANY(plPXSubWorld, plObjInterface);
+
+ void Read(hsStream* s, hsResMgr* mgr) HS_OVERRIDE;
+ void Write(hsStream* s, hsResMgr* mgr) HS_OVERRIDE;
+
+ Int32 GetNumProperties() const HS_OVERRIDE { return 0; }
+ void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) HS_OVERRIDE;
+
+ const hsVector3& GetGravity() const { return fGravity; }
+ hsVector3& GetGravity() { return fGravity; }
+ void SetGravity(const hsVector3& gravity) { fGravity = gravity; }
+};
+
+#endif // plPXSubWorld_h_inc
\ No newline at end of file
diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h b/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h
index cf986171..9c8cbfab 100644
--- a/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h
+++ b/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h
@@ -48,6 +48,9 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
REGISTER_CREATABLE(plPXPhysical);
+#include "plPXSubWorld.h"
+REGISTER_CREATABLE(plPXSubWorld);
+
//#include "plHKSimulationSynchMsg.h"
//REGISTER_CREATABLE(plHKSimulationSynchMsg);
diff --git a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp
index 5603c566..fc922676 100644
--- a/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp
+++ b/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp
@@ -49,6 +49,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "plPXPhysical.h"
#include "plPXPhysicalControllerCore.h"
#include "plPXConvert.h"
+#include "plPXSubWorld.h"
#include "plLOSDispatch.h"
#include "../plPhysical/plPhysicsSoundMgr.h"
#include "../plStatusLog/plStatusLog.h"
@@ -424,8 +425,18 @@ NxScene* plSimulationMgr::GetScene(plKey world)
{
UInt32 maxSteps = (UInt32)hsCeil(fMaxDelta / fStepSize);
+ // The world key is assumed to be loaded (or null for main world) if we are here.
+ // As such, let us grab the plSceneObject's PXSubWorld definition to figure out
+ // what gravity should look like. Who knows, we might be in MC Escher land...
+ NxVec3 gravity(X_GRAVITY, Y_GRAVITY, Z_GRAVITY);
+ if (plSceneObject* so = plSceneObject::ConvertNoRef(world->VerifyLoaded())) {
+ if (plPXSubWorld* subworld = plPXSubWorld::ConvertNoRef(so->GetGenericInterface(plPXSubWorld::Index()))) {
+ gravity = plPXConvert::Vector(subworld->GetGravity());
+ }
+ }
+
NxSceneDesc sceneDesc;
- sceneDesc.gravity.set(0, 0, -32.174049f);
+ sceneDesc.gravity = gravity;
sceneDesc.userTriggerReport = &gSensorReport;
sceneDesc.userContactReport = &gContactReport;
sceneDesc.maxTimestep = fStepSize;