| 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 | } |