summaryrefslogtreecommitdiff
path: root/Runtime/Dynamics/PhysicsManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Dynamics/PhysicsManager.cpp')
-rw-r--r--Runtime/Dynamics/PhysicsManager.cpp1508
1 files changed, 1508 insertions, 0 deletions
diff --git a/Runtime/Dynamics/PhysicsManager.cpp b/Runtime/Dynamics/PhysicsManager.cpp
new file mode 100644
index 0000000..1ba62fe
--- /dev/null
+++ b/Runtime/Dynamics/PhysicsManager.cpp
@@ -0,0 +1,1508 @@
+#include "UnityPrefix.h"
+#if ENABLE_PHYSICS
+#include "PhysicsManager.h"
+#include "Runtime/Serialize/PersistentManager.h"
+#include "Runtime/Input/TimeManager.h"
+#include "RigidBody.h"
+#include "Collider.h"
+#include "Joint.h"
+#include "PhysicMaterial.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h"
+#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h"
+#include "External/PhysX/builds/SDKs/Physics/include/NxIntersectionBoxBox.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Utilities/Utility.h"
+#include "NxWrapperUtility.h"
+#include "Runtime/Misc/Allocator.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Misc/SystemInfo.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h"
+#include "Cloth.h"
+#include "SkinnedCloth.h"
+#include "CharacterController.h"
+#include "PhysXRaycast.h"
+#include "Runtime/Profiler/ProfilerStats.h"
+#include "Runtime/Allocator/MemoryMacros.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+#include "PhysicsModule.h"
+
+#if UNITY_PS3
+#include "PS3/NxCellConfiguration.h"
+
+extern uint8_t g_aPhysXPriorities[8];
+extern CellSpurs2* g_pSpursInstance;
+
+#endif
+
+inline void VerifyObjectPtr(Object* obj)
+{
+ #if !UNITY_RELEASE
+ if (obj == NULL)
+ return;
+ // OutputDebugString is windows/360 only!
+ //if (Object::IDToPointer(obj->GetInstanceID()) != obj)
+ // OutputDebugString(Format("%x\n", obj).c_str());
+ Assert(Object::IDToPointer(obj->GetInstanceID()) == obj);
+ #endif
+}
+
+using namespace std;
+using namespace Unity;
+
+PROFILER_INFORMATION(gRaycastProfile, "Physics.Raycast", kProfilerPhysics)
+PROFILER_INFORMATION(gRaycastAllProfile, "Physics.RaycastAll", kProfilerPhysics)
+PROFILER_INFORMATION(gCapsuleTestProfile, "Physics.CheckCapsule", kProfilerPhysics)
+PROFILER_INFORMATION(gSphereOverlapProfile, "Physics.OverlapSphere", kProfilerPhysics)
+PROFILER_INFORMATION(gSphereTestProfile, "Physics.TestSphere", kProfilerPhysics)
+PROFILER_INFORMATION(gCapsuleCastProfile, "Physics.CapsuleCast", kProfilerPhysics)
+PROFILER_INFORMATION(gCapsuleCastAllProfile, "Physics.CapsuleCastAll", kProfilerPhysics)
+
+
+#define RETURNIFNOPHYSX( v ) if(!gPhysicsSDK) return v;
+#define RETURNIFNOPHYSX_VOID() if(!gPhysicsSDK) return;
+
+
+static Vector3f CalculateRelativeVelocity (NxActor& a, NxActor& b);
+
+#define DEBUG_VISUALIZE_PHYSX 0
+
+enum
+{
+#if UNITY_WII
+ kNovodexMemoryAlignment = 8
+#else
+ kNovodexMemoryAlignment = kDefaultMemoryAlignment
+#endif
+};
+
+class NovodexAllocator : public NxUserAllocator
+{
+ public:
+
+ void* malloc(size_t size, NxMemoryType type)
+ {
+ return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment);
+ }
+
+ void* malloc(size_t size)
+ {
+ return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment);
+ }
+
+ void* mallocDEBUG(size_t size, const char* fileName, int line, const char* className, NxMemoryType type)
+ {
+ return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment);
+ return 0;
+ }
+ void* mallocDEBUG(size_t size, const char* fileName, int line)
+ {
+ return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment);
+ return 0;
+ }
+
+ void* realloc(void* memory, size_t size)
+ {
+ return UNITY_REALLOC_ALIGNED(kMemPhysics, memory, size, kNovodexMemoryAlignment);
+ }
+
+ void free(void* memory)
+ {
+ UNITY_FREE(kMemPhysics, memory);
+ }
+
+/* check is a bad name to choose. The MacOSX libraries already Carbon declare that function name.
+ void check()
+ {
+ }
+*/
+};
+
+
+struct ErrorStream : public NxUserOutputStream
+{
+ virtual void reportError(NxErrorCode code, const char * message, const char *file, int line)
+ {
+ if (code == NXE_DB_WARNING)
+ return;
+
+ DebugStringToFile (message, 0, file, line, kError);
+ }
+ /**
+ Reports an assertion violation. The user should return
+ */
+ virtual NxAssertResponse reportAssertViolation(const char * message, const char *file, int line)
+ {
+ DebugStringToFile (message, 0, file, line, kAssert);
+ return NX_AR_CONTINUE;
+ }
+ /**
+ Simply prints some debug text
+ */
+ virtual void print(const char * message)
+ {
+ LogString (message);
+ }
+};
+
+static void CreateDynamicsScene ();
+
+/*
+struct CollisionData
+{
+
+};
+*/
+
+class RaycastCollector : public NxUserRaycastReport
+{
+ public:
+ PhysicsManager::RaycastHits* hits;
+
+ virtual bool onHit(const NxRaycastHit& hit)
+ {
+ RaycastHit outHit;
+ NxToRaycastHit(hit, outHit);
+ hits->push_back(outHit);
+ return true;
+ }
+};
+
+
+class TriggerMessage : public NxUserTriggerReport
+{
+ public:
+ PhysicsManager::RecordedTriggers* record;
+
+ virtual void onTrigger(NxShape& triggerShape, NxShape& otherShape, NxTriggerFlag status)
+ {
+ Collider* trigger = (Collider*)triggerShape.userData;
+ Collider* collider = (Collider*)otherShape.userData;
+ record->push_back (PhysicsManager::RecordedTrigger ());
+ record->back ().trigger = trigger;
+ record->back ().collider = collider;
+ record->back ().status = status;
+ }
+};
+
+static Vector3f CalculateRelativeVelocity (NxActor& a, NxActor& b)
+{
+ NxVec3 vel0, vel1;
+ if (a.isDynamic ())
+ vel0 = a.getLinearVelocity ();
+ else
+ vel0.zero ();
+
+ if (b.isDynamic ())
+ vel1 = b.getLinearVelocity ();
+ else
+ vel1.zero ();
+ vel0 -= vel1;
+
+ return (const Vector3f&)vel0;
+}
+
+class ContactMessage : public NxUserContactReport
+{
+ public:
+ PhysicsManager::RecordedContacts* record;
+
+ virtual void onContactNotify (NxContactPair& pair, NxU32 status)
+ {
+
+ Rigidbody* thisRigidbody = (Rigidbody*)pair.actors[0]->userData;
+ Rigidbody* otherRigidbody = (Rigidbody*)pair.actors[1]->userData;
+
+ Collider* thisCollider = NULL;
+ if (pair.actors[0]->getNbShapes())
+ thisCollider = (Collider*)pair.actors[0]->getShapes ()[0]->userData;
+
+ Collider* otherCollider = NULL;
+ if (pair.actors[1]->getNbShapes())
+ otherCollider = (Collider*)pair.actors[1]->getShapes ()[0]->userData;
+
+ record->push_back (Collision ());
+ record->back ().thisRigidbody = thisRigidbody;
+ record->back ().otherRigidbody = otherRigidbody;
+ record->back ().thisCollider = thisCollider;
+ record->back ().otherCollider = otherCollider;
+ record->back ().status = status;
+ record->back ().impactForceSum = (const Vector3f&)pair.sumNormalForce;
+ record->back ().frictionForceSum = (const Vector3f&)pair.sumFrictionForce;
+ record->back ().relativeVelocity = CalculateRelativeVelocity (*pair.actors[0], *pair.actors[1]);
+
+ VerifyObjectPtr(record->back ().thisRigidbody);
+ VerifyObjectPtr(record->back ().otherRigidbody);
+ VerifyObjectPtr(record->back ().thisCollider);
+ VerifyObjectPtr(record->back ().otherCollider);
+
+ std::list<ContactPoint>& contacts = record->back ().contacts;
+ NxContactStreamIterator i (pair.stream);
+
+ //user can call getNumPairs() here
+ while(i.goNextPair())
+ {
+ Collider* c0 = i.isDeletedShape(0) ? NULL : (Collider*)i.getShape (0)->userData;
+ Collider* c1 = i.isDeletedShape(1) ? NULL : (Collider*)i.getShape (1)->userData;
+ //user can also call getShape() and getNumPatches() here
+ while(i.goNextPatch())
+ {
+ Vector3f normal = (const Vector3f&)i.getPatchNormal ();
+ //user can also call getPatchNormal() and getNumPoints() here
+ while(i.goNextPoint())
+ {
+ ContactPoint c;
+ c.collider[0] = c0;
+ c.collider[1] = c1;
+ c.point = (const Vector3f&)i.getPoint ();
+ c.normal = normal;
+ contacts.push_back (c);
+ }
+ }
+ }
+ }
+};
+
+class BrokenJointMessage : public NxUserNotify
+{
+ public:
+ PhysicsManager::RecordedJointBreaks* records;
+
+ virtual bool onJointBreak (NxReal breakingForce, NxJoint & brokenJoint)
+ {
+ Joint* joint = (Joint*)brokenJoint.userData;
+ joint->NullJoint();
+ PhysicsManager::RecordedJointBreak jointbreak;
+ jointbreak.impulse = breakingForce;
+ jointbreak.joint = joint;
+ records->push_back(jointbreak);
+ return true;
+ }
+
+ virtual void onWake(NxActor** actors, NxU32 count) {}
+ virtual void onSleep(NxActor** actors, NxU32 count) { }
+};
+
+static ErrorStream gErrorStream;
+static NovodexAllocator gAllocator;
+static NxPhysicsSDK* gPhysicsSDK = NULL;
+static NxScene* gPhysicsScene = NULL;
+static std::vector<NxScene*> gClothingScenes;
+static NxScene* gInactivePhysicsScene = NULL;
+static NxActor* gNULLActor = NULL;
+
+void PhysicsManager::InitializeClass ()
+{
+ RegisterAllowNameConversion (PhysicsManager::GetClassStringStatic(), "m_BounceTreshold", "m_BounceThreshold");
+
+ PhysXRaycast::InitializeClass();
+ InitializePhysicsModule();
+
+ gPhysicsSDK = NxCreatePhysicsSDK (NX_PHYSICS_SDK_VERSION, &gAllocator, &gErrorStream);
+ if (!gPhysicsSDK)
+ FatalErrorString ("Couldn't load physics");
+ if (!NxInitCooking (0, &gErrorStream))
+ FatalErrorString ("Couldn't load physics ");
+
+#if UNITY_PS3
+ NxCellSpursControl::initWithSpurs(g_pSpursInstance,5, g_aPhysXPriorities);
+#endif
+
+#if DEBUG_VISUALIZE_PHYSX
+ gPhysicsSDK->setParameter (NX_VISUALIZATION_SCALE, 1F);
+ gPhysicsSDK->setParameter (NX_VISUALIZE_COLLISION_SHAPES, 1F);
+// gPhysicsSDK->setParameter (NX_VISUALIZE_COLLISION_COMPOUNDS, 1F);
+#endif
+
+ gPhysicsSDK->setParameter(NX_IMPROVED_SPRING_SOLVER, 0);
+
+ CreateDynamicsScene ();
+ CharacterController::CreateControllerManager ();
+
+ REGISTER_PLAYERLOOP_CALL (PhysicsFixedUpdate, GetPhysicsManager().FixedUpdate());
+ REGISTER_PLAYERLOOP_CALL (PhysicsUpdate, GetPhysicsManager().Update ());
+ REGISTER_PLAYERLOOP_CALL (PhysicsRefreshWhenPaused, GetPhysicsManager().RefreshWhenPaused ());
+ REGISTER_PLAYERLOOP_CALL (PhysicsSkinnedClothUpdate, GetPhysicsManager().SkinnedClothUpdate ());
+ REGISTER_PLAYERLOOP_CALL (PhysicsResetInterpolatedTransformPosition, GetPhysicsManager().ResetInterpolatedTransformPosition ());
+
+ REGISTER_GLOBAL_CALLBACK (didUnloadScene, GetPhysicsManager().RecreateScene());
+}
+
+
+// If we use multiple scenes per CPU, we may get better parallelization, when scene load becomes
+// unbalances because cloth objects get disabled or destroyed.
+// Apparently, PhysX doesn't handle more then 64 scenes - and we use two for rigidbody physics.
+
+#if (UNITY_XENON || UNITY_PS3)
+# define NUMBER_OF_CLOTHING_SCENES_PER_CPU 1
+# define MAX_CLOTHING_SCENES 6
+#else
+# define NUMBER_OF_CLOTHING_SCENES_PER_CPU 4
+# define MAX_CLOTHING_SCENES 32
+#endif
+
+static void CreateDynamicsScene ()
+{
+ AssertIf (gPhysicsScene != NULL);
+ NxSceneDesc sceneDesc;
+ // Multi threading causes issues when simulating 1000 objects at the same location.
+ // We are not taking advantage of it anyway. So...
+ // Test is in exception unit test on rudolph
+ sceneDesc.flags &= ~NX_SF_SIMULATE_SEPARATE_THREAD;
+
+ if (gInactivePhysicsScene == NULL)
+ gInactivePhysicsScene = gPhysicsSDK->createScene(sceneDesc);
+
+
+ gPhysicsScene = gPhysicsSDK->createScene(sceneDesc);
+ gPhysicsScene->setGravity (NxVec3 (0.0F, -9.81F, 0.0F));
+ gPhysicsScene->setTiming (1.0F, 8, NX_TIMESTEP_VARIABLE);
+//broken?
+#if !UNITY_WINRT
+ if (systeminfo::GetProcessorCount() > 1)
+ {
+ int numClothingScenes = std::min(NUMBER_OF_CLOTHING_SCENES_PER_CPU * systeminfo::GetProcessorCount(), MAX_CLOTHING_SCENES);
+ gClothingScenes.resize (numClothingScenes);
+ }
+ else
+#endif
+ gClothingScenes.resize (1);
+ for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!=gClothingScenes.end(); i++)
+ *i = NULL;
+
+ // Setup actor group masks!
+ // We put every actor into a group depending on what contact notifications it wants.
+ // This way we don't send too many contact notify messages!
+ int masks[3] = {
+ 0,
+ NX_NOTIFY_ON_START_TOUCH | NX_NOTIFY_ON_END_TOUCH,
+ NX_NOTIFY_ON_TOUCH | NX_NOTIFY_ON_START_TOUCH | NX_NOTIFY_ON_END_TOUCH
+ };
+
+ for (int i=0;i<3;i++)
+ {
+ for (int j=0;j<3;j++)
+ GetDynamicsScene ().setActorGroupPairFlags (i, j, masks[i] | masks[j]);
+ }
+
+ GetDynamicsScene ().setGroupCollisionFlag (kIgnoreCollisionLayer, kIgnoreCollisionLayer, 0);
+
+ // Create hole material
+ NxMaterialDesc holematerial;
+ NxMaterial* material = GetDynamicsScene ().createMaterial (holematerial);
+ AssertIf(material->getMaterialIndex() != 1);
+
+ // Create wheel collider material - used by wheel collider
+ NxMaterialDesc wheelMaterial;
+ wheelMaterial.flags |= NX_MF_DISABLE_FRICTION;
+
+ material = GetDynamicsScene ().createMaterial (wheelMaterial);
+ AssertIf(material->getMaterialIndex() != 2);
+}
+
+void PhysicsManager::CleanupReports()
+{
+ // is there any point doing these allocations?
+
+ TriggerMessage* trigger = (TriggerMessage*)gPhysicsScene->getUserTriggerReport();
+ gPhysicsScene->setUserTriggerReport(NULL);
+ delete trigger;
+
+ ContactMessage* contact = (ContactMessage*)gPhysicsScene->getUserContactReport();
+ gPhysicsScene->setUserContactReport(NULL);
+ delete contact;
+
+ BrokenJointMessage* notify = (BrokenJointMessage*)gPhysicsScene->getUserNotify();
+ gPhysicsScene->setUserNotify(NULL);
+ delete notify;
+}
+
+void PhysicsManager::CreateReports()
+{
+ CleanupReports();
+
+ TriggerMessage* triggerMessage = new TriggerMessage ();
+ triggerMessage->record = &m_RecordedTriggers;
+ gPhysicsScene->setUserTriggerReport (triggerMessage);
+
+ ContactMessage* contactMessage = new ContactMessage ();
+ contactMessage->record = &m_RecordedContacts;
+ gPhysicsScene->setUserContactReport (contactMessage);
+
+ BrokenJointMessage* brokenJoint = new BrokenJointMessage ();
+ brokenJoint->records = &m_RecordedJointBreaks;
+ gPhysicsScene->setUserNotify (brokenJoint);
+}
+
+void PhysicsManager::CleanupClass ()
+{
+ PhysXRaycast::CleanupClass();
+ CleanupPhysicsModule();
+
+ if( !gPhysicsSDK )
+ return; // happens when user quits the screen selector
+
+ // Needs to be cleaned up before the physics scene, or we get a crash, if there
+ // are still controllers around (as is the case in the editor, which does not clean up all objects).
+#if ENABLE_PHYSICS
+ CharacterController::CleanupControllerManager ();
+#endif
+
+ vector<PhysicMaterial*> materialsTemp;
+ vector<NxMaterialDesc> materialDescsTemp;
+ ReleaseMaterials(materialsTemp, materialDescsTemp);
+
+ CleanupReports();
+
+ gPhysicsSDK->releaseScene (*gPhysicsScene);
+ gPhysicsSDK->releaseScene (*gInactivePhysicsScene);
+
+ for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!=gClothingScenes.end(); i++)
+ {
+ if (*i != NULL)
+ {
+ gPhysicsSDK->releaseScene (**i);
+ *i = NULL;
+ }
+ }
+ std::vector<NxScene*> emptyScenes;
+ gClothingScenes.swap(emptyScenes);
+
+ // We are not using NxReleasePhysicsSDK, because it doesn't release the sdk (a bug in our implementation of USE_STATIC_LIBS)
+ // it's fine to call the release on gPhysicsSDK directly, except that it doesn't set g_nxPhysicsSDK in PhysX to NULL
+ // NxReleasePhysicsSDK(gPhysicsSDK);
+
+ NxFoundationSDK* foundationSDK = NxGetFoundationSDK ();
+ gPhysicsSDK->release ();
+ gPhysicsSDK = NULL;
+
+ foundationSDK->release();
+
+#if UNITY_PS3
+ if(NxCellSpursControl::isSpursInitialized())
+ CELLCALL(NxCellSpursControl::terminate());
+#endif
+}
+
+
+PhysicsManager::PhysicsManager (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+ m_RaycastsHitTriggers = true;
+
+ m_Gravity = Vector3f (0, -9.81F, 0.0F);
+ m_LayerCollisionMatrix.resize (kNumLayers, 0xffffffff);
+ m_DefaultIterationCount = 6;
+ m_RigidbodyTransformMessageEnabled = true;
+ m_SmoothedClothDeltaTime = 0.0f;
+
+ RETURNIFNOPHYSX_VOID();
+
+ GetDynamicsSDK ().setParameter (NX_SKIN_WIDTH, 0.01F);
+ GetDynamicsSDK ().setParameter (NX_CONTINUOUS_CD, true);
+ GetDynamicsSDK ().setParameter (NX_CCD_EPSILON, 0.01F);
+
+ SetupDefaultMaterial ();
+ CreateReports();
+ Object::FindAllDerivedClasses (ClassID (Collider), &m_DisableTransformMessage);
+}
+
+void PhysicsManager::Reset ()
+{
+ RETURNIFNOPHYSX_VOID();
+
+ Super::Reset();
+
+ m_Gravity = Vector3f (0, -9.81F, 0.0F);
+ m_LayerCollisionMatrix.resize (kNumLayers, 0xffffffff);
+ m_DefaultIterationCount = 6;
+ GetDynamicsSDK ().setParameter (NX_SKIN_WIDTH, 0.01F);
+
+
+}
+
+PhysicsManager::~PhysicsManager ()
+{
+ CleanupReports();
+ // delete objects after setting them to null on novodex - just in case
+}
+
+inline Unity::Component* AttachedRigidbodyOrCollider (Collider& collider)
+{
+ Rigidbody* body = collider.GetRigidbody ();
+ if (body)
+ return body;
+ else
+ return &collider;
+}
+
+
+inline Unity::Component* GetRigidbodyOrCollider (Collision& col)
+{
+ return col.thisRigidbody != NULL ? (Unity::Component*)col.thisRigidbody : (Unity::Component*)col.thisCollider;
+}
+
+inline Unity::Component* GetOtherRigidbodyOrCollider (Collision& col)
+{
+ return col.otherRigidbody != NULL ? (Unity::Component*)col.otherRigidbody : (Unity::Component*)col.otherCollider;
+}
+
+void PhysicsManager::SetTransformMessageEnabled(bool enable)
+{
+ // Disable rigid body / collider transform changed message handler
+ // This simply avoids setting the position/rotation in the rigidbody while we are fetching the novodex state.
+ for (int i=0;i<m_DisableTransformMessage.size ();i++)
+ {
+ GameObject::GetMessageHandler ().SetMessageEnabled (m_DisableTransformMessage[i], kTransformChanged.messageID, enable);
+ }
+
+ m_RigidbodyTransformMessageEnabled = enable;
+}
+
+void PhysicsManager::ReleaseMaterials(vector<PhysicMaterial*>& materials, vector<NxMaterialDesc>& materialDescs)
+{
+ materials.clear();
+ Object::FindObjectsOfType(&materials);
+
+ materialDescs.resize(materials.size());
+
+ for (int i=0;i<materials.size();i++)
+ {
+ materials[i]->m_Material->saveToDesc(materialDescs[i]);
+ GetDynamicsScene ().releaseMaterial (*materials[i]->m_Material);
+ materials[i]->m_Material = NULL;
+ }
+}
+
+void PhysicsManager::RecreateScene ()
+{
+ RETURNIFNOPHYSX_VOID();
+
+ if (GetDynamicsScene().getNbActors() == 0 && GetDynamicsScene().getNbStaticShapes() == 0 && GetDynamicsScene().getNbJoints() == 0)
+ {
+ vector<PhysicMaterial*> materials;
+ vector<NxMaterialDesc> materialDescs;
+ ReleaseMaterials(materials, materialDescs);
+
+ CleanupReports();
+ for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!=gClothingScenes.end(); i++)
+ {
+ if (*i != NULL)
+ {
+ gPhysicsSDK->releaseScene (**i);
+ *i = NULL;
+ }
+ }
+ gPhysicsSDK->releaseScene (*gPhysicsScene);
+ gPhysicsScene = NULL;
+ CreateDynamicsScene ();
+ CreateReports();
+
+ for (int i=0;i<materials.size();i++)
+ {
+ NxMaterial* material = GetDynamicsScene ().createMaterial (materialDescs[i]);
+
+ if (material)
+ {
+ materials[i]->m_MaterialIndex = material->getMaterialIndex ();
+ }
+ else
+ {
+ ErrorString(std::string("Invalid physics material ") + materials[i]->GetName());
+ materials[i]->m_MaterialIndex = 0;
+ }
+
+ materials[i]->m_Material = material;
+
+ materials[i]->AwakeFromLoad (kDefaultAwakeFromLoad);
+ }
+
+ GetPhysicsManager().AwakeFromLoad(kDefaultAwakeFromLoad);
+ }
+}
+
+PROFILER_INFORMATION(gPhysicsProfile, "Physics.Simulate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysicsClothProfile, "Physics.UpdateSkinnedCloth", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysicsInterpolationProfile, "Physics.Interpolation", kProfilerPhysics)
+
+void PhysicsManager::UpdateSkinnedClothes ()
+{
+#if ENABLE_CLOTH
+ if (m_SmoothedClothDeltaTime == 0.0)
+ m_SmoothedClothDeltaTime = GetTimeManager().GetDeltaTime();
+ else
+ m_SmoothedClothDeltaTime = Lerp (m_SmoothedClothDeltaTime, GetTimeManager().GetDeltaTime(), 0.01F);
+
+ if(!gClothingScenes.size())
+ return;
+
+ PROFILER_AUTO(gPhysicsClothProfile, NULL)
+
+ Assert(m_ActiveSkinnedMeshes.empty());
+ SkinnedMeshRenderer::UpdateAllSkinnedMeshes(SkinnedMeshRenderer::kUpdateCloth, &m_ActiveSkinnedMeshes);
+
+#if ENABLE_MULTITHREADED_SKINNING
+ JobScheduler& js = GetJobScheduler();
+ m_ClothJobGroup = js.BeginGroup (gClothingScenes.size());
+#endif
+
+ for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!= gClothingScenes.end(); i++)
+ {
+ if (*i != NULL)
+ {
+ if ((*i)->getNbCloths() == 0)
+ {
+ gPhysicsSDK->releaseScene (**i);
+ (*i) = NULL;
+ }
+ else
+ {
+ #if ENABLE_MULTITHREADED_SKINNING
+ js.SubmitJob (m_ClothJobGroup, SimulateClothingScene, *i, NULL);
+ #else
+ SimulateClothingScene(*i);
+ #endif
+ }
+ }
+ }
+#endif
+}
+
+void PhysicsManager::FinishUpdatingSkinnedClothes ()
+{
+#if ENABLE_CLOTH
+#if ENABLE_MULTITHREADED_SKINNING
+ PROFILER_AUTO(gPhysicsClothProfile, NULL)
+
+ JobScheduler& js = GetJobScheduler();
+ js.WaitForGroup (m_ClothJobGroup);
+#endif
+
+ SkinnedMeshRenderer::UploadSkinnedClothes(m_ActiveSkinnedMeshes);
+ m_ActiveSkinnedMeshes.clear();
+#endif
+}
+
+void PhysicsManager::FixedUpdate ()
+{
+ PROFILER_AUTO(gPhysicsProfile, NULL)
+
+ RETURNIFNOPHYSX_VOID();
+
+ AssertIf (!m_RecordedTriggers.empty ());
+
+ // Store interpolated position
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();i++)
+ {
+ Rigidbody* body = i->body;
+ i->disabled = 0;
+ if (body->GetInterpolation() == kInterpolate)
+ {
+ i->position = body->GetPosition();
+ i->rotation = body->GetRotation();
+ }
+ }
+
+ // Force inactive scene to deallocate memory for all released actors
+ // Requires PhysX code to be patched accordingly
+ // NOTE: we don't need to call it on active physics scene since such deallocation will happen inside simulate() method
+ gInactivePhysicsScene->flushCaches();
+
+ gPhysicsScene->simulate(GetTimeManager ().GetFixedDeltaTime ());
+
+ gPhysicsScene->flushStream();
+ gPhysicsScene->fetchResults(NX_RIGID_BODY_FINISHED, true);
+
+ // Disable rigid body / collider / controller transform changed message handler
+ // This simply avoids setting the position/rotation in the rigidbody while we are fetching the novodex state.
+ SetTransformMessageEnabled (false);
+
+ // Update position / rotation of all rigid bodies
+ MessageData velocityMessageData;
+ for (int level=0;level<kMaxSortedActorsDepth;level++)
+ {
+ RigidbodyList& bodies = m_SortedActors[level];
+ for (RigidbodyList::iterator i=bodies.begin();i != bodies.end();i++)
+ {
+ Rigidbody& body = **i;
+ NxActor* actor = body.m_Actor;
+
+ if (actor->isSleeping ())
+ continue;
+
+ if (body.m_DisableReadUpdateTransform == 0)
+ {
+ GameObject& go = body.GetGameObject();
+ Transform& transform = go.GetComponent (Transform);
+ NxVec3 pos = actor->getGlobalPosition ();
+ NxQuat rot = actor->getGlobalOrientationQuat ();
+
+ /// @TODO: DONT NORMALIZE. NOVODEX VALUES ARE PRETTY CLOSE TO UNIT.
+ /// INSTEAD MAKE OUR NORMALIZE ASSERT CHECKS MORE RELAXED! LOTS OF WORK!
+ transform.SetPositionAndRotationSafe ((const Vector3f&)pos, (const Quaternionf&)rot);
+
+
+ // Synchronize velocity with other components eg. NavMesh
+ if (go.GetSupportedMessages() & kSupportsVelocityChanged)
+ {
+ Vector3f velocity = Vec3FromNx(actor->getLinearVelocity ());
+ velocityMessageData.SetData(&velocity, ClassID(Vector3f));
+
+ go.SendMessageAny(kDidVelocityChange, velocityMessageData);
+ }
+ }
+ }
+ }
+
+ // Enable rigid body transform changed message handler
+ SetTransformMessageEnabled (true);
+
+ ProcessRecordedReports();
+}
+
+void PhysicsManager::ResetInterpolatedTransformPosition ()
+{
+ PROFILER_AUTO(gPhysicsInterpolationProfile, NULL)
+
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();i++)
+ {
+ Rigidbody* body = i->body;
+ if (body->IsSleeping())
+ continue;
+
+ Transform& transform = body->GetComponent(Transform);
+
+ Vector3f pos = body->GetPosition();
+ Quaternionf rot = body->GetRotation();
+ transform.SetPositionAndRotationSafeWithoutNotification (pos, rot);
+ }
+}
+
+#if ENABLE_PROFILER
+void PhysicsManager::GetPerformanceStats(PhysicsStats& physicsStats)
+{
+ NxSceneStats stats;
+ GetDynamicsScene().getStats(stats);
+
+ physicsStats.activeRigidbodies = stats.numDynamicActorsInAwakeGroups;
+ physicsStats.sleepingRigidbodies = stats.numDynamicActors - stats.numDynamicActorsInAwakeGroups;
+
+ physicsStats.numberOfShapePairs = stats.numPairs;
+
+ physicsStats.numberOfStaticColliders = stats.numStaticShapes;
+ physicsStats.numberOfDynamicColliders = stats.numDynamicShapes;
+}
+#endif
+
+void PhysicsManager::SkinnedClothUpdate ()
+{
+ UpdateSkinnedClothes();
+ FinishUpdatingSkinnedClothes ();
+}
+
+void *PhysicsManager::SimulateClothingScene (void *voidScene)
+{
+#if ENABLE_CLOTH
+ NxScene *scene = (NxScene*)voidScene;
+
+ // For correct simulation, cloth should be updated in Fixed Update.
+ // However, dynamic update tends to behave better for performance - as cloth is a purely visible effect, we care more about
+ // performance, then about accuracy.
+ // The problem is that physX seems to apply external forces stronger when using a longer delta time for the simulation,
+ // resulting in cloth hanging down further at slower frame rates, because gravity is stronger. This can be fixed by adjusting
+ // the gravity and external forces accordingly.
+
+ // Also, we don't use the actual dynamic delta time, but a highly smoothed version of it.
+ // Otherwise, the changes in frame rate cause the cloth to jitter.
+ // This will make the cloth simulate to fast or too slow for a seconds or so, when the frame rate changes, resulting in
+ // the appearance of more or less damping. While this is technically incorrect, it likely won't be notced, and seems to be better
+ // then the alternative of running in fixed time.
+
+ float timeScale = GetTimeManager().GetFixedDeltaTime() / GetPhysicsManager().m_SmoothedClothDeltaTime;
+ Vector3f adjustedGravity = GetPhysicsManager().m_Gravity*timeScale;
+ scene->setGravity ((NxVec3&)adjustedGravity);
+
+ // same adjustment for external forces
+ int numCloths = scene->getNbCloths();
+ NxCloth **cloths = scene->getCloths();
+ for (int j=0; j<numCloths; j++)
+ cloths[j]->setExternalAcceleration(cloths[j]->getExternalAcceleration() * timeScale);
+
+ scene->simulate (GetPhysicsManager().m_SmoothedClothDeltaTime);
+ scene->flushStream();
+ scene->fetchResults(NX_RIGID_BODY_FINISHED, true);
+
+ for (int j=0; j<numCloths; j++)
+ ((SkinnedCloth*)(cloths[j]->userData))->ReadBackSkinnedBuffers();
+#endif
+ return NULL;
+}
+
+NxScene* PhysicsManager::GetClothingScene ()
+{
+#if ENABLE_CLOTH
+ int minVertices = std::numeric_limits<int>::max();
+ std::vector<NxScene*>::iterator found = gClothingScenes.begin();
+ for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!= gClothingScenes.end(); i++)
+ {
+ if ((*i) == NULL)
+ {
+ NxSceneDesc sceneDesc;
+ // after simulating the cloth, we still need to transform the results, which can also be threaded.
+ // so, we use our own threading wrapped around PhysX. No need to enable this, and upper comment suggests
+ // it has issues.
+ sceneDesc.flags &= ~NX_SF_SIMULATE_SEPARATE_THREAD;
+ // Create the scene if it doesn't exist
+ (*i) = gPhysicsSDK->createScene(sceneDesc);
+ (*i)->setGravity (NxVec3 (0.0F, -9.81F, 0.0F));
+ (*i)->setTiming (1.0F, 8, NX_TIMESTEP_VARIABLE);
+#if UNITY_PS3
+ NxCellConfig::setSceneParamInt((*i),NxCellConfig::NX_CELL_SCENE_PARAM_SPU_CLOTH, 0);
+#endif
+ }
+
+ int numVertices = 0;
+ int numCloths = (**i).getNbCloths();
+ NxCloth **cloths = (**i).getCloths();
+ for (int j=0; j<numCloths; j++)
+ numVertices += cloths[j]->getNumberOfParticles();
+ if (numVertices < minVertices)
+ {
+ found = i;
+ minVertices = numVertices;
+ }
+ // No need to look further - this one is empty.
+ if (numVertices == 0)
+ return *found;
+ }
+ return *found;
+#else
+ return NULL;
+#endif
+}
+
+void PhysicsManager::Update ()
+{
+ PROFILER_AUTO(gPhysicsInterpolationProfile, NULL)
+
+ SetTransformMessageEnabled (false);
+
+ // Also disable rigidbody transform changed message, otherwise interpolation will affect physics results.
+ // This is not done in SetTransformMessageEnabled, as we need the rigidbody messages in the physics fixed update
+ // so kinematic child rigidbodies are moved with their parents.
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody), kTransformChanged.messageID, false);
+
+ // Interpolation time is [0...1] between the two steps
+ // Extrapolation time the delta time since the last fixed step
+ float dynamicTime = GetTimeManager().GetCurTime();
+ float step = GetTimeManager().GetFixedDeltaTime();
+ float fixedTime = GetTimeManager().GetFixedTime();
+ float interpolationTime = clamp01 ((dynamicTime - fixedTime) / step);
+ float extrapolationTime = dynamicTime - fixedTime;
+// AssertIf (t < 0.0F || t > 1.0F);
+
+ // Update interpolated position
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();i++)
+ {
+ Rigidbody* body = i->body;
+ if (i->disabled || body->IsSleeping())
+ continue;
+
+ Transform& transform = body->GetComponent(Transform);
+
+ Quaternionf rot;
+ Vector3f pos;
+ RigidbodyInterpolation interpolation = body->GetInterpolation();
+ // Interpolate between this physics and last physics frame
+ if (interpolation == kInterpolate)
+ {
+ pos = Lerp(i->position, body->GetPosition(), interpolationTime);
+ rot = Slerp(i->rotation, body->GetRotation(), interpolationTime);
+ transform.SetPositionAndRotationSafe (pos, rot);
+ }
+ // Extrapolate current position using velocity
+ else if (interpolation == kExtrapolate)
+ {
+ pos = body->GetPosition() + body->GetVelocity() * extrapolationTime;
+ rot = AngularVelocityToQuaternion(body->GetAngularVelocity(), extrapolationTime) * body->GetRotation();
+ transform.SetPositionAndRotationSafe (pos, rot);
+ }
+ }
+ SetTransformMessageEnabled (true);
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody), kTransformChanged.messageID, true);
+}
+
+template<class TransferFunction>
+void PhysicsManager::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER_SIMPLE (m_Gravity);
+ TRANSFER_SIMPLE (m_DefaultMaterial);
+ TRANSFER_PROPERTY (float, m_BounceThreshold, GetBounceThreshold, SetBounceThreshold);
+ TRANSFER_PROPERTY (float, m_SleepVelocity, GetSleepVelocity, SetSleepVelocity);
+ TRANSFER_PROPERTY (float, m_SleepAngularVelocity, GetSleepAngularVelocity, SetSleepAngularVelocity);
+ TRANSFER_PROPERTY (float, m_MaxAngularVelocity, GetMaxAngularVelocity, SetMaxAngularVelocity);
+ TRANSFER_PROPERTY (float, m_MinPenetrationForPenalty, GetMinPenetrationForPenalty, SetMinPenetrationForPenalty);
+ TRANSFER_PROPERTY (int, m_SolverIterationCount, GetSolverIterationCount, SetSolverIterationCount);
+ TRANSFER (m_RaycastsHitTriggers);
+ transfer.Align();
+ transfer.Transfer (m_LayerCollisionMatrix, "m_LayerCollisionMatrix", kHideInEditorMask);
+}
+
+#if ENABLE_CLUSTER_SYNC
+template<class TransferFunction>
+void PhysicsManager::ClusterTransfer(TransferFunction& transfer)
+{
+ TRANSFER(m_SmoothedClothDeltaTime);
+}
+#endif
+
+void PhysicsManager::SetupDefaultMaterial ()
+{
+ m_CachedDefaultMaterial = m_DefaultMaterial;
+ if (m_CachedDefaultMaterial)
+ m_CachedDefaultMaterial->CopyMaterialToDefault ();
+ else
+ {
+ NxMaterialDesc material;
+ material.dynamicFriction = 0.6F;
+ material.staticFriction = 0.6F;
+ GetDynamicsScene ().getMaterialFromIndex (0)->loadFromDesc (material);
+ }
+}
+
+void PhysicsManager::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsScene ().setGravity((const NxVec3&)m_Gravity);
+ SetupCollisionLayerMatrix ();
+
+ /// FIXME: We need to check this since when we load from disk we don't have a physicsmanager setup yet.
+ if (GetManagerPtrFromContext (ManagerContext::kPhysicsManager))
+ {
+ SetupDefaultMaterial ();
+ }
+}
+
+NxActor* PhysicsManager::GetNULLActor ()
+{
+ if (gNULLActor)
+ return gNULLActor;
+
+ NxBodyDesc bodyDesc;
+ NxActorDesc actorDesc;
+ bodyDesc.massSpaceInertia = NxVec3 (1.0F, 1.0F, 1.0F);
+ bodyDesc.mass = 1.0F;
+ actorDesc.body = &bodyDesc;
+
+ gNULLActor = GetInactiveDynamicsScene ().createActor (actorDesc);
+ return gNULLActor;
+}
+
+
+Vector3f PhysicsManager::GetGravity ()
+{
+ return m_Gravity;
+}
+
+void PhysicsManager::WakeUpScene ()
+{
+ NxU32 nbActors = GetDynamicsScene().getNbActors();
+ NxActor** actors = GetDynamicsScene().getActors();
+ for (int i =0; i<nbActors; i++)
+ {
+ if (actors[i]->isDynamic())
+ actors[i]->wakeUp();
+ }
+}
+
+void PhysicsManager::SetGravity (const Vector3f& value)
+{
+ if (m_Gravity != value)
+ {
+ m_Gravity = value;
+ GetDynamicsScene ().setGravity ((const NxVec3&)value);
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1))
+ WakeUpScene ();
+ SetDirty ();
+ }
+}
+
+void PhysicsManager::SetMinPenetrationForPenalty (float value)
+{
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsSDK ().setParameter (NX_SKIN_WIDTH, value);
+ SetDirty ();
+}
+
+float PhysicsManager::GetMinPenetrationForPenalty ()
+{
+ RETURNIFNOPHYSX(1.0f);
+ return GetDynamicsSDK ().getParameter (NX_SKIN_WIDTH);
+}
+
+void PhysicsManager::SetBounceThreshold (float value)
+{
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsSDK ().setParameter (NX_BOUNCE_THRESHOLD, -value);
+ SetDirty ();
+}
+
+float PhysicsManager::GetBounceThreshold ()
+{
+ return -GetDynamicsSDK ().getParameter (NX_BOUNCE_THRESHOLD);
+}
+
+void PhysicsManager::SetSleepVelocity (float value)
+{
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsSDK ().setParameter (NX_DEFAULT_SLEEP_LIN_VEL_SQUARED, value * value);
+ SetDirty ();
+}
+
+float PhysicsManager::GetSleepVelocity ()
+{
+ return Sqrt (GetDynamicsSDK ().getParameter (NX_DEFAULT_SLEEP_LIN_VEL_SQUARED));
+}
+
+void PhysicsManager::SetSleepAngularVelocity (float value)
+{
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsSDK ().setParameter (NX_DEFAULT_SLEEP_ANG_VEL_SQUARED, value * value);
+ SetDirty ();
+}
+
+float PhysicsManager::GetSleepAngularVelocity ()
+{
+ return Sqrt (GetDynamicsSDK ().getParameter (NX_DEFAULT_SLEEP_ANG_VEL_SQUARED));
+}
+
+void PhysicsManager::SetMaxAngularVelocity (float value)
+{
+ RETURNIFNOPHYSX_VOID();
+ GetDynamicsSDK ().setParameter (NX_MAX_ANGULAR_VELOCITY, value);
+ SetDirty ();
+}
+
+float PhysicsManager::GetMaxAngularVelocity ()
+{
+ return GetDynamicsSDK ().getParameter (NX_MAX_ANGULAR_VELOCITY);
+}
+
+void PhysicsManager::SetSolverIterationCount (int value)
+{
+ RETURNIFNOPHYSX_VOID();
+ m_DefaultIterationCount = clamp(value, 1, 100);
+ SetDirty ();
+}
+
+class CollideShapesReport : public NxUserEntityReport<NxShape*>
+{
+public:
+
+ NxSphere sphere;
+ bool checkShapes;
+ PhysicsManager::ColliderCache& cache;
+ CollideShapesReport (PhysicsManager::ColliderCache& c) : cache (c) {}
+
+ virtual bool onEvent(NxU32 nbEntities, NxShape** entities)
+ {
+ int offset = cache.size ();
+ cache.reserve (offset + nbEntities);
+ for (int i=0;i<nbEntities;i++)
+ {
+ if (entities[i]->checkOverlapSphere (sphere))
+ cache.push_back ((Collider*)entities[i]->userData);
+ }
+ return true;
+ }
+};
+
+PhysicsManager::ColliderCache& PhysicsManager::OverlapSphere (const Vector3f& p, float radius, int mask)
+{
+ PROFILER_AUTO(gSphereOverlapProfile, NULL)
+ m_ColliderCache.clear ();
+ CollideShapesReport report (m_ColliderCache);
+ report.sphere = NxSphere ((NxVec3&)p, radius);
+ report.checkShapes = true;
+
+ GetDynamicsScene ().overlapSphereShapes (report.sphere, NX_ALL_SHAPES, 0, NULL, &report, mask);
+ return m_ColliderCache;
+}
+
+bool PhysicsManager::SphereTest (const Vector3f& p, float radius, int mask)
+{
+ PROFILER_AUTO(gSphereTestProfile, NULL)
+ NxSphere sphere((NxVec3&)p, radius);
+ return GetDynamicsScene ().checkOverlapSphere (sphere, NX_ALL_SHAPES, mask);
+}
+
+
+bool PhysicsManager::RaycastTest (const Ray& ray, float distance, int mask)
+{
+ PROFILER_AUTO(gRaycastProfile, NULL)
+
+ AssertIf (!IsNormalized (ray.GetDirection ()));
+ if (distance == std::numeric_limits<float>::infinity())
+ distance = NX_MAX_F32;
+ return GetDynamicsScene ().raycastAnyShape ((NxRay&)ray, NX_ALL_SHAPES, mask, distance);
+}
+
+bool PhysicsManager::Raycast (const Ray& ray, float distance, RaycastHit& outHit, int mask)
+{
+ AssertIf (!IsNormalized (ray.GetDirection ()));
+ PROFILER_AUTO(gRaycastProfile, NULL)
+
+ if (distance == std::numeric_limits<float>::infinity())
+ distance = NX_MAX_F32;
+
+ NxRaycastHit hit;
+ NxShape* shape = GetDynamicsScene ().raycastClosestShape ((NxRay&)ray, NX_ALL_SHAPES, hit, mask, distance);
+ if (shape)
+ {
+ NxToRaycastHit(hit, outHit);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool PhysicsManager::CapsuleCast (const Vector3f &p0, const Vector3f &p1, float radius, const Vector3f &direction, float distance, RaycastHit& outHit, int mask)
+{
+ AssertIf (!IsNormalized (direction));
+ PROFILER_AUTO(gCapsuleCastProfile, NULL)
+
+ if (distance == std::numeric_limits<float>::infinity())
+ // CapsuleCasts fail when using NX_MAX_F32 here.
+ // So pick a lower "high" number instead.
+ distance = 1000000.0f;
+
+ NxCapsule testCapsule;
+ testCapsule.radius = radius;
+ testCapsule.p0 = (const NxVec3&)p0;
+ testCapsule.p1 = (const NxVec3&)p1;
+
+ NxSweepQueryHit hit;
+ NxU32 nb = GetDynamicsScene ().linearCapsuleSweep (testCapsule, (const NxVec3&)direction * distance, NX_SF_DYNAMICS|NX_SF_STATICS, NULL, 1, &hit, NULL, mask);
+ if (nb)
+ {
+ NxToRaycastHit(hit, distance, outHit);
+ return true;
+ }
+ else
+ return false;
+}
+
+void NxToRaycastHit (const NxRaycastHit& hit, RaycastHit& outHit)
+{
+ outHit.collider = (Collider*)hit.shape->userData;
+ outHit.point = (const Vector3f&)hit.worldImpact;
+ outHit.normal = (const Vector3f&)hit.worldNormal;
+ outHit.faceID = hit.faceID;
+ outHit.distance = hit.distance;
+ outHit.uv.x = hit.u;
+ outHit.uv.y = hit.v;
+}
+
+void NxToRaycastHit (const NxSweepQueryHit& hit, float sweepDistance, RaycastHit& outHit)
+{
+ outHit.collider = (Collider*)hit.hitShape->userData;
+ outHit.point = (const Vector3f&)hit.point;
+ outHit.normal = (const Vector3f&)hit.normal;
+ outHit.faceID = hit.faceID;
+ outHit.distance = hit.t * sweepDistance;
+ outHit.uv.x = 0;
+ outHit.uv.y = 0;
+}
+
+bool PhysicsManager::CapsuleTest (Vector3f start, Vector3f end, float radius, int mask)
+{
+ PROFILER_AUTO(gCapsuleTestProfile, NULL)
+ NxCapsule capsule;
+ capsule.p0 = Vec3ToNx(start);
+ capsule.p1 = Vec3ToNx(end);
+ capsule.radius = radius;
+
+ return GetDynamicsScene ().checkOverlapCapsule (capsule, NX_ALL_SHAPES, mask);
+}
+
+
+const PhysicsManager::RaycastHits& PhysicsManager::RaycastAll (const Ray& ray, float distance, int mask)
+{
+ AssertIf (!IsNormalized (ray.GetDirection ()));
+ PROFILER_AUTO(gRaycastAllProfile, NULL)
+
+ if (distance == std::numeric_limits<float>::infinity())
+ distance = NX_MAX_F32;
+
+ static vector<RaycastHit> hits;
+ hits.resize(0);
+ RaycastCollector collector;
+ collector.hits = &hits;
+
+ GetDynamicsScene ().raycastAllShapes ((NxRay&)ray, collector, NX_ALL_SHAPES, mask, distance);
+
+ return hits;
+}
+
+#define kCapsuleCastMaxHits 128
+const PhysicsManager::RaycastHits& PhysicsManager::CapsuleCastAll (const Vector3f &p0, const Vector3f &p1, float radius, const Vector3f &direction, float distance, int mask)
+{
+ AssertIf (!IsNormalized (direction));
+ PROFILER_AUTO(gCapsuleCastAllProfile, NULL)
+
+ if (distance == std::numeric_limits<float>::infinity())
+ // CapsuleCasts fail when using NX_MAX_F32 here.
+ // So pick a lower "high" number instead.
+ distance = 1000000.0f;
+
+ static vector<RaycastHit> outHits;
+
+ NxCapsule testCapsule;
+ testCapsule.radius = radius;
+ testCapsule.p0 = (const NxVec3&)p0;
+ testCapsule.p1 = (const NxVec3&)p1;
+
+ NxSweepQueryHit hits[kCapsuleCastMaxHits];
+ NxU32 nb = GetDynamicsScene ().linearCapsuleSweep (testCapsule, (const NxVec3&)direction * distance, NX_SF_DYNAMICS|NX_SF_STATICS|NX_SF_ALL_HITS, NULL, kCapsuleCastMaxHits, hits, NULL, mask);
+
+ outHits.resize(nb);
+ for (int i=0; i<nb; i++)
+ NxToRaycastHit(hits[i], distance, outHits[i]);
+
+ return outHits;
+}
+
+void PhysicsManager::IgnoreCollision (Collider& lhs, Collider& rhs, bool ignore)
+{
+ if (lhs.m_Shape == NULL || rhs.m_Shape == NULL)
+ {
+ ErrorString ("Ignore collision failed. Both colliders need to be activated when calling this IgnoreCollision");
+ return;
+ }
+
+ if (ignore)
+ GetDynamicsScene().setShapePairFlags(*lhs.m_Shape, *rhs.m_Shape, NX_IGNORE_PAIR);
+ else
+ GetDynamicsScene().setShapePairFlags(*lhs.m_Shape, *rhs.m_Shape, 0);
+}
+
+void PhysicsManager::IgnoreCollision(int layer1, int layer2, bool ignore)
+{
+ if (layer1 >= kNumLayers || layer2 >= kNumLayers)
+ {
+ ErrorString(Format("layer numbers must be between 0 and %d", kNumLayers));
+ return;
+ }
+
+ Assert (kNumLayers <= m_LayerCollisionMatrix.size());
+ Assert (kNumLayers <= sizeof(m_LayerCollisionMatrix[0])*8);
+
+ GetDynamicsScene ().setGroupCollisionFlag(layer1, layer2, !ignore);
+ if (ignore)
+ {
+ m_LayerCollisionMatrix[layer1] &= ~(1<<layer2);
+ m_LayerCollisionMatrix[layer2] &= ~(1<<layer1);
+ }
+ else
+ {
+ m_LayerCollisionMatrix[layer1] |= 1<<layer2;
+ m_LayerCollisionMatrix[layer2] |= 1<<layer1;
+ }
+ SetDirty();
+}
+
+bool PhysicsManager::GetIgnoreCollision(int layer1, int layer2)
+{
+ if (layer1 >= kNumLayers || layer2 >= kNumLayers)
+ {
+ ErrorString(Format("layer numbers must be between 0 and %d", kNumLayers));
+ return false;
+ }
+
+ return !GetDynamicsScene ().getGroupCollisionFlag(layer1, layer2);
+}
+
+void PhysicsManager::SetupCollisionLayerMatrix()
+{
+ for (int i=0;i<kNumLayers;i++)
+ {
+ for (int j=0;j<kNumLayers;j++)
+ {
+ if (i <= j)
+ {
+ bool enabled = m_LayerCollisionMatrix[i] & (1<<j);
+ GetDynamicsScene().setGroupCollisionFlag(i, j, enabled);
+ }
+ }
+ }
+}
+
+
+void PhysicsManager::AddBody(int depth, ListNode<Rigidbody>& node)
+{
+ depth = min(kMaxSortedActorsDepth-1, depth);
+ if (depth >= kMaxSortedActorsDepth-1)
+ {
+ ErrorString("Too deep hierarchy to perform rigidbody ordering. Nested rigidbodies might look strange");
+ depth = kMaxSortedActorsDepth-1;
+ }
+ m_SortedActors[depth].push_back(node);
+}
+
+NxScene& GetDynamicsScene ()
+{
+ AssertIf (gPhysicsScene == NULL);
+ return *gPhysicsScene;
+}
+
+NxScene& GetInactiveDynamicsScene ()
+{
+ AssertIf (gInactivePhysicsScene == NULL);
+ return *gInactivePhysicsScene;
+}
+
+
+NxPhysicsSDK& GetDynamicsSDK ()
+{
+ AssertIf (gPhysicsSDK == NULL);
+ return *gPhysicsSDK;
+}
+
+void PhysicsManager::ProcessRecordedReports()
+{
+ SetDisableImmediateDestruction(true);
+
+ /// Report recorded triggers.
+ /// We can't call the recorded triggers immediately because novodex still keeps a write lock at that time.
+ /// So we can't do much with physics at that time.
+ for (int i=0;i<m_RecordedTriggers.size ();i++)
+ {
+ RecordedTrigger& trigger = m_RecordedTriggers[i];
+
+ VerifyObjectPtr(trigger.collider);
+ VerifyObjectPtr(trigger.trigger);
+
+ Unity::Component* masterOfCollider = AttachedRigidbodyOrCollider (*trigger.collider);
+ Unity::Component* masterOfTrigger = AttachedRigidbodyOrCollider (*trigger.trigger);
+ MessageIdentifier messageID;
+ if (trigger.status == NX_TRIGGER_ON_ENTER)
+ messageID = kEnterTrigger;
+ else if (trigger.status == NX_TRIGGER_ON_LEAVE)
+ messageID = kExitTrigger;
+ else
+ messageID = kStayTrigger;
+
+ trigger.trigger->SendMessage (messageID, trigger.collider, ClassID (Collider));
+ masterOfCollider->SendMessage (messageID, trigger.trigger, ClassID (Collider));
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1))
+ {
+ if (masterOfCollider->GetGameObjectPtr() != trigger.collider->GetGameObjectPtr() && trigger.collider->GetIsTrigger())
+ trigger.collider->SendMessage (messageID, trigger.trigger, ClassID (Collider));
+ if (masterOfTrigger->GetGameObjectPtr() != trigger.trigger->GetGameObjectPtr())
+ masterOfTrigger->SendMessage (messageID, trigger.collider, ClassID (Collider));
+ }
+ }
+ m_RecordedTriggers.clear ();
+
+ /// Report recorded contact events.
+ for (int i=0;i<m_RecordedContacts.size ();i++)
+ {
+ Collision& contact = m_RecordedContacts[i];
+ VerifyObjectPtr(contact.thisRigidbody);
+ VerifyObjectPtr(contact.otherRigidbody);
+ VerifyObjectPtr(contact.thisCollider);
+ VerifyObjectPtr(contact.otherCollider);
+
+ if (contact.status & NX_NOTIFY_ON_START_TOUCH)
+ {
+ contact.flipped = false;
+ GetRigidbodyOrCollider(contact)->SendMessage (kEnterContact, &contact, ClassID (Collision));
+ contact.flipped = true;
+ GetOtherRigidbodyOrCollider(contact)->SendMessage (kEnterContact, &contact, ClassID (Collision));
+ }
+ if (contact.status & NX_NOTIFY_ON_END_TOUCH)
+ {
+ contact.flipped = false;
+ GetRigidbodyOrCollider(contact)->SendMessage (kExitContact, &contact, ClassID (Collision));
+ contact.flipped = true;
+ GetOtherRigidbodyOrCollider(contact)->SendMessage (kExitContact, &contact, ClassID (Collision));
+ }
+ if (contact.status & NX_NOTIFY_ON_TOUCH)
+ {
+ contact.flipped = false;
+ GetRigidbodyOrCollider(contact)->SendMessage (kStayContact, &contact, ClassID (Collision));
+ contact.flipped = true;
+ GetOtherRigidbodyOrCollider(contact)->SendMessage (kStayContact, &contact, ClassID (Collision));
+ }
+ }
+ m_RecordedContacts.clear ();
+
+ /// Report recorded joint breaks
+ for (int i=0;i<m_RecordedJointBreaks.size ();i++)
+ {
+ RecordedJointBreak& record = m_RecordedJointBreaks[i];
+ Joint* joint = record.joint;
+ if (joint && joint->IsActive())
+ {
+ joint->GetGameObject().SendMessage (kJointBreak, record.impulse, ClassID (float));
+ }
+ joint = record.joint;
+ if (joint && joint->GetGameObjectPtr())
+ {
+ SetDisableImmediateDestruction(false);
+ DestroyObjectHighLevel(joint, true);
+ SetDisableImmediateDestruction(true);
+ }
+ }
+ m_RecordedJointBreaks.clear();
+
+ SetDisableImmediateDestruction(false);
+}
+
+// When using raycasts or other physics functionality in edit mode (such as is done when generating terrain lightmaps),
+// this needs to be called to make sure all updates to colliders in the scene are taken into account.
+void PhysicsManager::RefreshWhenPaused()
+{
+ gPhysicsScene->simulate(0);
+ gPhysicsScene->flushCaches();
+ gPhysicsScene->flushStream();
+ gPhysicsScene->fetchResults(NX_RIGID_BODY_FINISHED, true);
+ ProcessRecordedReports();
+}
+
+#if ENABLE_SCRIPTING
+ScriptingArrayPtr ConvertNativeRaycastHitsToManaged(const PhysicsManager::RaycastHits& hits)
+{
+ ScriptingArrayPtr arr = CreateScriptingArray(hits.size() > 0 ? &hits[0] : NULL, hits.size(), GetScriptingManager().GetCommonClasses().raycastHit);
+ RaycastHit* firstElement = Scripting::GetScriptingArrayStart<RaycastHit>(arr);
+ for (int i=0;i<hits.size();i++)
+ {
+ firstElement[i].collider = reinterpret_cast<Collider*>(ScriptingGetObjectReference (firstElement[i].collider));
+ }
+ return arr;
+}
+#endif
+
+GET_MANAGER (PhysicsManager)
+GET_MANAGER_PTR (PhysicsManager)
+IMPLEMENT_CLASS_HAS_INIT (PhysicsManager)
+IMPLEMENT_OBJECT_SERIALIZE (PhysicsManager)
+IMPLEMENT_CLUSTER_SERIALIZE (PhysicsManager)
+#endif //ENABLE_PHYSICS