14 if (!collider)
return;
17 for (
auto* c : m_colliders) {
18 if (c == collider)
return;
22 int proxyId = m_tree.Insert(worldAABB, collider);
24 m_colliders.push_back(collider);
28 if (!collider)
return;
32 m_tree.Remove(proxyId);
37 std::remove(m_colliders.begin(), m_colliders.end(), collider),
43 for (
auto* collider : m_colliders) {
44 if (
auto* owner = collider->GetOwner()) {
49 rb->ClearCollisionState();
53 rb->SetGrounded(
false);
55 if (rb->GetUseGravity()) {
60 vel = vel + rb->GetGravity() * dt;
65 if (vel.
GetY() <= 0.0f) {
72 float termVel = rb->GetTerminalVelocity();
73 if (vel.
GetY() < -termVel) {
85 transform->Translate(delta);
86 }
else if (
auto* cam =
dynamic_cast<Camera*
>(owner)) {
87 cam->AddPosition(delta);
94 FindPairsAndResolve();
97void PhysicsWorld::UpdateBroadphase() {
98 for (
auto* collider : m_colliders) {
99 int proxyId = collider->GetProxyId();
100 if (proxyId < 0)
continue;
102 AABB newAABB = collider->GetWorldAABB();
107void PhysicsWorld::FindPairsAndResolve() {
108 for (
size_t i = 0; i < m_colliders.size(); ++i) {
109 ColliderComponent* colliderA = m_colliders[i];
110 if (colliderA->GetProxyId() < 0)
continue;
112 AABB worldA = colliderA->GetWorldAABB();
114 m_tree.
Query(worldA, [&](
int proxyId) ->
bool {
115 auto* colliderB =
static_cast<ColliderComponent*
>(m_tree.
GetUserData(proxyId));
116 if (colliderB == colliderA)
return true;
119 if (colliderA > colliderB)
return true;
122 if ((colliderA->GetLayer() & colliderB->GetMask()) == 0)
return true;
123 if ((colliderB->GetLayer() & colliderA->GetMask()) == 0)
return true;
126 auto* ownerA = colliderA->GetOwner();
127 auto* ownerB = colliderB->GetOwner();
128 if (!ownerA || !ownerB)
return true;
131 Vector3D scaleA(1, 1, 1), scaleB(1, 1, 1);
133 auto* transformA = ownerA->GetComponent<TransformComponent>();
135 posA = transformA->GetWorldPosition() + colliderA->GetOffset();
136 scaleA = transformA->GetWorldScale();
137 }
else if (
auto* camA =
dynamic_cast<Camera*
>(ownerA)) {
138 posA = camA->GetPosition() + colliderA->GetOffset();
141 auto* transformB = ownerB->GetComponent<TransformComponent>();
143 posB = transformB->GetWorldPosition() + colliderB->GetOffset();
144 scaleB = transformB->GetWorldScale();
145 }
else if (
auto* camB =
dynamic_cast<Camera*
>(ownerB)) {
146 posB = camB->GetPosition() + colliderB->GetOffset();
150 colliderA->GetShape(), posA, scaleA,
151 colliderB->GetShape(), posB, scaleB);
153 if (!manifold.hasCollision)
return true;
156 if (colliderA->IsTrigger() && colliderB->IsTrigger())
return true;
159 auto* rbA = ownerA->GetComponent<RigidbodyComponent>();
160 auto* rbB = ownerB->GetComponent<RigidbodyComponent>();
163 rbA->ResolveCollision(manifold.contact.normal * -1.0f,
164 manifold.contact.penetration);
167 rbB->ResolveCollision(manifold.contact.normal,
168 manifold.contact.penetration);
177 std::vector<CollisionPair> results;
181 m_tree.Query(queryAABB, [&](
int proxyId) ->
bool {
183 if ((collider->GetLayer() & layerMask) == 0)
return true;
187 results.push_back(pair);
195 std::vector<CollisionPair> results;
197 m_tree.Query(aabb, [&](
int proxyId) ->
bool {
199 if ((collider->GetLayer() & layerMask) == 0)
return true;
203 results.push_back(pair);
211 float radius,
float maxDist, uint32_t layerMask)
const {
215 Vector3D end = start + direction * maxDist;
218 std::min(start.
GetY(), end.
GetY()) - radius,
219 std::min(start.
GetZ(), end.
GetZ()) - radius),
221 std::max(start.
GetY(), end.
GetY()) + radius,
222 std::max(start.
GetZ(), end.
GetZ()) + radius)
225 float closestDist = maxDist;
227 m_tree.Query(sweepAABB, [&](
int proxyId) ->
bool {
229 if ((collider->GetLayer() & layerMask) == 0)
return true;
232 AABB targetAABB = collider->GetWorldAABB();
239 targetAABB.
min.
GetY() - radius,
240 targetAABB.
min.
GetZ() - radius),
242 targetAABB.
max.
GetY() + radius,
243 targetAABB.
max.
GetZ() + radius)
248 std::abs(direction.
GetX()) > 1e-8f ? 1.0f / direction.
GetX() : 1e8f,
249 std::abs(direction.
GetY()) > 1e-8f ? 1.0f / direction.
GetY() : 1e8f,
250 std::abs(direction.
GetZ()) > 1e-8f ? 1.0f / direction.
GetZ() : 1e8f
253 float t1x = (expandedTarget.
min.
GetX() - start.
GetX()) * invDir.
GetX();
254 float t2x = (expandedTarget.
max.
GetX() - start.
GetX()) * invDir.
GetX();
255 float t1y = (expandedTarget.
min.
GetY() - start.
GetY()) * invDir.
GetY();
256 float t2y = (expandedTarget.
max.
GetY() - start.
GetY()) * invDir.
GetY();
257 float t1z = (expandedTarget.
min.
GetZ() - start.
GetZ()) * invDir.
GetZ();
258 float t2z = (expandedTarget.
max.
GetZ() - start.
GetZ()) * invDir.
GetZ();
260 float tmin = std::max({std::min(t1x, t2x), std::min(t1y, t2y), std::min(t1z, t2z)});
261 float tmax = std::min({std::max(t1x, t2x), std::max(t1y, t2y), std::max(t1z, t2z)});
263 if (tmax < 0 || tmin > tmax || tmin > closestDist)
return true;
265 float hitDist = std::max(tmin, 0.0f);
266 if (hitDist < closestDist) {
267 closestDist = hitDist;
271 result.
point = start + direction * hitDist;
275 Vector3D diff = hitPt - targetCenter;
278 float ax = std::abs(diff.
GetX()) / std::max(targetExtents.
GetX(), 0.001f);
279 float ay = std::abs(diff.
GetY()) / std::max(targetExtents.
GetY(), 0.001f);
280 float az = std::abs(diff.
GetZ()) / std::max(targetExtents.
GetZ(), 0.001f);
282 if (ax > ay && ax > az) {
284 }
else if (ay > az) {
298 float maxDist, uint32_t layerMask)
const {
Physics::AABB GetWorldAABB() const
Local shape transformed into world space by the owner's current transform.
void Query(const AABB &queryAABB, const std::function< bool(int)> &callback) const
Visits every leaf whose fat AABB overlaps queryAABB; stop early by returning false from callback.
bool MoveProxy(int proxyId, const AABB &newAABB, const Vector3D &displacement)
Refits a proxy's fat AABB to newAABB, re-inserting it only if it moved outside the fat margin.
void * GetUserData(int proxyId) const
std::vector< CollisionPair > OverlapSphere(const Vector3D ¢er, float radius, uint32_t layerMask=0xFFFFFFFF) const
Query API: colliders overlapping a sphere, filtered by layerMask.
void RegisterCollider(ColliderComponent *collider)
void UnregisterCollider(ColliderComponent *collider)
SweepResult SphereSweep(const Vector3D &start, const Vector3D &direction, float radius, float maxDist, uint32_t layerMask=0xFFFFFFFF) const
Sweeps a sphere from start along direction and returns the first collider it hits within maxDist.
RayHit Raycast(const Vector3D &origin, const Vector3D &direction, float maxDist, uint32_t layerMask=0xFFFFFFFF) const
Casts a ray and returns the closest collider hit within maxDist.
std::vector< CollisionPair > OverlapAABB(const AABB &aabb, uint32_t layerMask=0xFFFFFFFF) const
Colliders overlapping an AABB, filtered by layerMask.
Collision shapes, the broadphase tree, and the world that steps them.
CollisionManifold TestCollision(const ColliderShape &shapeA, const Vector3D &posA, const Vector3D &scaleA, const ColliderShape &shapeB, const Vector3D &posB, const Vector3D &scaleB)
Dispatches to the right narrow-phase test based on the runtime shape held by each variant.
Root namespace for everything the engine exposes.
Vector3D GetCenter() const
Vector3D GetExtents() const
ColliderComponent * collider
ColliderComponent * collider