1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "BsPhysX.h"
4#include "PxPhysicsAPI.h"
5#include "BsPhysXMaterial.h"
6#include "BsPhysXMesh.h"
7#include "BsPhysXRigidbody.h"
8#include "BsPhysXBoxCollider.h"
9#include "BsPhysXSphereCollider.h"
10#include "BsPhysXPlaneCollider.h"
11#include "BsPhysXCapsuleCollider.h"
12#include "BsPhysXMeshCollider.h"
13#include "BsPhysXFixedJoint.h"
14#include "BsPhysXDistanceJoint.h"
15#include "BsPhysXHingeJoint.h"
16#include "BsPhysXSphericalJoint.h"
17#include "BsPhysXSliderJoint.h"
18#include "BsPhysXD6Joint.h"
19#include "BsPhysXCharacterController.h"
20#include "Threading/BsTaskScheduler.h"
21#include "Components/BsCCollider.h"
22#include "BsFPhysXCollider.h"
23#include "Utility/BsTime.h"
24#include "Math/BsVector3.h"
25#include "Math/BsAABox.h"
26#include "Math/BsCapsule.h"
27#include "foundation/PxTransform.h"
28
29using namespace physx;
30
31namespace bs
32{
33 class PhysXAllocator : public PxAllocatorCallback
34 {
35 public:
36 void* allocate(size_t size, const char*, const char*, int) override
37 {
38 void* ptr = bs_alloc_aligned16((UINT32)size);
39 PX_ASSERT((reinterpret_cast<size_t>(ptr) & 15) == 0);
40 return ptr;
41 }
42
43 void deallocate(void* ptr) override
44 {
45 bs_free_aligned16(ptr);
46 }
47 };
48
49 class PhysXErrorCallback : public PxErrorCallback
50 {
51 public:
52 void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) override
53 {
54 const char* errorCode = nullptr;
55
56 UINT32 severity = 0;
57
58 if ((code & PxErrorCode::eDEBUG_INFO) != 0)
59 {
60 errorCode = "Info";
61 severity = 0;
62 }
63
64 if((code & PxErrorCode::eINVALID_PARAMETER) != 0)
65 {
66 errorCode = "Invalid parameter";
67 severity = 1;
68 }
69
70 if ((code & PxErrorCode::eINVALID_OPERATION) != 0)
71 {
72 errorCode = "Invalid operation";
73 severity = 1;
74 }
75
76 if ((code & PxErrorCode::eDEBUG_WARNING) != 0)
77 {
78 errorCode = "Generic";
79 severity = 1;
80 }
81
82 if ((code & PxErrorCode::ePERF_WARNING) != 0)
83 {
84 errorCode = "Performance";
85 severity = 1;
86 }
87
88 if ((code & PxErrorCode::eOUT_OF_MEMORY) != 0)
89 {
90 errorCode = "Out of memory";
91 severity = 2;
92 }
93
94 if ((code & PxErrorCode::eABORT) != 0)
95 {
96 errorCode = "Abort";
97 severity = 2;
98 }
99
100 if ((code & PxErrorCode::eINTERNAL_ERROR) != 0)
101 {
102 errorCode = "Internal";
103 severity = 2;
104 }
105
106 StringStream ss;
107
108 switch(severity)
109 {
110 case 0:
111 ss << "PhysX info (" << errorCode << "): " << message << " at " << file << ":" << line;
112 LOGDBG(ss.str());
113 break;
114 case 1:
115 ss << "PhysX warning (" << errorCode << "): " << message << " at " << file << ":" << line;
116 LOGWRN(ss.str());
117 break;
118 case 2:
119 ss << "PhysX error (" << errorCode << "): " << message << " at " << file << ":" << line;
120 LOGERR(ss.str());
121 BS_ASSERT(false); // Halt execution on debug builds when error occurs
122 break;
123 }
124 }
125 };
126
127 class PhysXEventCallback : public PxSimulationEventCallback
128 {
129 void onWake(PxActor** actors, PxU32 count) override { /* Do nothing */ }
130 void onSleep(PxActor** actors, PxU32 count) override { /* Do nothing */ }
131
132 void onTrigger(PxTriggerPair* pairs, PxU32 count) override
133 {
134 for (PxU32 i = 0; i < count; i++)
135 {
136 const PxTriggerPair& pair = pairs[i];
137 if (pair.triggerShape->userData == nullptr)
138 continue;
139
140 PhysX::ContactEventType type;
141 bool ignoreContact = false;
142 PhysXObjectFilterFlags flags = PhysXObjectFilterFlags(pair.triggerShape->getSimulationFilterData().word2);
143
144 if (flags.isSet(PhysXObjectFilterFlag::ReportAll))
145 {
146 switch ((UINT32)pair.status)
147 {
148 case PxPairFlag::eNOTIFY_TOUCH_FOUND:
149 type = PhysX::ContactEventType::ContactBegin;
150 break;
151 case PxPairFlag::eNOTIFY_TOUCH_PERSISTS:
152 type = PhysX::ContactEventType::ContactStay;
153 break;
154 case PxPairFlag::eNOTIFY_TOUCH_LOST:
155 type = PhysX::ContactEventType::ContactEnd;
156 break;
157 default:
158 ignoreContact = true;
159 break;
160 }
161 }
162 else if (flags.isSet(PhysXObjectFilterFlag::ReportBasic))
163 {
164 switch ((UINT32)pair.status)
165 {
166 case PxPairFlag::eNOTIFY_TOUCH_FOUND:
167 type = PhysX::ContactEventType::ContactBegin;
168 break;
169 case PxPairFlag::eNOTIFY_TOUCH_LOST:
170 type = PhysX::ContactEventType::ContactEnd;
171 break;
172 default:
173 ignoreContact = true;
174 break;
175 }
176 }
177 else
178 ignoreContact = true;
179
180 if (ignoreContact)
181 continue;
182
183 PhysX::TriggerEvent event;
184 event.trigger = (Collider*)pair.triggerShape->userData;
185 event.other = (Collider*)pair.otherShape->userData;
186 event.type = type;
187
188 gPhysX()._reportTriggerEvent(event);
189 }
190 }
191
192 void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 count) override
193 {
194 for (PxU32 i = 0; i < count; i++)
195 {
196 const PxContactPair& pair = pairs[i];
197
198 PhysX::ContactEventType type;
199 bool ignoreContact = false;
200 switch((UINT32)pair.events)
201 {
202 case PxPairFlag::eNOTIFY_TOUCH_FOUND:
203 type = PhysX::ContactEventType::ContactBegin;
204 break;
205 case PxPairFlag::eNOTIFY_TOUCH_PERSISTS:
206 type = PhysX::ContactEventType::ContactStay;
207 break;
208 case PxPairFlag::eNOTIFY_TOUCH_LOST:
209 type = PhysX::ContactEventType::ContactEnd;
210 break;
211 default:
212 ignoreContact = true;
213 break;
214 }
215
216 if (ignoreContact)
217 continue;
218
219 PhysX::ContactEvent event;
220 event.type = type;
221
222 PxU32 contactCount = pair.contactCount;
223 const PxU8* stream = pair.contactStream;
224 PxU16 streamSize = pair.contactStreamSize;
225
226 if (contactCount > 0 && streamSize > 0)
227 {
228 PxU32 contactIdx = 0;
229 PxContactStreamIterator iter((PxU8*)stream, streamSize);
230
231 stream += ((streamSize + 15) & ~15);
232
233 const PxReal* impulses = reinterpret_cast<const PxReal*>(stream);
234 PxU32 hasImpulses = (pair.flags & PxContactPairFlag::eINTERNAL_HAS_IMPULSES);
235
236 while (iter.hasNextPatch())
237 {
238 iter.nextPatch();
239 while (iter.hasNextContact())
240 {
241 iter.nextContact();
242
243 ContactPoint point;
244 point.position = fromPxVector(iter.getContactPoint());
245 point.separation = iter.getSeparation();
246 point.normal = fromPxVector(iter.getContactNormal());
247
248 if (hasImpulses)
249 point.impulse = impulses[contactIdx];
250 else
251 point.impulse = 0.0f;
252
253 event.points.push_back(point);
254
255 contactIdx++;
256 }
257 }
258 }
259
260 event.colliderA = (Collider*)pair.shapes[0]->userData;
261 event.colliderB = (Collider*)pair.shapes[1]->userData;
262
263 gPhysX()._reportContactEvent(event);
264 }
265 }
266
267 void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count) override
268 {
269 for (UINT32 i = 0; i < count; i++)
270 {
271 PxConstraintInfo& constraintInfo = constraints[i];
272
273 if (constraintInfo.type != PxConstraintExtIDs::eJOINT)
274 continue;
275
276 PxJoint* pxJoint = (PxJoint*)constraintInfo.externalReference;
277
278 PhysX::JointBreakEvent event;
279 event.joint = (Joint*)pxJoint->userData;
280
281 if(event.joint != nullptr)
282 gPhysX()._reportJointBreakEvent(event);
283 }
284 }
285 };
286
287 class PhysXCPUDispatcher : public PxCpuDispatcher
288 {
289 public:
290 void submitTask(PxBaseTask& physxTask) override
291 {
292 // Note: Framework's task scheduler is pretty low granularity. Consider a better task manager in case PhysX ends
293 // up submitting many tasks.
294 // - PhysX's task manager doesn't seem much lighter either. But perhaps I can at least create a task pool to
295 // avoid allocating them constantly.
296
297 auto runTask = [&]() { physxTask.run(); physxTask.release(); };
298 SPtr<Task> task = Task::create("PhysX", runTask);
299
300 TaskScheduler::instance().addTask(task);
301 }
302
303 PxU32 getWorkerCount() const override
304 {
305 return (PxU32)TaskScheduler::instance().getNumWorkers();
306 }
307 };
308
309 class PhysXBroadPhaseCallback : public PxBroadPhaseCallback
310 {
311 void onObjectOutOfBounds(PxShape& shape, PxActor& actor) override
312 {
313 Collider* collider = (Collider*)shape.userData;
314 if (collider != nullptr)
315 LOGWRN("Physics object out of bounds. Consider increasing broadphase region!");
316 }
317
318 void onObjectOutOfBounds(PxAggregate& aggregate) override { /* Do nothing */ }
319 };
320
321 PxFilterFlags PhysXFilterShader(PxFilterObjectAttributes attr0, PxFilterData data0, PxFilterObjectAttributes attr1,
322 PxFilterData data1, PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
323 {
324 PhysXObjectFilterFlags flags0 = PhysXObjectFilterFlags(data0.word2);
325 PhysXObjectFilterFlags flags1 = PhysXObjectFilterFlags(data1.word2);
326
327 if (flags0.isSet(PhysXObjectFilterFlag::ReportAll) || flags1.isSet(PhysXObjectFilterFlag::ReportAll))
328 pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND | PxPairFlag::eNOTIFY_TOUCH_LOST | PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eNOTIFY_CONTACT_POINTS;
329 else if (flags0.isSet(PhysXObjectFilterFlag::ReportBasic) || flags1.isSet(PhysXObjectFilterFlag::ReportBasic))
330 pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND | PxPairFlag::eNOTIFY_TOUCH_LOST | PxPairFlag::eNOTIFY_CONTACT_POINTS;
331
332 if (PxFilterObjectIsTrigger(attr0) || PxFilterObjectIsTrigger(attr1))
333 {
334 if (!pairFlags)
335 return PxFilterFlag::eSUPPRESS; // Trigger with no notify flags
336
337 pairFlags |= PxPairFlag::eDETECT_DISCRETE_CONTACT;
338 return PxFilterFlags();
339 }
340
341 UINT64 groupA = *(UINT64*)&data0.word0;
342 UINT64 groupB = *(UINT64*)&data1.word0;
343
344 bool canCollide = gPhysics().isCollisionEnabled(groupA, groupB);
345 if (!canCollide)
346 return PxFilterFlag::eSUPPRESS;
347
348 if (flags0.isSet(PhysXObjectFilterFlag::CCD) || flags1.isSet(PhysXObjectFilterFlag::CCD))
349 pairFlags |= PxPairFlag::eDETECT_CCD_CONTACT;
350
351 pairFlags |= PxPairFlag::eSOLVE_CONTACT | PxPairFlag::eDETECT_DISCRETE_CONTACT;
352 return PxFilterFlags();
353 }
354
355 void setUnmappedTriangleIndex(const PxQueryHit& input, PhysicsQueryHit& output, PxShape* shapeHint = nullptr)
356 {
357 // We can only assign a valid unmapped triangle index if the hit geometry is a triangle mesh
358 // and it was created with the flags to store the remapping.
359 // As a fallback, the raw face index is used.
360
361 PxShape* shape = shapeHint ? shapeHint : input.shape;
362
363 if (shape != nullptr && shape->getGeometryType() == PxGeometryType::eTRIANGLEMESH)
364 {
365 PxTriangleMeshGeometry triMeshGeometry;
366 shape->getTriangleMeshGeometry(triMeshGeometry);
367
368 if (triMeshGeometry.isValid() && triMeshGeometry.triangleMesh->getTrianglesRemap() != nullptr)
369 {
370 output.unmappedTriangleIdx = triMeshGeometry.triangleMesh->getTrianglesRemap()[input.faceIndex];
371 return;
372 }
373 }
374
375 output.unmappedTriangleIdx = input.faceIndex;
376 }
377
378 void parseHit(const PxRaycastHit& input, PhysicsQueryHit& output, PxShape* shapeHint = nullptr)
379 {
380 output.point = fromPxVector(input.position);
381 output.normal = fromPxVector(input.normal);
382 output.distance = input.distance;
383 output.triangleIdx = input.faceIndex;
384 setUnmappedTriangleIndex(input, output, shapeHint);
385 output.uv = Vector2(input.u, input.v);
386
387 if(input.shape)
388 output.colliderRaw = (Collider*)input.shape->userData;
389
390 if (output.colliderRaw != nullptr)
391 {
392 CCollider* component = (CCollider*)output.colliderRaw->_getOwner(PhysicsOwnerType::Component);
393 if (component != nullptr)
394 output.collider = static_object_cast<CCollider>(component->getHandle());
395 }
396 }
397
398 void parseHit(const PxSweepHit& input, PhysicsQueryHit& output, PxShape* shapeHint = nullptr)
399 {
400 output.point = fromPxVector(input.position);
401 output.normal = fromPxVector(input.normal);
402 output.uv = Vector2::ZERO;
403 output.distance = input.distance;
404 output.triangleIdx = input.faceIndex;
405 setUnmappedTriangleIndex(input, output, shapeHint);
406 output.colliderRaw = (Collider*)input.shape->userData;
407
408 if (output.colliderRaw != nullptr)
409 {
410 CCollider* component = (CCollider*)output.colliderRaw->_getOwner(PhysicsOwnerType::Component);
411 if (component != nullptr)
412 output.collider = static_object_cast<CCollider>(component->getHandle());
413 }
414 }
415
416 struct PhysXRaycastQueryCallback : PxRaycastCallback
417 {
418 static const int MAX_HITS = 32;
419 PxRaycastHit buffer[MAX_HITS];
420
421 Vector<PhysicsQueryHit> data;
422
423 PhysXRaycastQueryCallback()
424 :PxRaycastCallback(buffer, MAX_HITS)
425 { }
426
427 PxAgain processTouches(const PxRaycastHit* buffer, PxU32 nbHits) override
428 {
429 for (PxU32 i = 0; i < nbHits; i++)
430 {
431 data.push_back(PhysicsQueryHit());
432 parseHit(buffer[i], data.back());
433 }
434
435 return true;
436 }
437 };
438
439 struct PhysXSweepQueryCallback : PxSweepCallback
440 {
441 static const int MAX_HITS = 32;
442 PxSweepHit buffer[MAX_HITS];
443
444 Vector<PhysicsQueryHit> data;
445
446 PhysXSweepQueryCallback()
447 :PxSweepCallback(buffer, MAX_HITS)
448 { }
449
450 PxAgain processTouches(const PxSweepHit* buffer, PxU32 nbHits) override
451 {
452 for (PxU32 i = 0; i < nbHits; i++)
453 {
454 data.push_back(PhysicsQueryHit());
455 parseHit(buffer[i], data.back());
456 }
457
458 return true;
459 }
460 };
461
462 struct PhysXOverlapQueryCallback : PxOverlapCallback
463 {
464 static const int MAX_HITS = 32;
465 PxOverlapHit buffer[MAX_HITS];
466
467 Vector<Collider*> data;
468
469 PhysXOverlapQueryCallback()
470 :PxOverlapCallback(buffer, MAX_HITS)
471 { }
472
473 PxAgain processTouches(const PxOverlapHit* buffer, PxU32 nbHits) override
474 {
475 for (PxU32 i = 0; i < nbHits; i++)
476 data.push_back((Collider*)buffer[i].shape->userData);
477
478 return true;
479 }
480 };
481
482 static PhysXAllocator gPhysXAllocator;
483 static PhysXErrorCallback gPhysXErrorHandler;
484 static PhysXCPUDispatcher gPhysXCPUDispatcher;
485 static PhysXEventCallback gPhysXEventCallback;
486 static PhysXBroadPhaseCallback gPhysXBroadphaseCallback;
487
488 static const UINT32 SIZE_16K = 1 << 14;
489 const UINT32 PhysX::SCRATCH_BUFFER_SIZE = SIZE_16K * 64; // 1MB by default
490
491 PhysX::PhysX(const PHYSICS_INIT_DESC& input)
492 :Physics(input), mInitDesc(input)
493 {
494 mScale.length = input.typicalLength;
495 mScale.speed = input.typicalSpeed;
496
497 mFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gPhysXAllocator, gPhysXErrorHandler);
498 mPhysics = PxCreateBasePhysics(PX_PHYSICS_VERSION, *mFoundation, mScale);
499
500 PxRegisterArticulations(*mPhysics);
501
502 if (input.initCooking)
503 {
504 // Note: PhysX supports cooking for specific platforms to make the generated results better. Consider
505 // allowing the meshes to be re-cooked when target platform is changed. Right now we just use the default value.
506
507 PxCookingParams cookingParams(mScale);
508 mCooking = PxCreateCooking(PX_PHYSICS_VERSION, *mFoundation, cookingParams);
509 }
510
511 mDefaultMaterial = mPhysics->createMaterial(1.0f, 1.0f, 0.5f);
512 }
513
514 PhysX::~PhysX()
515 {
516 assert(mScenes.empty() && "All scenes must be freed before physics system shutdown");
517
518 if (mCooking != nullptr)
519 mCooking->release();
520
521 mPhysics->release();
522 mFoundation->release();
523 }
524
525 void PhysX::fixedUpdate(float step)
526 {
527 if (mPaused)
528 return;
529
530 mUpdateInProgress = true;
531
532 // Note: Consider delaying fetchResults one frame. This could improve performance because Physics update would be
533 // able to run parallel to the simulation thread, but at a cost to input latency.
534 bs_frame_mark();
535 UINT8* scratchBuffer = bs_frame_alloc_aligned(SCRATCH_BUFFER_SIZE, 16);
536
537 for(auto& scene : mScenes)
538 {
539 scene->mScene->simulate(step, nullptr, scratchBuffer, SCRATCH_BUFFER_SIZE);
540
541 UINT32 errorState;
542 if (!scene->mScene->fetchResults(true, &errorState))
543 LOGWRN("Physics simulation failed. Error code: " + toString(errorState));
544 }
545
546 bs_frame_free_aligned(scratchBuffer);
547 bs_frame_clear();
548
549 // Update rigidbodies with new transforms
550 for(auto& scene : mScenes)
551 {
552 PxU32 numActiveTransforms;
553 const PxActiveTransform* activeTransforms = scene->mScene->getActiveTransforms(numActiveTransforms);
554
555 for (PxU32 i = 0; i < numActiveTransforms; i++)
556 {
557 Rigidbody* rigidbody = static_cast<Rigidbody*>(activeTransforms[i].userData);
558
559 // Note: This should never happen, as actors gets their userData set to null when they're destroyed. However
560 // in some cases PhysX seems to keep those actors alive for a frame or few, and reports their state here. Until
561 // I find out why I need to perform this check.
562 if (activeTransforms[i].actor->userData == nullptr)
563 continue;
564
565 const PxTransform& transform = activeTransforms[i].actor2World;
566
567 // Note: Make this faster, avoid dereferencing Rigidbody and attempt to access pos/rot destination directly,
568 // use non-temporal writes
569 rigidbody->_setTransform(fromPxVector(transform.p), fromPxQuaternion(transform.q));
570 }
571 }
572
573 // Note: Consider extrapolating for the remaining "simulationAmount" value
574 mUpdateInProgress = false;
575
576 triggerEvents();
577 }
578
579 void PhysX::update()
580 {
581 // Note: Potentially interpolate (would mean a one frame delay needs to be introduced)
582 }
583
584 void PhysX::_reportContactEvent(const ContactEvent& event)
585 {
586 mContactEvents.push_back(event);
587 }
588
589 void PhysX::_reportTriggerEvent(const TriggerEvent& event)
590 {
591 mTriggerEvents.push_back(event);
592 }
593
594 void PhysX::_reportJointBreakEvent(const JointBreakEvent& event)
595 {
596 mJointBreakEvents.push_back(event);
597 }
598
599 void PhysX::triggerEvents()
600 {
601 CollisionDataRaw data;
602
603 for(auto& entry : mTriggerEvents)
604 {
605 data.colliders[0] = entry.trigger;
606 data.colliders[1] = entry.other;
607
608 switch (entry.type)
609 {
610 case ContactEventType::ContactBegin:
611 entry.trigger->onCollisionBegin(data);
612 break;
613 case ContactEventType::ContactStay:
614 entry.trigger->onCollisionStay(data);
615 break;
616 case ContactEventType::ContactEnd:
617 entry.trigger->onCollisionEnd(data);
618 break;
619 }
620 }
621
622 auto notifyContact = [&](Collider* obj, Collider* other, ContactEventType type,
623 const Vector<ContactPoint>& points, bool flipNormals = false)
624 {
625 data.colliders[0] = obj;
626 data.colliders[1] = other;
627 data.contactPoints = points;
628
629 if(flipNormals)
630 {
631 for (auto& point : data.contactPoints)
632 point.normal = -point.normal;
633 }
634
635 Rigidbody* rigidbody = obj->getRigidbody();
636 if(rigidbody != nullptr)
637 {
638 switch (type)
639 {
640 case ContactEventType::ContactBegin:
641 rigidbody->onCollisionBegin(data);
642 break;
643 case ContactEventType::ContactStay:
644 rigidbody->onCollisionStay(data);
645 break;
646 case ContactEventType::ContactEnd:
647 rigidbody->onCollisionEnd(data);
648 break;
649 }
650 }
651 else
652 {
653 switch (type)
654 {
655 case ContactEventType::ContactBegin:
656 obj->onCollisionBegin(data);
657 break;
658 case ContactEventType::ContactStay:
659 obj->onCollisionStay(data);
660 break;
661 case ContactEventType::ContactEnd:
662 obj->onCollisionEnd(data);
663 break;
664 }
665 }
666 };
667
668 for (auto& entry : mContactEvents)
669 {
670 if (entry.colliderA != nullptr)
671 {
672 CollisionReportMode reportModeA = entry.colliderA->getCollisionReportMode();
673
674 if (reportModeA == CollisionReportMode::ReportPersistent)
675 notifyContact(entry.colliderA, entry.colliderB, entry.type, entry.points, true);
676 else if (reportModeA == CollisionReportMode::Report && entry.type != ContactEventType::ContactStay)
677 notifyContact(entry.colliderA, entry.colliderB, entry.type, entry.points, true);
678 }
679
680 if (entry.colliderB != nullptr)
681 {
682 CollisionReportMode reportModeB = entry.colliderB->getCollisionReportMode();
683
684 if (reportModeB == CollisionReportMode::ReportPersistent)
685 notifyContact(entry.colliderB, entry.colliderA, entry.type, entry.points, false);
686 else if (reportModeB == CollisionReportMode::Report && entry.type != ContactEventType::ContactStay)
687 notifyContact(entry.colliderB, entry.colliderA, entry.type, entry.points, false);
688 }
689 }
690
691 for(auto& entry : mJointBreakEvents)
692 {
693 entry.joint->onJointBreak();
694 }
695
696 mTriggerEvents.clear();
697 mContactEvents.clear();
698 mJointBreakEvents.clear();
699 }
700
701 SPtr<PhysicsMaterial> PhysX::createMaterial(float staticFriction, float dynamicFriction, float restitution)
702 {
703 return bs_core_ptr_new<PhysXMaterial>(mPhysics, staticFriction, dynamicFriction, restitution);
704 }
705
706 SPtr<PhysicsMesh> PhysX::createMesh(const SPtr<MeshData>& meshData, PhysicsMeshType type)
707 {
708 return bs_core_ptr_new<PhysXMesh>(meshData, type);
709 }
710
711 SPtr<PhysicsScene> PhysX::createPhysicsScene()
712 {
713 SPtr<PhysXScene> scene = bs_shared_ptr_new<PhysXScene>(mPhysics, mInitDesc, mScale);
714 mScenes.push_back(scene.get());
715
716 return scene;
717 }
718
719 void PhysX::_notifySceneDestroyed(PhysXScene* scene)
720 {
721 auto iterFind = std::find(mScenes.begin(), mScenes.end(), scene);
722 assert(iterFind != mScenes.end());
723
724 mScenes.erase(iterFind);
725 }
726
727 void PhysX::setPaused(bool paused)
728 {
729 mPaused = paused;
730 }
731
732 bool PhysX::_rayCast(const Vector3& origin, const Vector3& unitDir, const Collider& collider, PhysicsQueryHit& hit,
733 float maxDist) const
734 {
735 FPhysXCollider* physxCollider = static_cast<FPhysXCollider*>(collider._getInternal());
736 PxShape* shape = physxCollider->_getShape();
737
738 PxTransform transform = toPxTransform(collider.getPosition(), collider.getRotation());
739
740 PxRaycastHit hitInfo;
741 PxU32 maxHits = 1;
742 bool anyHit = false;
743 PxHitFlags hitFlags = PxHitFlag::eDEFAULT | PxHitFlag::eUV;
744 PxU32 hitCount = PxGeometryQuery::raycast(toPxVector(origin), toPxVector(unitDir),
745 shape->getGeometry().any(), transform,
746 maxDist, hitFlags, maxHits, &hitInfo, anyHit);
747
748 if(hitCount > 0)
749 parseHit(hitInfo, hit, shape); // We have to provide a hint for the tested shape, as it is not contained in single-geometry raycast hit results
750
751 return hitCount > 0;
752 }
753
754 PhysXScene::PhysXScene(PxPhysics* physics, const PHYSICS_INIT_DESC& input, const physx::PxTolerancesScale& scale)
755 :mPhysics(physics)
756 {
757 PxSceneDesc sceneDesc(scale); // TODO - Test out various other parameters provided by scene desc
758 sceneDesc.gravity = toPxVector(input.gravity);
759 sceneDesc.cpuDispatcher = &gPhysXCPUDispatcher;
760 sceneDesc.filterShader = PhysXFilterShader;
761 sceneDesc.simulationEventCallback = &gPhysXEventCallback;
762 sceneDesc.broadPhaseCallback = &gPhysXBroadphaseCallback;
763
764 // Optionally: eENABLE_KINEMATIC_STATIC_PAIRS, eENABLE_KINEMATIC_PAIRS, eENABLE_PCM
765 sceneDesc.flags = PxSceneFlag::eENABLE_ACTIVETRANSFORMS;
766
767 if (input.flags.isSet(PhysicsFlag::CCD_Enable))
768 sceneDesc.flags |= PxSceneFlag::eENABLE_CCD;
769
770 // Optionally: eMBP
771 sceneDesc.broadPhaseType = PxBroadPhaseType::eSAP;
772
773 mScene = physics->createScene(sceneDesc);
774
775 // Character controller
776 mCharManager = PxCreateControllerManager(*mScene);
777 }
778
779 PhysXScene::~PhysXScene()
780 {
781 mCharManager->release();
782 mScene->release();
783
784 gPhysX()._notifySceneDestroyed(this);
785 }
786
787 SPtr<Rigidbody> PhysXScene::createRigidbody(const HSceneObject& linkedSO)
788 {
789 return bs_shared_ptr_new<PhysXRigidbody>(mPhysics, mScene, linkedSO);
790 }
791
792 SPtr<BoxCollider> PhysXScene::createBoxCollider(const Vector3& extents, const Vector3& position,
793 const Quaternion& rotation)
794 {
795 return bs_shared_ptr_new<PhysXBoxCollider>(mPhysics, mScene, position, rotation, extents);
796 }
797
798 SPtr<SphereCollider> PhysXScene::createSphereCollider(float radius, const Vector3& position, const Quaternion& rotation)
799 {
800 return bs_shared_ptr_new<PhysXSphereCollider>(mPhysics, mScene, position, rotation, radius);
801 }
802
803 SPtr<PlaneCollider> PhysXScene::createPlaneCollider(const Vector3& position, const Quaternion& rotation)
804 {
805 return bs_shared_ptr_new<PhysXPlaneCollider>(mPhysics, mScene, position, rotation);
806 }
807
808 SPtr<CapsuleCollider> PhysXScene::createCapsuleCollider(float radius, float halfHeight, const Vector3& position,
809 const Quaternion& rotation)
810 {
811 return bs_shared_ptr_new<PhysXCapsuleCollider>(mPhysics, mScene, position, rotation, radius, halfHeight);
812 }
813
814 SPtr<MeshCollider> PhysXScene::createMeshCollider(const Vector3& position, const Quaternion& rotation)
815 {
816 return bs_shared_ptr_new<PhysXMeshCollider>(mPhysics, mScene, position, rotation);
817 }
818
819 SPtr<FixedJoint> PhysXScene::createFixedJoint(const FIXED_JOINT_DESC& desc)
820 {
821 return bs_shared_ptr_new<PhysXFixedJoint>(mPhysics, desc);
822 }
823
824 SPtr<DistanceJoint> PhysXScene::createDistanceJoint(const DISTANCE_JOINT_DESC& desc)
825 {
826 return bs_shared_ptr_new<PhysXDistanceJoint>(mPhysics, desc);
827 }
828
829 SPtr<HingeJoint> PhysXScene::createHingeJoint(const HINGE_JOINT_DESC& desc)
830 {
831 return bs_shared_ptr_new<PhysXHingeJoint>(mPhysics, desc);
832 }
833
834 SPtr<SphericalJoint> PhysXScene::createSphericalJoint(const SPHERICAL_JOINT_DESC& desc)
835 {
836 return bs_shared_ptr_new<PhysXSphericalJoint>(mPhysics, desc);
837 }
838
839 SPtr<SliderJoint> PhysXScene::createSliderJoint(const SLIDER_JOINT_DESC& desc)
840 {
841 return bs_shared_ptr_new<PhysXSliderJoint>(mPhysics, desc);
842 }
843
844 SPtr<D6Joint> PhysXScene::createD6Joint(const D6_JOINT_DESC& desc)
845 {
846 return bs_shared_ptr_new<PhysXD6Joint>(mPhysics, desc);
847 }
848
849 SPtr<CharacterController> PhysXScene::createCharacterController(const CHAR_CONTROLLER_DESC& desc)
850 {
851 return bs_shared_ptr_new<PhysXCharacterController>(mCharManager, desc);
852 }
853
854 Vector<PhysicsQueryHit> PhysXScene::sweepAll(const PxGeometry& geometry, const PxTransform& tfrm, const Vector3& unitDir,
855 UINT64 layer, float maxDist) const
856 {
857 PhysXSweepQueryCallback output;
858
859 PxQueryFilterData filterData;
860 memcpy(&filterData.data.word0, &layer, sizeof(layer));
861
862 mScene->sweep(geometry, tfrm, toPxVector(unitDir), maxDist, output,
863 PxHitFlag::eDEFAULT | PxHitFlag::eUV, filterData);
864
865 return output.data;
866 }
867
868 bool PhysXScene::sweepAny(const PxGeometry& geometry, const PxTransform& tfrm, const Vector3& unitDir, UINT64 layer,
869 float maxDist) const
870 {
871 PxSweepBuffer output;
872
873 PxQueryFilterData filterData;
874 filterData.flags |= PxQueryFlag::eANY_HIT;
875 memcpy(&filterData.data.word0, &layer, sizeof(layer));
876
877 return mScene->sweep(geometry, tfrm, toPxVector(unitDir), maxDist, output,
878 PxHitFlag::eDEFAULT | PxHitFlag::eUV | PxHitFlag::eMESH_ANY, filterData);
879 }
880
881 bool PhysXScene::rayCast(const Vector3& origin, const Vector3& unitDir, PhysicsQueryHit& hit, UINT64 layer, float max) const
882 {
883 PxRaycastBuffer output;
884
885 PxQueryFilterData filterData;
886 memcpy(&filterData.data.word0, &layer, sizeof(layer));
887
888 bool wasHit = mScene->raycast(toPxVector(origin),
889 toPxVector(unitDir), max, output, PxHitFlag::eDEFAULT | PxHitFlag::eUV, filterData);
890
891 if (wasHit)
892 parseHit(output.block, hit);
893
894 return wasHit;
895 }
896
897 bool PhysXScene::boxCast(const AABox& box, const Quaternion& rotation, const Vector3& unitDir, PhysicsQueryHit& hit,
898 UINT64 layer, float max) const
899 {
900 PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
901 PxTransform transform = toPxTransform(box.getCenter(), rotation);
902
903 return sweep(geometry, transform, unitDir, hit, layer, max);
904 }
905
906 bool PhysXScene::sphereCast(const Sphere& sphere, const Vector3& unitDir, PhysicsQueryHit& hit,
907 UINT64 layer, float max) const
908 {
909 PxSphereGeometry geometry(sphere.getRadius());
910 PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
911
912 return sweep(geometry, transform, unitDir, hit, layer, max);
913 }
914
915 bool PhysXScene::capsuleCast(const Capsule& capsule, const Quaternion& rotation, const Vector3& unitDir,
916 PhysicsQueryHit& hit, UINT64 layer, float max) const
917 {
918 PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
919 PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
920
921 return sweep(geometry, transform, unitDir, hit, layer, max);
922 }
923
924 bool PhysXScene::convexCast(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
925 const Vector3& unitDir, PhysicsQueryHit& hit, UINT64 layer, float max) const
926 {
927 if (mesh == nullptr)
928 return false;
929
930 if (mesh->getType() != PhysicsMeshType::Convex)
931 return false;
932
933 FPhysXMesh* physxMesh = static_cast<FPhysXMesh*>(mesh->_getInternal());
934 PxConvexMeshGeometry geometry(physxMesh->_getConvex());
935 PxTransform transform = toPxTransform(position, rotation);
936
937 return sweep(geometry, transform, unitDir, hit, layer, max);
938 }
939
940 Vector<PhysicsQueryHit> PhysXScene::rayCastAll(const Vector3& origin, const Vector3& unitDir,
941 UINT64 layer, float max) const
942 {
943 PhysXRaycastQueryCallback output;
944
945 PxQueryFilterData filterData;
946 memcpy(&filterData.data.word0, &layer, sizeof(layer));
947
948 mScene->raycast(toPxVector(origin), toPxVector(unitDir), max, output,
949 PxHitFlag::eDEFAULT | PxHitFlag::eUV | PxHitFlag::eMESH_MULTIPLE, filterData);
950
951 return output.data;
952 }
953
954 Vector<PhysicsQueryHit> PhysXScene::boxCastAll(const AABox& box, const Quaternion& rotation,
955 const Vector3& unitDir, UINT64 layer, float max) const
956 {
957 PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
958 PxTransform transform = toPxTransform(box.getCenter(), rotation);
959
960 return sweepAll(geometry, transform, unitDir, layer, max);
961 }
962
963 Vector<PhysicsQueryHit> PhysXScene::sphereCastAll(const Sphere& sphere, const Vector3& unitDir,
964 UINT64 layer, float max) const
965 {
966 PxSphereGeometry geometry(sphere.getRadius());
967 PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
968
969 return sweepAll(geometry, transform, unitDir, layer, max);
970 }
971
972 Vector<PhysicsQueryHit> PhysXScene::capsuleCastAll(const Capsule& capsule, const Quaternion& rotation,
973 const Vector3& unitDir, UINT64 layer, float max) const
974 {
975 PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
976 PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
977
978 return sweepAll(geometry, transform, unitDir, layer, max);
979 }
980
981 Vector<PhysicsQueryHit> PhysXScene::convexCastAll(const HPhysicsMesh& mesh, const Vector3& position,
982 const Quaternion& rotation, const Vector3& unitDir, UINT64 layer, float max) const
983 {
984 if (mesh == nullptr)
985 return Vector<PhysicsQueryHit>(0);
986
987 if (mesh->getType() != PhysicsMeshType::Convex)
988 return Vector<PhysicsQueryHit>(0);
989
990 FPhysXMesh* physxMesh = static_cast<FPhysXMesh*>(mesh->_getInternal());
991 PxConvexMeshGeometry geometry(physxMesh->_getConvex());
992 PxTransform transform = toPxTransform(position, rotation);
993
994 return sweepAll(geometry, transform, unitDir, layer, max);
995 }
996
997 bool PhysXScene::rayCastAny(const Vector3& origin, const Vector3& unitDir,
998 UINT64 layer, float max) const
999 {
1000 PxRaycastBuffer output;
1001
1002 PxQueryFilterData filterData;
1003 filterData.flags |= PxQueryFlag::eANY_HIT;
1004 memcpy(&filterData.data.word0, &layer, sizeof(layer));
1005
1006 return mScene->raycast(toPxVector(origin),
1007 toPxVector(unitDir), max, output, PxHitFlag::eDEFAULT | PxHitFlag::eUV | PxHitFlag::eMESH_ANY, filterData);
1008 }
1009
1010 bool PhysXScene::boxCastAny(const AABox& box, const Quaternion& rotation, const Vector3& unitDir,
1011 UINT64 layer, float max) const
1012 {
1013 PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
1014 PxTransform transform = toPxTransform(box.getCenter(), rotation);
1015
1016 return sweepAny(geometry, transform, unitDir, layer, max);
1017 }
1018
1019 bool PhysXScene::sphereCastAny(const Sphere& sphere, const Vector3& unitDir,
1020 UINT64 layer, float max) const
1021 {
1022 PxSphereGeometry geometry(sphere.getRadius());
1023 PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
1024
1025 return sweepAny(geometry, transform, unitDir, layer, max);
1026 }
1027
1028 bool PhysXScene::capsuleCastAny(const Capsule& capsule, const Quaternion& rotation, const Vector3& unitDir,
1029 UINT64 layer, float max) const
1030 {
1031 PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
1032 PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
1033
1034 return sweepAny(geometry, transform, unitDir, layer, max);
1035 }
1036
1037 bool PhysXScene::convexCastAny(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
1038 const Vector3& unitDir, UINT64 layer, float max) const
1039 {
1040 if (mesh == nullptr)
1041 return false;
1042
1043 if (mesh->getType() != PhysicsMeshType::Convex)
1044 return false;
1045
1046 FPhysXMesh* physxMesh = static_cast<FPhysXMesh*>(mesh->_getInternal());
1047 PxConvexMeshGeometry geometry(physxMesh->_getConvex());
1048 PxTransform transform = toPxTransform(position, rotation);
1049
1050 return sweepAny(geometry, transform, unitDir, layer, max);
1051 }
1052
1053 Vector<Collider*> PhysXScene::_boxOverlap(const AABox& box, const Quaternion& rotation,
1054 UINT64 layer) const
1055 {
1056 PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
1057 PxTransform transform = toPxTransform(box.getCenter(), rotation);
1058
1059 return overlap(geometry, transform, layer);
1060 }
1061
1062 Vector<Collider*> PhysXScene::_sphereOverlap(const Sphere& sphere, UINT64 layer) const
1063 {
1064 PxSphereGeometry geometry(sphere.getRadius());
1065 PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
1066
1067 return overlap(geometry, transform, layer);
1068 }
1069
1070 Vector<Collider*> PhysXScene::_capsuleOverlap(const Capsule& capsule, const Quaternion& rotation,
1071 UINT64 layer) const
1072 {
1073 PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
1074 PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
1075
1076 return overlap(geometry, transform, layer);
1077 }
1078
1079 Vector<Collider*> PhysXScene::_convexOverlap(const HPhysicsMesh& mesh, const Vector3& position,
1080 const Quaternion& rotation, UINT64 layer) const
1081 {
1082 if (mesh == nullptr)
1083 return Vector<Collider*>(0);
1084
1085 if (mesh->getType() != PhysicsMeshType::Convex)
1086 return Vector<Collider*>(0);
1087
1088 FPhysXMesh* physxMesh = static_cast<FPhysXMesh*>(mesh->_getInternal());
1089 PxConvexMeshGeometry geometry(physxMesh->_getConvex());
1090 PxTransform transform = toPxTransform(position, rotation);
1091
1092 return overlap(geometry, transform, layer);
1093 }
1094
1095 bool PhysXScene::boxOverlapAny(const AABox& box, const Quaternion& rotation, UINT64 layer) const
1096 {
1097 PxBoxGeometry geometry(toPxVector(box.getHalfSize()));
1098 PxTransform transform = toPxTransform(box.getCenter(), rotation);
1099
1100 return overlapAny(geometry, transform, layer);
1101 }
1102
1103 bool PhysXScene::sphereOverlapAny(const Sphere& sphere, UINT64 layer) const
1104 {
1105 PxSphereGeometry geometry(sphere.getRadius());
1106 PxTransform transform = toPxTransform(sphere.getCenter(), Quaternion::IDENTITY);
1107
1108 return overlapAny(geometry, transform, layer);
1109 }
1110
1111 bool PhysXScene::capsuleOverlapAny(const Capsule& capsule, const Quaternion& rotation,
1112 UINT64 layer) const
1113 {
1114 PxCapsuleGeometry geometry(capsule.getRadius(), capsule.getHeight() * 0.5f);
1115 PxTransform transform = toPxTransform(capsule.getCenter(), Quaternion::IDENTITY);
1116
1117 return overlapAny(geometry, transform, layer);
1118 }
1119
1120 bool PhysXScene::convexOverlapAny(const HPhysicsMesh& mesh, const Vector3& position, const Quaternion& rotation,
1121 UINT64 layer) const
1122 {
1123 if (mesh == nullptr)
1124 return false;
1125
1126 if (mesh->getType() != PhysicsMeshType::Convex)
1127 return false;
1128
1129 FPhysXMesh* physxMesh = static_cast<FPhysXMesh*>(mesh->_getInternal());
1130 PxConvexMeshGeometry geometry(physxMesh->_getConvex());
1131 PxTransform transform = toPxTransform(position, rotation);
1132
1133 return overlapAny(geometry, transform, layer);
1134 }
1135
1136 bool PhysXScene::sweep(const PxGeometry& geometry, const PxTransform& tfrm, const Vector3& unitDir,
1137 PhysicsQueryHit& hit, UINT64 layer, float maxDist) const
1138 {
1139 PxSweepBuffer output;
1140
1141 PxQueryFilterData filterData;
1142 memcpy(&filterData.data.word0, &layer, sizeof(layer));
1143
1144 bool wasHit = mScene->sweep(geometry, tfrm, toPxVector(unitDir), maxDist, output,
1145 PxHitFlag::eDEFAULT | PxHitFlag::eUV, filterData);
1146
1147 if (wasHit)
1148 parseHit(output.block, hit);
1149
1150 return wasHit;
1151 }
1152
1153 bool PhysXScene::overlapAny(const PxGeometry& geometry, const PxTransform& tfrm, UINT64 layer) const
1154 {
1155 PxOverlapBuffer output;
1156
1157 PxQueryFilterData filterData;
1158 filterData.flags |= PxQueryFlag::eANY_HIT;
1159 memcpy(&filterData.data.word0, &layer, sizeof(layer));
1160
1161 return mScene->overlap(geometry, tfrm, output, filterData);
1162 }
1163
1164 Vector<Collider*> PhysXScene::overlap(const PxGeometry& geometry, const PxTransform& tfrm, UINT64 layer) const
1165 {
1166 PhysXOverlapQueryCallback output;
1167
1168 PxQueryFilterData filterData;
1169 memcpy(&filterData.data.word0, &layer, sizeof(layer));
1170
1171 mScene->overlap(geometry, tfrm, output, filterData);
1172 return output.data;
1173 }
1174
1175 void PhysXScene::setFlag(PhysicsFlags flag, bool enabled)
1176 {
1177 PhysicsScene::setFlag(flag, enabled);
1178
1179 mCharManager->setOverlapRecoveryModule(mFlags.isSet(PhysicsFlag::CCT_OverlapRecovery));
1180 mCharManager->setPreciseSweeps(mFlags.isSet(PhysicsFlag::CCT_PreciseSweeps));
1181 mCharManager->setTessellation(mFlags.isSet(PhysicsFlag::CCT_Tesselation), mTesselationLength);
1182 }
1183
1184 Vector3 PhysXScene::getGravity() const
1185 {
1186 return fromPxVector(mScene->getGravity());
1187 }
1188
1189 void PhysXScene::setGravity(const Vector3& gravity)
1190 {
1191 mScene->setGravity(toPxVector(gravity));
1192 }
1193
1194 void PhysXScene::setMaxTesselationEdgeLength(float length)
1195 {
1196 mTesselationLength = length;
1197
1198 mCharManager->setTessellation(mFlags.isSet(PhysicsFlag::CCT_Tesselation), mTesselationLength);
1199 }
1200
1201 UINT32 PhysXScene::addBroadPhaseRegion(const AABox& region)
1202 {
1203 UINT32 id = mNextRegionIdx++;
1204
1205 PxBroadPhaseRegion pxRegion;
1206 pxRegion.bounds = PxBounds3(toPxVector(region.getMin()), toPxVector(region.getMax()));
1207 pxRegion.userData = (void*)(UINT64)id;
1208
1209 UINT32 handle = mScene->addBroadPhaseRegion(pxRegion, true);
1210 mBroadPhaseRegionHandles[id] = handle;
1211
1212 return handle;
1213 }
1214
1215 void PhysXScene::removeBroadPhaseRegion(UINT32 regionId)
1216 {
1217 auto iterFind = mBroadPhaseRegionHandles.find(regionId);
1218 if (iterFind == mBroadPhaseRegionHandles.end())
1219 return;
1220
1221 mScene->removeBroadPhaseRegion(iterFind->second);
1222 mBroadPhaseRegionHandles.erase(iterFind);
1223 }
1224
1225 void PhysXScene::clearBroadPhaseRegions()
1226 {
1227 for(auto& entry : mBroadPhaseRegionHandles)
1228 mScene->removeBroadPhaseRegion(entry.second);
1229
1230 mBroadPhaseRegionHandles.clear();
1231 }
1232
1233 PhysX& gPhysX()
1234 {
1235 return static_cast<PhysX&>(PhysX::instance());
1236 }
1237}