1#ifndef GLM_ENABLE_EXPERIMENTAL
2 #define GLM_ENABLE_EXPERIMENTAL 1
5#include <glm/gtc/constants.hpp>
6#include <glm/gtx/matrix_decompose.hpp>
7#include <components/ErrorUtils.hpp>
10#if defined(__cpp_lib_execution) && defined(__cpp_lib_parallel_algorithm)
12 #define VEX_HAS_PARALLEL_EXECUTION
20#pragma push_macro("max")
26 inline void fastSyncTransform(
const glm::vec3& pos,
const glm::quat& rot,
27 JPH::BodyInterface& bi,
const JPH::BodyID&
id) {
28 JPH::RVec3 jPos(pos.x, pos.y, pos.z);
29 JPH::Quat jRot(rot.x, rot.y, rot.z, rot.w);
31 bi.SetPositionAndRotation(
id, jPos, jRot, JPH::EActivation::DontActivate);
35 JPH::RegisterDefaultAllocator();
37 if (JPH::Factory::sInstance ==
nullptr) {
38 JPH::Factory::sInstance =
new JPH::Factory();
44 m_tempAllocator =
new JPH::TempAllocatorImpl(10 * 1024 * 1024);
46 m_jobSystem =
new JPH::JobSystemThreadPool(
49 std::max(1u, std::thread::hardware_concurrency() - 1)
52 m_physicsSystem =
new JPH::PhysicsSystem();
54 m_physicsSystem->Init(
55 static_cast<uint32_t
>(maxBodies),
63 m_physicsSystem->SetGravity(JPH::Vec3(0.0f, -9.81f, 0.0f));
65 m_activationListener = std::make_unique<MyActivationListener>();
66 m_physicsSystem->SetBodyActivationListener(m_activationListener.get());
68 m_contactListener = std::make_unique<MyContactListener>(*
this);
69 m_physicsSystem->SetContactListener(m_contactListener.get());
72 this->onPhysicsComponentDestroy(m_registry, entity);
86 if (m_physicsSystem) {
91 }
catch (
const std::exception& e) {
95 if (m_physicsSystem) {
96 delete m_physicsSystem;
97 m_physicsSystem =
nullptr;
101 m_jobSystem =
nullptr;
103 if (m_tempAllocator) {
104 delete m_tempAllocator;
105 m_tempAllocator =
nullptr;
107 JPH::UnregisterTypes();
108 if (JPH::Factory::sInstance !=
nullptr) {
109 delete JPH::Factory::sInstance;
110 JPH::Factory::sInstance =
nullptr;
115 m_debugRenderer = renderer;
120 if (m_physicsSystem && m_debugRenderer) {
121 JPH::BodyManager::DrawSettings settings;
122 settings.mDrawShape = drawWireframe;
123 settings.mDrawShapeWireframe = drawWireframe;
127 m_physicsSystem->DrawBodies(settings, m_debugRenderer, &filter);
129 if (drawConstraints) {
130 m_physicsSystem->DrawConstraints(m_debugRenderer);
136 JPH::Quat glmToJph(
const glm::quat& q) {
137 return JPH::Quat(q.x, q.y, q.z, q.w);
142 if (!m_physicsSystem)
return;
144 auto& bodyInterface = m_physicsSystem->GetBodyInterface();
147 if (pc.bodyId.GetIndexAndSequenceNumber() == JPH::BodyID::cInvalidBodyID) [[unlikely]] {
159 log(
"This method is meant for debug builds only");
167 JPH::Ref<JPH::Shape> shape =
new JPH::CapsuleShape(
168 0.5f * (cc.standingHeight - 2.0f * cc.standingRadius),
172 JPH::CharacterVirtualSettings settings;
173 settings.mShape = shape;
174 settings.mMaxSlopeAngle = JPH::DegreesToRadians(cc.maxSlopeAngle);
175 settings.mMass = cc.mass;
177 JPH::RVec3 pos(tc.getWorldPosition().x, tc.getWorldPosition().y, tc.getWorldPosition().z);
178 JPH::Quat rot = glmToJph(tc.getWorldQuaternion());
180 cc.character =
new JPH::CharacterVirtual(&settings, pos, rot, 0, m_physicsSystem);
184 if (!m_physicsSystem)
return;
186 auto& bodyInterface = m_physicsSystem->GetBodyInterface();
189 if (pc.bodyId.GetIndexAndSequenceNumber() == JPH::BodyID::cInvalidBodyID) {
194 auto& cache = m_interpCache[pc.bodyId];
199 float posDiff = glm::distance(vexPos, cache.lastVisualPos);
200 glm::quat jRotGlm(cache.lastVisualRot.w, cache.lastVisualRot.x, cache.lastVisualRot.y, cache.lastVisualRot.z);
201 float rotDiff = 1.0f - std::abs(glm::dot(vexRot, jRotGlm));
203 if (posDiff > 0.0001f || rotDiff > 0.0001f || cache.desynced) {
204 bodyInterface.SetPositionAndRotation(
206 JPH::RVec3(vexPos.x, vexPos.y, vexPos.z),
208 JPH::EActivation::Activate
211 cache.prevPos = cache.currPos = cache.lastVisualPos = vexPos;
212 cache.prevRot = cache.currRot = cache.lastVisualRot = vexRot;
213 cache.desynced =
false;
217 m_accumulator += deltaTime;
219 while (m_accumulator >= m_fixedDt) {
221 auto& cache = m_interpCache[pc.bodyId];
222 cache.prevPos = cache.currPos;
223 cache.prevRot = cache.currRot;
227 if (!cc.isInitialized()) InitializeCharacter(e, cc);
230 JPH::RVec3 charPos = cc.character->GetPosition();
231 if (glm::distance(vexPos, glm::vec3(charPos.GetX(), charPos.GetY(), charPos.GetZ())) > 0.0001f) {
232 cc.character->SetPosition(JPH::RVec3(vexPos.x, vexPos.y, vexPos.z));
235 JPH::Vec3 currentVelocity = cc.character->GetLinearVelocity();
237 float newVerticalVel = currentVelocity.GetY() + m_physicsSystem->GetGravity().GetY() * m_fixedDt;
238 if (cc.character->IsSupported()) {
239 newVerticalVel = std::max(0.0f, newVerticalVel);
242 JPH::Vec3 finalVelocity(cc.controlInput.x, (newVerticalVel + cc.controlInput.y), cc.controlInput.z);
243 cc.character->SetLinearVelocity(finalVelocity);
245 JPH::CharacterVirtual::ExtendedUpdateSettings updateSettings;
246 JPH::ObjectLayer charLayer = 0;
248 cc.character->ExtendedUpdate(
250 m_physicsSystem->GetGravity(),
252 m_physicsSystem->GetDefaultBroadPhaseLayerFilter(charLayer),
253 m_physicsSystem->GetDefaultLayerFilter(charLayer),
254 {}, {}, *m_tempAllocator
257 JPH::RVec3 newPos = cc.character->GetPosition();
261 m_physicsSystem->Update(m_fixedDt, 1, m_tempAllocator, m_jobSystem);
264 auto& cache = m_interpCache[pc.bodyId];
265 JPH::RVec3 pos = bodyInterface.GetCenterOfMassPosition(pc.bodyId);
266 JPH::Quat rot = bodyInterface.GetRotation(pc.bodyId);
267 cache.currPos = glm::vec3(pos.GetX(), pos.GetY(), pos.GetZ());
268 cache.currRot = glm::quat(rot.GetW(), rot.GetX(), rot.GetY(), rot.GetZ());
271 m_accumulator -= m_fixedDt;
275 cc.controlInput = glm::vec3(0.0f);
278 float alpha = m_accumulator / m_fixedDt;
281 if (pc.bodyId.GetIndexAndSequenceNumber() != JPH::BodyID::cInvalidBodyID) {
282 SyncBodyToTransform(e, m_registry, pc.bodyId, alpha);
288 JPH::VertexList& outVerts, JPH::IndexedTriangleList& outTris) {
292 bool operator<(
const Vec3Key& other)
const {
293 if (m_v.x != other.m_v.x)
return m_v.x < other.m_v.x;
294 if (m_v.y != other.m_v.y)
return m_v.y < other.m_v.y;
295 return m_v.z < other.m_v.z;
299 std::map<Vec3Key, uint32_t> uniqueMap;
302 outVerts.reserve(inVerts.size());
303 outTris.reserve(inIndices.size() / 3);
305 std::vector<uint32_t> remappedIndices;
306 remappedIndices.resize(inIndices.size());
308 for (
size_t i = 0; i < inIndices.size(); ++i) {
309 uint32_t originalIdx = inIndices[i];
310 if(originalIdx >= inVerts.size())
continue;
312 glm::vec3 pos = inVerts[originalIdx];
315 if (uniqueMap.find(key) == uniqueMap.end()) {
316 uint32_t newIdx = (uint32_t)outVerts.size();
317 uniqueMap[key] = newIdx;
318 outVerts.emplace_back(pos.x, pos.y, pos.z);
319 remappedIndices[i] = newIdx;
321 remappedIndices[i] = uniqueMap[key];
325 for (
size_t i = 0; i < remappedIndices.size(); i += 3) {
326 outTris.emplace_back(remappedIndices[i], remappedIndices[i+1], remappedIndices[i+2]);
334 JPH::RVec3 pos(t.getWorldPosition().x, t.getWorldPosition().y, t.getWorldPosition().z);
335 JPH::Quat rot = glmToJph(t.getWorldQuaternion());
337 JPH::ShapeRefC shape;
340 shape =
new JPH::BoxShape(JPH::Vec3(pc.boxHalfExtents.x, pc.boxHalfExtents.y, pc.boxHalfExtents.z));
342 case ShapeType::ROUNDED_BOX:
344 JPH::Vec3 halfExtents(
345 std::max(pc.roundedRadius, pc.boxHalfExtents.x) - pc.roundedRadius,
346 std::max(pc.roundedRadius, pc.boxHalfExtents.y) - pc.roundedRadius,
347 std::max(pc.roundedRadius, pc.boxHalfExtents.z) - pc.roundedRadius
350 JPH::BoxShapeSettings settings(halfExtents, pc.roundedRadius);
351 shape = settings.Create().Get();
354 case ShapeType::SPHERE:
355 shape =
new JPH::SphereShape(pc.sphereRadius);
357 case ShapeType::CAPSULE:
358 shape =
new JPH::CapsuleShape(pc.capsuleHeight / 2.0f, pc.capsuleRadius);
360 case ShapeType::CYLINDER:
361 shape =
new JPH::CylinderShape(pc.cylinderHeight / 2.0f, pc.cylinderRadius);
363 case ShapeType::CONVEX_HULL:
364 if (pc.convexPoints.empty()) {
365 log(LogLevel::ERROR,
"Convex hull shape has no points");
369 JPH::Array<JPH::Vec3> vertices;
370 vertices.reserve(pc.convexPoints.size()/3);
371 for (
const auto& p : pc.convexPoints) {
372 vertices.emplace_back(JPH::Vec3(p.GetX(), p.GetY(), p.GetZ()));
374 JPH::ConvexHullShapeSettings settings(vertices);
375 shape = settings.Create().Get();
378 case ShapeType::MESH:
379 if (pc.meshVertices.empty() || pc.meshIndices.empty()) {
380 log(LogLevel::ERROR,
"Mesh shape has no vertices or indices");
385 if (!mc.meshData.submeshes.empty()) {
386 pc.meshVertices.clear();
387 pc.meshIndices.clear();
389 size_t vertexOffset = 0;
391 for (
const auto& sm : mc.meshData.submeshes) {
392 for (
const auto& v : sm.vertices) {
393 pc.meshVertices.push_back(v.position);
395 for (
auto idx : sm.indices) {
396 pc.meshIndices.push_back(
static_cast<uint32_t
>(idx + vertexOffset));
398 vertexOffset += sm.vertices.size();
401 if (pc.meshVertices.empty() || pc.meshIndices.empty()) {
402 log(LogLevel::ERROR,
"Mesh shape has no vertices or indices. I have no fucking idea why because it just copied those and would error out if there were none :C");
406 log(LogLevel::ERROR,
"MeshComponent has no verticies");
410 log(LogLevel::ERROR,
"MeshComponent also not found");
415 if (pc.bodyType == BodyType::DYNAMIC) {
416 JPH::Array<JPH::Vec3> verts;
417 verts.reserve(pc.meshVertices.size());
418 for (
const auto& v : pc.meshVertices) {
419 verts.emplace_back(JPH::Vec3(v.x, v.y, v.z));
421 log(LogLevel::WARNING,
"Dynamic mesh fallback to convex hull");
422 JPH::ConvexHullShapeSettings settings(verts);
424 auto result = settings.Create();
425 if (result.IsValid()) {
426 shape = result.Get();
428 log(LogLevel::ERROR,
"Failed to create Convex Hull: %s", result.GetError().c_str());
443 JPH::VertexList verts;
444 JPH::IndexedTriangleList tris;
445 WeldVertices(pc.meshVertices, pc.meshIndices, verts, tris);
447 JPH::MeshShapeSettings settings(verts, tris);
448 settings.mActiveEdgeCosThresholdAngle = cos(JPH::DegreesToRadians(25.0f));
449 shape = settings.Create().Get();
454 JPH::EMotionType motion = JPH::EMotionType::Static;
455 if (pc.bodyType == BodyType::DYNAMIC) motion = JPH::EMotionType::Dynamic;
456 else if (pc.bodyType == BodyType::KINEMATIC) motion = JPH::EMotionType::Kinematic;
457 else if (pc.bodyType == BodyType::SENSOR) motion = JPH::EMotionType::Static;
459 JPH::BodyCreationSettings settings(shape, pos, rot, motion, pc.objectLayer);
460 settings.mLinearDamping = pc.linearDamping;
461 settings.mAngularDamping = pc.angularDamping;
462 settings.mAllowSleeping = pc.allowSleeping;
463 settings.mIsSensor = pc.isSensor || pc.bodyType == BodyType::SENSOR;
464 if (pc.bodyType == BodyType::DYNAMIC || pc.bodyType == BodyType::KINEMATIC) {
465 settings.mOverrideMassProperties = JPH::EOverrideMassProperties::CalculateInertia;
466 settings.mMassPropertiesOverride.mMass = pc.mass;
469 auto& bodyInterface = m_physicsSystem->GetBodyInterface();
470 JPH::BodyID bodyId = bodyInterface.CreateAndAddBody(settings, JPH::EActivation::Activate);
471 if (bodyId.GetIndexAndSequenceNumber() == JPH::BodyID::cInvalidBodyID)
return std::nullopt;
473 bodyInterface.SetFriction(bodyId, pc.friction);
474 bodyInterface.SetRestitution(bodyId, pc.bounce);
477 m_bodyToEntity[bodyId] = e;
479 JPH::RVec3 jPos = bodyInterface.GetCenterOfMassPosition(bodyId);
480 JPH::Quat jRot = bodyInterface.GetRotation(bodyId);
483 cache.prevPos = cache.currPos = cache.lastVisualPos = glm::vec3(jPos.GetX(), jPos.GetY(), jPos.GetZ());
484 cache.prevRot = cache.currRot = cache.lastVisualRot = glm::quat(jRot.GetW(), jRot.GetX(), jRot.GetY(), jRot.GetZ());
485 m_interpCache[bodyId] = cache;
496 if (!m_physicsSystem || pc.bodyId.GetIndexAndSequenceNumber() == JPH::BodyID::cInvalidBodyID)
return;
497 auto& bi = m_physicsSystem->GetBodyInterface();
498 bi.RemoveBody(pc.bodyId);
499 bi.DestroyBody(pc.bodyId);
500 m_bodyToEntity.erase(pc.bodyId);
501 m_interpCache.erase(pc.bodyId);
502 pc.bodyId = JPH::BodyID(JPH::BodyID::cInvalidBodyID);
512 JPH::Quat PhysicsSystem::EulerToQuat(
const glm::vec3& eulerDeg) {
513 glm::vec3 eulerRad = glm::radians(eulerDeg);
514 glm::quat q = glm::yawPitchRoll(eulerRad.y, eulerRad.x, eulerRad.z);
515 return JPH::Quat(q.x, q.y, q.z, q.w);
518 glm::vec3 PhysicsSystem::QuatToEuler(
const JPH::Quat& q) {
519 glm::quat gq(q.GetX(), q.GetY(), q.GetZ(), q.GetW());
520 return glm::degrees(glm::eulerAngles(gq));
523 void PhysicsSystem::SyncBodyToTransform(
vex::Entity e, vex::Registry& r,
const JPH::BodyID&
id,
float alpha) {
524 if(!enableSmoothing) alpha = 1.f;
526 auto& t = r.
get<TransformComponent>(e);
527 auto& cache = m_interpCache[id];
529 glm::vec3 vexPos = t.getWorldPosition();
530 glm::quat vexRot = t.getWorldQuaternion();
532 float posDiff = glm::distance(vexPos, cache.lastVisualPos);
533 glm::quat jRotGlm(cache.lastVisualRot.w, cache.lastVisualRot.x, cache.lastVisualRot.y, cache.lastVisualRot.z);
534 float rotDiff = 1.0f - std::abs(glm::dot(vexRot, jRotGlm));
536 const float epsilon = 0.0001f;
538 if (posDiff > epsilon || rotDiff > epsilon) {
539 glm::vec3 deltaPos = vexPos - cache.lastVisualPos;
540 glm::quat deltaRot = vexRot * glm::inverse(cache.lastVisualRot);
542 cache.prevPos += deltaPos;
543 cache.currPos += deltaPos;
544 cache.prevRot = glm::normalize(deltaRot * cache.prevRot);
545 cache.currRot = glm::normalize(deltaRot * cache.currRot);
546 cache.desynced =
true;
549 glm::vec3 interpPos = glm::mix(cache.prevPos, cache.currPos, alpha);
551 glm::quat targetRot = cache.currRot;
552 if (glm::dot(cache.prevRot, targetRot) < 0.0f) {
553 targetRot = -targetRot;
555 glm::quat interpRot = glm::slerp(cache.prevRot, targetRot, alpha);
557 t.setWorldPositionPhys(interpPos);
558 t.setWorldQuaternionPhys(interpRot);
560 cache.lastVisualPos = interpPos;
561 cache.lastVisualRot = interpRot;
564 PhysicsComponent& PhysicsSystem::getPhysicsComponentByBodyId(JPH::BodyID
id) {
565 auto it = m_bodyToEntity.find(
id);
566 if (it != m_bodyToEntity.end()) {
567 return m_registry.get<PhysicsComponent>(it->second);
569 static PhysicsComponent dummy;
573 vex::Entity PhysicsSystem::getEntityByBodyId(JPH::BodyID
id) {
574 auto it = m_bodyToEntity.find(
id);
578 class IgnoreSensorFilter :
public JPH::BodyFilter {
580 IgnoreSensorFilter(
const JPH::BodyLockInterface& lockInterface) : m_lockInterface(lockInterface) {}
582 bool ShouldCollide(
const JPH::BodyID& inBodyID)
const override {
583 JPH::BodyLockRead lock(m_lockInterface, inBodyID);
584 if (lock.Succeeded()) {
585 return !lock.GetBody().IsSensor();
590 bool ShouldCollideLocked(
const JPH::Body& inBody)
const override {
591 return !inBody.IsSensor();
595 const JPH::BodyLockInterface& m_lockInterface;
599 if (!m_physicsSystem)
return false;
601 JPH::RVec3 start(origin.x, origin.y, origin.z);
602 JPH::Vec3 dir(direction.x, direction.y, direction.z);
603 JPH::RRayCast ray(start, dir * maxDistance);
604 JPH::RayCastResult result;
608 if (m_physicsSystem->GetNarrowPhaseQuery().CastRay(ray, result, { }, { }, filter)) {
609 hit.bodyId = result.mBodyID;
610 hit.distance = result.mFraction * maxDistance;
611 hit.position = origin + direction * hit.distance;
612 hit.normal = glm::vec3(0.0f);
619 auto& bi = m_physicsSystem->GetBodyInterface();
620 bi.SetFriction(bodyId, friction);
621 getPhysicsComponentByBodyId(bodyId).friction = friction;
625 return m_physicsSystem->GetBodyInterface().GetFriction(bodyId);
629 auto& bi = m_physicsSystem->GetBodyInterface();
630 bi.SetRestitution(bodyId, bounciness);
631 getPhysicsComponentByBodyId(bodyId).bounce = bounciness;
635 return m_physicsSystem->GetBodyInterface().GetRestitution(bodyId);
639 m_physicsSystem->GetBodyInterface().SetLinearVelocity(bodyId, JPH::Vec3(velocity.x, velocity.y, velocity.z));
643 auto vel = m_physicsSystem->GetBodyInterface().GetLinearVelocity(bodyId);
644 return {vel.GetX(), vel.GetY(), vel.GetZ()};
648 JPH::RVec3 joltPoint(point.x, point.y, point.z);
649 JPH::Vec3 joltVel = m_physicsSystem->GetBodyInterface().GetPointVelocity(bodyId, joltPoint);
650 return glm::vec3(joltVel.GetX(), joltVel.GetY(), joltVel.GetZ());
654 auto& bi = m_physicsSystem->GetBodyInterface();
655 JPH::Vec3 current = bi.GetLinearVelocity(bodyId);
656 bi.SetLinearVelocity(bodyId, current + JPH::Vec3(velocity.x, velocity.y, velocity.z));
660 m_physicsSystem->GetBodyInterface().SetAngularVelocity(bodyId, JPH::Vec3(velocity.x, velocity.y, velocity.z));
664 auto vel = m_physicsSystem->GetBodyInterface().GetAngularVelocity(bodyId);
665 return {vel.GetX(), vel.GetY(), vel.GetZ()};
669 auto& bi = m_physicsSystem->GetBodyInterface();
670 JPH::Vec3 current = bi.GetAngularVelocity(bodyId);
671 bi.SetAngularVelocity(bodyId, current + JPH::Vec3(velocity.x, velocity.y, velocity.z));
675 m_physicsSystem->GetBodyInterface().AddTorque(bodyId, JPH::Vec3(torque.x, torque.y, torque.z));
679 m_physicsSystem->GetBodyInterface().AddForce(bodyId, JPH::Vec3(force.x, force.y, force.z));
683 m_physicsSystem->GetBodyInterface().AddForce(bodyId, JPH::Vec3(force.x, force.y, force.z), JPH::RVec3(position.x, position.y, position.z));
687 m_physicsSystem->GetBodyInterface().AddImpulse(bodyId, JPH::Vec3(impulse.x, impulse.y, impulse.z));
691 m_physicsSystem->GetBodyInterface().AddImpulse(bodyId, JPH::Vec3(impulse.x, impulse.y, impulse.z), JPH::RVec3(position.x, position.y, position.z));
695 m_physicsSystem->GetBodyInterface().AddAngularImpulse(bodyId, JPH::Vec3(impulse.x, impulse.y, impulse.z));
699 auto& bi = m_physicsSystem->GetBodyInterface();
700 active ? bi.ActivateBody(bodyId) : bi.DeactivateBody(bodyId);
704 return m_physicsSystem->GetBodyInterface().IsActive(bodyId);
708 if (!m_physicsSystem || bodyId.IsInvalid())
return glm::vec3(0.0f);
709 JPH::RVec3 pos = m_physicsSystem->GetBodyInterfaceNoLock().GetCenterOfMassPosition(bodyId);
710 return glm::vec3(pos.GetX(), pos.GetY(), pos.GetZ());
714 if (!m_physicsSystem || bodyId.IsInvalid())
return glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
715 JPH::Quat rot = m_physicsSystem->GetBodyInterfaceNoLock().GetRotation(bodyId);
716 return glm::quat(rot.GetW(), rot.GetX(), rot.GetY(), rot.GetZ());
719 void MyContactListener::OnContactAdded(
const JPH::Body& inBody1,
const JPH::Body& inBody2,
const JPH::ContactManifold& inManifold, JPH::ContactSettings& ioSettings) {
720 vex::Entity e1 = m_system.getEntityByBodyId(inBody1.GetID());
721 vex::Entity e2 = m_system.getEntityByBodyId(inBody2.GetID());
723 hit.position = glm::vec3(inManifold.mWorldSpaceNormal.GetX(), inManifold.mWorldSpaceNormal.GetY(), inManifold.mWorldSpaceNormal.GetZ());
724 hit.normal = hit.position;
725 hit.impulse = ioSettings.mCombinedRestitution;
729 if (pc.onCollisionEnter) pc.onCollisionEnter(e1, e2, hit);
732 auto& pc = m_system.m_registry.
get<PhysicsComponent>(e2);
733 if (pc.onCollisionEnter) pc.onCollisionEnter(e2, e1, hit);
737 void MyContactListener::OnContactPersisted(
const JPH::Body& inBody1,
const JPH::Body& inBody2,
const JPH::ContactManifold& inManifold, JPH::ContactSettings& ioSettings) {
738 vex::Entity e1 = m_system.getEntityByBodyId(inBody1.GetID());
739 vex::Entity e2 = m_system.getEntityByBodyId(inBody2.GetID());
741 hit.position = glm::vec3(inManifold.mWorldSpaceNormal.GetX(), inManifold.mWorldSpaceNormal.GetY(), inManifold.mWorldSpaceNormal.GetZ());
742 hit.normal = hit.position;
743 hit.impulse = ioSettings.mCombinedRestitution;
745 if (e1 !=
vex::NULL_ENTITY && m_system.m_registry.has<PhysicsComponent>(e1)) {
746 auto& pc = m_system.m_registry.get<PhysicsComponent>(e1);
747 if (pc.onCollisionStay) pc.onCollisionStay(e1, e2, hit);
749 if (e2 !=
vex::NULL_ENTITY && m_system.m_registry.has<PhysicsComponent>(e2)) {
750 auto& pc = m_system.m_registry.get<PhysicsComponent>(e2);
751 if (pc.onCollisionStay) pc.onCollisionStay(e2, e1, hit);
755 void MyContactListener::OnContactRemoved(
const JPH::SubShapeIDPair& inSubShapePair) {
756 JPH::BodyID id1 = inSubShapePair.GetBody1ID();
757 JPH::BodyID id2 = inSubShapePair.GetBody2ID();
761 if (e1 !=
vex::NULL_ENTITY && m_system.m_registry.has<PhysicsComponent>(e1)) {
762 auto& pc = m_system.m_registry.get<PhysicsComponent>(e1);
763 if (pc.onCollisionExit) pc.onCollisionExit(e1, e2);
765 if (e2 !=
vex::NULL_ENTITY && m_system.m_registry.has<PhysicsComponent>(e2)) {
766 auto& pc = m_system.m_registry.get<PhysicsComponent>(e2);
767 if (pc.onCollisionExit) pc.onCollisionExit(e2, e1);
This file exist only because of coliding macros.
This file defines PhysicsSystem class.
Implementation of JPH::BodyDrawFilter for debugging physics bodies.
void SetLinearVelocity(JPH::BodyID bodyId, const glm::vec3 &velocity)
Allows for updating linear velocity.
void SyncBodies()
Scans registry for PhysicsComponents without bodies and creates them.
void SetBodyActive(JPH::BodyID bodyId, bool active)
Allows for setting body active state.
void AddForceAtPosition(JPH::BodyID bodyId, const glm::vec3 &force, const glm::vec3 &position)
Applies force at the given position of the body, it is reset next physics tick.
void SetFriction(JPH::BodyID bodyId, float friction)
Allows for updating friction.
float GetFriction(JPH::BodyID bodyId)
Allows for getting friction.
void AddLinearVelocity(JPH::BodyID bodyId, const glm::vec3 &velocity)
Allows for adding to linear velocity.
void setDebugRenderer(JPH::DebugRenderer *renderer)
Sets the debug renderer for the physics system.
bool raycast(const glm::vec3 &origin, const glm::vec3 &direction, float maxDistance, RaycastHit &hit)
Performs a raycast in the physics world.
void WeldVertices(const std::vector< glm::vec3 > &inVerts, const std::vector< uint32_t > &inIndices, JPH::VertexList &outVerts, JPH::IndexedTriangleList &outTris)
Helper to weld vertices based on position ONLY (ignoring UVs/Normals which split render meshes).
void update(float deltaTime)
Updates physics, it is technically fixed time but needs delta to track if required time already passe...
void AddForce(JPH::BodyID bodyId, const glm::vec3 &force)
Applies force at the mass center of the body, it is reset next physics tick.
void AddAngularImpulse(JPH::BodyID bodyId, const glm::vec3 &impulse)
Applies angular impulse at the mass center of the body, it is reset next physics tick.
void AddTorque(JPH::BodyID bodyId, const glm::vec3 &torque)
Applies torque to the body, it is reset next physics tick.
void AddAngularVelocity(JPH::BodyID bodyId, const glm::vec3 &velocity)
Allows for adding to angular velocity.
void AddImpulse(JPH::BodyID bodyId, const glm::vec3 &impulse)
Applies impulse at the mass center of the body, it is reset next physics tick.
void shutdown()
clears all physics objects
void SetAngularVelocity(JPH::BodyID bodyId, const glm::vec3 &velocity)
Allows for setting angular velocity.
glm::vec3 GetPhysicsPosition(JPH::BodyID bodyId)
Retrieves the physics position of a body.
glm::vec3 GetLinearVelocity(JPH::BodyID bodyId)
Allows for getting linear velocity.
std::optional< JPH::BodyID > CreateBodyForEntity(vex::Entity e, vex::Registry &r, PhysicsComponent &pc)
Allows to recreate physics body at runtime.
std::optional< JPH::BodyID > RecreateBodyForEntity(vex::Entity e, PhysicsComponent &pc)
Allows to recreate physics body at runtime.
bool init(size_t maxBodies=1024)
Initializes jolts physics system.
void AddImpulseAtPosition(JPH::BodyID bodyId, const glm::vec3 &impulse, const glm::vec3 &position)
Applies impulse at the given position of the body, it is reset next physics tick.
void drawDebug(bool drawConstraints=true, bool drawWireframe=true)
Draws debug information for the physics system.
void DestroyBodyForEntity(PhysicsComponent &pc)
Destroys a physics body.
void SetBounciness(JPH::BodyID bodyId, float bounciness)
Allows for updating bounciness.
glm::vec3 GetAngularVelocity(JPH::BodyID bodyId)
Allows for getting angular velocity.
bool GetBodyActive(JPH::BodyID bodyId)
Allows for getting body active state.
glm::vec3 GetVelocityAtPosition(JPH::BodyID bodyId, const glm::vec3 &point)
Gets the velocity of a specific point on the body (in World Space).
~PhysicsSystem()
Destructor, simply calls shutdown().
glm::quat GetPhysicsRotation(JPH::BodyID bodyId)
Retrieves the physics rotation of a body.
float GetBounciness(JPH::BodyID bodyId)
Allows for getting bounciness.
Central registry for managing entities and their components in the ECS system.
bool has(Entity entity)
Checks if an entity has a component of type T.
T & get(Entity entity)
Retrieves a component from an entity.
Provides iteration over entities that have all specified component types.
void each(Func func)
Iterates over all entities with the specified component types.
uint32_t Entity
Type alias representing a unique entity identifier in the ECS system.
void VEX_EXPORT log(const char *fmt,...)
Logs a formatted message.
void VEX_EXPORT handle_exception(const std::exception &e)
Handles an exception based on build configuration.
constexpr Entity NULL_ENTITY
Special entity value indicating an invalid or null entity.
Character component for player or NPC characters.
Structure representing a collision hit.
Struct containing raw meshData, mesh id, texture paths and material properties. It just template and ...
Structure representing a physics component.
Structure representing a raycast hit.