/* * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include namespace PhysX { namespace SceneQueryHelpers { physx::PxQueryFlags GetPxQueryFlags(const AzPhysics::SceneQuery::QueryType& queryType) { physx::PxQueryFlags queryFlags = physx::PxQueryFlag::ePREFILTER; switch (queryType) { case AzPhysics::SceneQuery::QueryType::StaticAndDynamic: queryFlags |= physx::PxQueryFlag::eSTATIC | physx::PxQueryFlag::eDYNAMIC; break; case AzPhysics::SceneQuery::QueryType::Dynamic: queryFlags |= physx::PxQueryFlag::eDYNAMIC; break; case AzPhysics::SceneQuery::QueryType::Static: queryFlags |= physx::PxQueryFlag::eSTATIC; break; default: AZ_Warning("Physics::World", false, "Unhandled queryType"); break; } return queryFlags; } AzPhysics::SceneQueryHit GetHitFromPxHit(const physx::PxLocationHit& pxHit) { AzPhysics::SceneQueryHit hit; hit.m_distance = pxHit.distance; hit.m_resultFlags |= AzPhysics::SceneQuery::ResultFlags::Distance; if (pxHit.flags & physx::PxHitFlag::ePOSITION) { hit.m_position = PxMathConvert(pxHit.position); hit.m_resultFlags |= AzPhysics::SceneQuery::ResultFlags::Position; } if (pxHit.flags & physx::PxHitFlag::eNORMAL) { hit.m_normal = PxMathConvert(pxHit.normal); hit.m_resultFlags |= AzPhysics::SceneQuery::ResultFlags::Normal; } const ActorData* actorData = Utils::GetUserData(pxHit.actor); hit.m_bodyHandle = actorData->GetBodyHandle(); if (hit.m_bodyHandle != AzPhysics::InvalidSimulatedBodyHandle) { hit.m_resultFlags |= AzPhysics::SceneQuery::ResultFlags::BodyHandle; } hit.m_entityId = actorData->GetEntityId(); if (hit.m_entityId.IsValid()) { hit.m_resultFlags |= AzPhysics::SceneQuery::ResultFlags::EntityId; } hit.m_shape = Utils::GetUserData(pxHit.shape); if (hit.m_shape != nullptr) { hit.m_resultFlags |= AzPhysics::SceneQuery::ResultFlags::Shape; } if (pxHit.faceIndex != 0xFFFFffff) { PHYSX_SCENE_READ_LOCK(pxHit.actor->getScene()); hit.m_material = Utils::GetUserData(pxHit.shape->getMaterialFromInternalFaceIndex(pxHit.faceIndex)); } else if (hit.m_shape != nullptr) { hit.m_material = hit.m_shape->GetMaterial().get(); } if (hit.m_material != nullptr) { hit.m_resultFlags |= AzPhysics::SceneQuery::ResultFlags::Material; } return hit; } AzPhysics::SceneQueryHit GetHitFromPxOverlapHit(const physx::PxOverlapHit& pxHit) { AzPhysics::SceneQueryHit hit; if (ActorData* actorData = Utils::GetUserData(pxHit.actor)) { hit.m_entityId = actorData->GetEntityId(); if (hit.m_entityId.IsValid()) { hit.m_resultFlags |= AzPhysics::SceneQuery::ResultFlags::EntityId; } hit.m_bodyHandle = actorData->GetBodyHandle(); if (hit.m_bodyHandle != AzPhysics::InvalidSimulatedBodyHandle) { hit.m_resultFlags |= AzPhysics::SceneQuery::ResultFlags::BodyHandle; } if (pxHit.shape != nullptr) { hit.m_shape = static_cast(pxHit.shape->userData); hit.m_resultFlags |= AzPhysics::SceneQuery::ResultFlags::Shape; } } return hit; } physx::PxHitFlags GetPxHitFlags(AzPhysics::SceneQuery::HitFlags hitFlags) { static_assert( static_cast(AzPhysics::SceneQuery::HitFlags::Position) == static_cast(physx::PxHitFlag::ePOSITION) && static_cast(AzPhysics::SceneQuery::HitFlags::Normal) == static_cast(physx::PxHitFlag::eNORMAL) && static_cast(AzPhysics::SceneQuery::HitFlags::UV) == static_cast(physx::PxHitFlag::eUV) && static_cast(AzPhysics::SceneQuery::HitFlags::AssumeNoInitialOverlap) == static_cast(physx::PxHitFlag::eASSUME_NO_INITIAL_OVERLAP) && static_cast(AzPhysics::SceneQuery::HitFlags::MeshMultiple) == static_cast(physx::PxHitFlag::eMESH_MULTIPLE) && static_cast(AzPhysics::SceneQuery::HitFlags::MeshAny) == static_cast(physx::PxHitFlag::eMESH_ANY) && static_cast(AzPhysics::SceneQuery::HitFlags::MeshBothSides) == static_cast(physx::PxHitFlag::eMESH_BOTH_SIDES) && static_cast(AzPhysics::SceneQuery::HitFlags::PreciseSweep) == static_cast(physx::PxHitFlag::ePRECISE_SWEEP) && static_cast(AzPhysics::SceneQuery::HitFlags::MTD) == static_cast(physx::PxHitFlag::eMTD) && static_cast(AzPhysics::SceneQuery::HitFlags::FaceIndex) == static_cast(physx::PxHitFlag::eFACE_INDEX), "Physics::HitFlags values do not match the corresponding PhysX ones."); return static_cast(static_cast(hitFlags)); } physx::PxQueryHitType::Enum GetPxHitType(AzPhysics::SceneQuery::QueryHitType hitType) { static_assert(static_cast(AzPhysics::SceneQuery::QueryHitType::None) == static_cast(physx::PxQueryHitType::eNONE) && static_cast(AzPhysics::SceneQuery::QueryHitType::Touch) == static_cast(physx::PxQueryHitType::eTOUCH) && static_cast(AzPhysics::SceneQuery::QueryHitType::Block) == static_cast(physx::PxQueryHitType::eBLOCK), "PhysX hit types do not match QueryHitTypes"); return static_cast(hitType); } AzPhysics::SceneQueryHit ClosestRayHitAgainstPxRigidActor( const AzPhysics::RayCastRequest& worldSpaceRequest, physx::PxRigidActor* actor) { const physx::PxVec3 start = PxMathConvert(worldSpaceRequest.m_start); const physx::PxVec3 unitDir = PxMathConvert(worldSpaceRequest.m_direction.GetNormalized()); const physx::PxU32 maxHits = 1; const physx::PxHitFlags hitFlags = GetPxHitFlags(worldSpaceRequest.m_hitFlags); AzPhysics::SceneQueryHit closestHit; float closestHitDistance = FLT_MAX; int numShapes = actor->getNbShapes(); AZStd::vector shapes(numShapes); actor->getShapes(shapes.data(), numShapes); { physx::PxScene* scene = actor->getScene(); PHYSX_SCENE_READ_LOCK(scene); const physx::PxTransform actorTransform = actor->getGlobalPose(); for (physx::PxShape* shape : shapes) { const physx::PxTransform shapeTransform = actorTransform * shape->getLocalPose(); physx::PxRaycastHit pxHitInfo; const bool hit = physx::PxGeometryQuery::raycast(start, unitDir, shape->getGeometry().any(), shapeTransform, worldSpaceRequest.m_distance, hitFlags, maxHits, &pxHitInfo); if (hit && pxHitInfo.distance < closestHitDistance) { // Fill actor and shape, as they won't be filled from PxGeometryQuery pxHitInfo.actor = actor; pxHitInfo.shape = shape; closestHit = GetHitFromPxHit(pxHitInfo); closestHitDistance = pxHitInfo.distance; } } } return closestHit; } AzPhysics::SceneQueryHit ClosestRayHitAgainstShapes(const AzPhysics::RayCastRequest& request, const AZStd::vector>& shapes, const AZ::Transform& parentTransform) { AzPhysics::SceneQueryHit closestHit; float closestHitDist = AZStd::numeric_limits::max(); for (auto& shape : shapes) { AzPhysics::SceneQueryHit hit = shape->RayCast(request, parentTransform); if (hit && hit.m_distance < closestHitDist) { closestHit = hit; closestHitDist = hit.m_distance; } } return closestHit; } AzPhysics::SceneQuery::FilterCallback GetSceneQueryBlockFilterCallback(const AzPhysics::SceneQuery::FilterCallback& filterCallback) { if (!filterCallback) { return nullptr; } return [filterCallback](const AzPhysics::SimulatedBody* body, const Physics::Shape* shape) { if (filterCallback(body, shape) != AzPhysics::SceneQuery::QueryHitType::None) { return AzPhysics::SceneQuery::QueryHitType::Block; } return AzPhysics::SceneQuery::QueryHitType::None; }; } AzPhysics::SceneQuery::FilterCallback GetFilterCallbackFromOverlap(const AzPhysics::SceneQuery::OverlapFilterCallback& overlapFilterCallback) { if (!overlapFilterCallback) { return nullptr; } return [overlapFilterCallback](const AzPhysics::SimulatedBody* body, const Physics::Shape* shape) { if (overlapFilterCallback(body, shape)) { return AzPhysics::SceneQuery::QueryHitType::Touch; } return AzPhysics::SceneQuery::QueryHitType::None; }; } PhysXQueryFilterCallback::PhysXQueryFilterCallback( const AzPhysics::CollisionGroup& collisionGroup, AzPhysics::SceneQuery::FilterCallback filterCallback, physx::PxQueryHitType::Enum hitType) : m_filterCallback(AZStd::move(filterCallback)) , m_collisionGroup(collisionGroup) , m_hitType(hitType) { } //Performs game specific entity filtering physx::PxQueryHitType::Enum PhysXQueryFilterCallback::preFilter( [[maybe_unused]] const physx::PxFilterData& queryFilterData, const physx::PxShape* pxShape, const physx::PxRigidActor* actor, [[maybe_unused]] physx::PxHitFlags& queryTypes) { auto shapeFilterData = pxShape->getQueryFilterData(); if (m_collisionGroup.GetMask() & PhysX::Collision::Combine(shapeFilterData.word0, shapeFilterData.word1)) { if (m_filterCallback) { ActorData* userData = Utils::GetUserData(actor); Physics::Shape* shape = Utils::GetUserData(pxShape); if (userData != nullptr && userData->GetSimulatedBody()) { return GetPxHitType(m_filterCallback(userData->GetSimulatedBody(), shape)); } } return m_hitType; } return physx::PxQueryHitType::eNONE; } // Unused, we're only pre-filtering at this time physx::PxQueryHitType::Enum PhysXQueryFilterCallback::postFilter( [[maybe_unused]] const physx::PxFilterData&, [[maybe_unused]] const physx::PxQueryHit&) { return physx::PxQueryHitType::eNONE; } UnboundedOverlapCallback::UnboundedOverlapCallback(const AzPhysics::SceneQuery::UnboundedOverlapHitCallback& hitCallback, AZStd::vector& hitBuffer) : m_hitCallback(hitCallback) , physx::PxHitCallback(hitBuffer.begin(), static_cast(hitBuffer.size())) {} physx::PxAgain UnboundedOverlapCallback::processTouches(const physx::PxOverlapHit* buffer, physx::PxU32 numHits) { for (auto it = buffer; it != buffer + numHits; ++it) { const AzPhysics::SceneQueryHit hit = SceneQueryHelpers::GetHitFromPxOverlapHit(*it); if (hit.IsValid() && !m_hitCallback(AZStd::optional(hit))) { return false; } m_results.m_hits.emplace_back(hit); } return true; }; void UnboundedOverlapCallback::finalizeQuery() { m_hitCallback({}); } } // namespace SceneQuery }