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 | |
29 | using namespace physx; |
30 | |
31 | namespace 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 (const PxContactPairHeader& , 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 | } |