void ConvertQueryImpactHit(const UWorld* World, const PxLocationHit& PHit, FHitResult& OutResult, float CheckLength, const PxFilterData& QueryFilter, const FVector& StartLoc, const FVector& EndLoc, const PxGeometry* const Geom, const PxTransform& QueryTM, bool bReturnFaceIndex, bool bReturnPhysMat) { SCOPE_CYCLE_COUNTER(STAT_ConvertQueryImpactHit); checkSlow(PHit.flags & PxHitFlag::eDISTANCE); const bool bInitialOverlap = PHit.hadInitialOverlap(); if (bInitialOverlap && Geom != nullptr) { ConvertOverlappedShapeToImpactHit(World, PHit, StartLoc, EndLoc, OutResult, *Geom, QueryTM, QueryFilter, bReturnPhysMat); return; } // See if this is a 'blocking' hit const PxFilterData PShapeFilter = PHit.shape->getQueryFilterData(); const PxSceneQueryHitType::Enum HitType = FPxQueryFilterCallback::CalcQueryHitType(QueryFilter, PShapeFilter); OutResult.bBlockingHit = (HitType == PxSceneQueryHitType::eBLOCK); OutResult.bStartPenetrating = bInitialOverlap; // calculate the hit time const float HitTime = PHit.distance/CheckLength; OutResult.Time = HitTime; OutResult.Distance = PHit.distance; // figure out where the the "safe" location for this shape is by moving from the startLoc toward the ImpactPoint const FVector TraceStartToEnd = EndLoc - StartLoc; const FVector SafeLocationToFitShape = StartLoc + (HitTime * TraceStartToEnd); OutResult.Location = SafeLocationToFitShape; const bool bUsePxPoint = ((PHit.flags & PxHitFlag::ePOSITION) && !bInitialOverlap); OutResult.ImpactPoint = bUsePxPoint ? P2UVector(PHit.position) : StartLoc; // Caution: we may still have an initial overlap, but with null Geom. This is the case for RayCast results. const bool bUsePxNormal = ((PHit.flags & PxHitFlag::eNORMAL) && !bInitialOverlap); FVector Normal = bUsePxNormal ? P2UVector(PHit.normal).GetSafeNormal() : -TraceStartToEnd.GetSafeNormal(); OutResult.Normal = Normal; OutResult.ImpactNormal = Normal; OutResult.TraceStart = StartLoc; OutResult.TraceEnd = EndLoc; #if ENABLE_CHECK_HIT_NORMAL CheckHitResultNormal(OutResult, TEXT("Invalid Normal from ConvertQueryImpactHit"), StartLoc, EndLoc, Geom); #endif // ENABLE_CHECK_HIT_NORMAL if (bUsePxNormal && !Normal.IsNormalized()) { // TraceStartToEnd should never be zero, because of the length restriction in the raycast and sweep tests. Normal = -TraceStartToEnd.GetSafeNormal(); OutResult.Normal = Normal; OutResult.ImpactNormal = Normal; } const PxGeometryType::Enum SweptGeometryType = Geom ? Geom->getType() : PxGeometryType::eINVALID; OutResult.ImpactNormal = FindGeomOpposingNormal(SweptGeometryType, PHit, TraceStartToEnd, Normal); // Fill in Actor, Component, material, etc. SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat); if( PHit.shape->getGeometryType() == PxGeometryType::eHEIGHTFIELD) { // Lookup physical material for heightfields if (bReturnPhysMat && PHit.faceIndex != InvalidQueryHit.faceIndex) { PxMaterial* HitMaterial = PHit.shape->getMaterialFromInternalFaceIndex(PHit.faceIndex); if (HitMaterial != NULL) { OutResult.PhysMaterial = FPhysxUserData::Get<UPhysicalMaterial>(HitMaterial->userData); } } } else if(bReturnFaceIndex && PHit.shape->getGeometryType() == PxGeometryType::eTRIANGLEMESH) { PxTriangleMeshGeometry PTriMeshGeom; if( PHit.shape->getTriangleMeshGeometry(PTriMeshGeom) && PTriMeshGeom.triangleMesh != NULL && PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles() ) { OutResult.FaceIndex = PTriMeshGeom.triangleMesh->getTrianglesRemap()[PHit.faceIndex]; } } }
void ConvertQueryImpactHit(const PxLocationHit& PHit, FHitResult& OutResult, float CheckLength, const PxFilterData& QueryFilter, const FVector& StartLoc, const FVector& EndLoc, const PxGeometry* const Geom, const PxTransform& QueryTM, bool bReturnFaceIndex, bool bReturnPhysMat) { if (Geom != NULL && PHit.hadInitialOverlap()) { ConvertOverlappedShapeToImpactHit(PHit.shape, PHit.actor, StartLoc, EndLoc, OutResult, *Geom, QueryTM, QueryFilter, bReturnPhysMat, PHit.faceIndex); return; } SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat); // calculate the hit time const float HitTime = PHit.distance/CheckLength; // figure out where the the "safe" location for this shape is by moving from the startLoc toward the ImpactPoint const FVector TraceDir = EndLoc - StartLoc; const FVector SafeLocationToFitShape = StartLoc + (HitTime * TraceDir); // Other info OutResult.Location = SafeLocationToFitShape; OutResult.ImpactPoint = P2UVector(PHit.position); OutResult.Normal = P2UVector(PHit.normal); OutResult.ImpactNormal = OutResult.Normal; OutResult.TraceStart = StartLoc; OutResult.TraceEnd = EndLoc; OutResult.Time = HitTime; // See if this is a 'blocking' hit PxFilterData PShapeFilter = PHit.shape->getQueryFilterData(); PxSceneQueryHitType::Enum HitType = FPxQueryFilterCallback::CalcQueryHitType(QueryFilter, PShapeFilter); OutResult.bBlockingHit = (HitType == PxSceneQueryHitType::eBLOCK); OutResult.bStartPenetrating = (PxU32)(PHit.hadInitialOverlap()); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST || !WITH_EDITOR) CheckHitResultNormal(OutResult, TEXT("Invalid Normal from ConvertQueryImpactHit"), StartLoc, EndLoc, Geom); #endif // Special handling for swept-capsule results if(Geom && Geom->getType() == PxGeometryType::eCAPSULE) { const PxCapsuleGeometry* Capsule = static_cast<const PxCapsuleGeometry*>(Geom); // Compute better ImpactNormal. This is the same as Normal except when we hit convex/trimesh, and then its the most opposing normal of the geom at point of impact. FVector PointOnGeom(P2UVector(PHit.position)); if (FindGeomOpposingNormal(PHit, OutResult.ImpactNormal, PointOnGeom)) { ConvertConvexNormalToCapsuleNormal(Capsule->halfHeight, Capsule->radius, OutResult, &PointOnGeom); } else { ConvertConvexNormalToCapsuleNormal(Capsule->halfHeight, Capsule->radius, OutResult, NULL); OutResult.ImpactNormal = OutResult.Normal; } } else if (Geom && Geom->getType() == PxGeometryType::eSPHERE) { const PxSphereGeometry* Sphere = static_cast<const PxSphereGeometry*>(Geom); // Compute better ImpactNormal. This is the same as Normal except when we hit convex/trimesh, and then its the most opposing normal of the geom at point of impact. FVector PointOnGeom(P2UVector(PHit.position)); if (FindGeomOpposingNormal(PHit, OutResult.ImpactNormal, PointOnGeom)) { const FPlane GeomPlane(PointOnGeom, OutResult.ImpactNormal); const float DistFromPlane = FMath::Abs(GeomPlane.PlaneDot(OutResult.Location)); if (DistFromPlane >= Sphere->radius) { // Use the new (better) impact normal to compute a new impact point by projecting the sphere's location onto the geometry. OutResult.ImpactPoint = OutResult.Location - DistFromPlane * OutResult.ImpactNormal; } // Use the impact point to compute a better sphere surface normal (impact point to center of sphere). ConvertConvexNormalToSphereNormal(Sphere->radius, OutResult); } else { ConvertConvexNormalToSphereNormal(Sphere->radius, OutResult); OutResult.ImpactNormal = OutResult.Normal; } } if( PHit.shape->getGeometryType() == PxGeometryType::eHEIGHTFIELD) { // Lookup physical material for heightfields PxMaterial* HitMaterial = PHit.shape->getMaterialFromInternalFaceIndex(PHit.faceIndex); if( HitMaterial != NULL ) { OutResult.PhysMaterial = FPhysxUserData::Get<UPhysicalMaterial>(HitMaterial->userData); } } else if(bReturnFaceIndex && PHit.shape->getGeometryType() == PxGeometryType::eTRIANGLEMESH) { PxTriangleMeshGeometry PTriMeshGeom; PxConvexMeshGeometry PConvexMeshGeom; if( PHit.shape->getTriangleMeshGeometry(PTriMeshGeom) && PTriMeshGeom.triangleMesh != NULL && PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles() ) { OutResult.FaceIndex = PTriMeshGeom.triangleMesh->getTrianglesRemap()[PHit.faceIndex]; } else { OutResult.FaceIndex = INDEX_NONE; } } else { OutResult.FaceIndex = INDEX_NONE; } }
EConvertQueryResult ConvertQueryImpactHit(const UWorld* World, const PxLocationHit& PHit, FHitResult& OutResult, float CheckLength, const PxFilterData& QueryFilter, const FVector& StartLoc, const FVector& EndLoc, const PxGeometry* const Geom, const PxTransform& QueryTM, bool bReturnFaceIndex, bool bReturnPhysMat) { SCOPE_CYCLE_COUNTER(STAT_ConvertQueryImpactHit); #if WITH_EDITOR if(bReturnFaceIndex && World->IsGameWorld()) { if(!ensure(UPhysicsSettings::Get()->bSuppressFaceRemapTable == false)) { UE_LOG(LogPhysics, Error, TEXT("A scene query is relying on face indices, but bSuppressFaceRemapTable is true.")); bReturnFaceIndex = false; } } #endif checkSlow(PHit.flags & PxHitFlag::eDISTANCE); const bool bInitialOverlap = PHit.hadInitialOverlap(); if (bInitialOverlap && Geom != nullptr) { ConvertOverlappedShapeToImpactHit(World, PHit, StartLoc, EndLoc, OutResult, *Geom, QueryTM, QueryFilter, bReturnPhysMat); return EConvertQueryResult::Valid; } // See if this is a 'blocking' hit const PxFilterData PShapeFilter = PHit.shape->getQueryFilterData(); const PxQueryHitType::Enum HitType = FPxQueryFilterCallback::CalcQueryHitType(QueryFilter, PShapeFilter); OutResult.bBlockingHit = (HitType == PxQueryHitType::eBLOCK); OutResult.bStartPenetrating = bInitialOverlap; // calculate the hit time const float HitTime = PHit.distance/CheckLength; OutResult.Time = HitTime; OutResult.Distance = PHit.distance; // figure out where the the "safe" location for this shape is by moving from the startLoc toward the ImpactPoint const FVector TraceStartToEnd = EndLoc - StartLoc; const FVector SafeLocationToFitShape = StartLoc + (HitTime * TraceStartToEnd); OutResult.Location = SafeLocationToFitShape; const bool bUsePxPoint = ((PHit.flags & PxHitFlag::ePOSITION) && !bInitialOverlap); if (bUsePxPoint && !PHit.position.isFinite()) { #if ENABLE_NAN_DIAGNOSTIC SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat); UE_LOG(LogCore, Error, TEXT("ConvertQueryImpactHit() NaN details:\n>> Actor:%s (%s)\n>> Component:%s\n>> Item:%d\n>> BoneName:%s\n>> Time:%f\n>> Distance:%f\n>> Location:%s\n>> bIsBlocking:%d\n>> bStartPenetrating:%d"), *GetNameSafe(OutResult.GetActor()), OutResult.Actor.IsValid() ? *OutResult.GetActor()->GetPathName() : TEXT("no path"), *GetNameSafe(OutResult.GetComponent()), OutResult.Item, *OutResult.BoneName.ToString(), OutResult.Time, OutResult.Distance, *OutResult.Location.ToString(), OutResult.bBlockingHit ? 1 : 0, OutResult.bStartPenetrating ? 1 : 0); #endif // ENABLE_NAN_DIAGNOSTIC OutResult.Reset(); logOrEnsureNanError(TEXT("ConvertQueryImpactHit() received NaN/Inf for position: %.2f %.2f %.2f"), PHit.position.x, PHit.position.y, PHit.position.z); return EConvertQueryResult::Invalid; } OutResult.ImpactPoint = bUsePxPoint ? P2UVector(PHit.position) : StartLoc; // Caution: we may still have an initial overlap, but with null Geom. This is the case for RayCast results. const bool bUsePxNormal = ((PHit.flags & PxHitFlag::eNORMAL) && !bInitialOverlap); if (bUsePxNormal && !PHit.normal.isFinite()) { #if ENABLE_NAN_DIAGNOSTIC SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat); UE_LOG(LogCore, Error, TEXT("ConvertQueryImpactHit() NaN details:\n>> Actor:%s (%s)\n>> Component:%s\n>> Item:%d\n>> BoneName:%s\n>> Time:%f\n>> Distance:%f\n>> Location:%s\n>> bIsBlocking:%d\n>> bStartPenetrating:%d"), *GetNameSafe(OutResult.GetActor()), OutResult.Actor.IsValid() ? *OutResult.GetActor()->GetPathName() : TEXT("no path"), *GetNameSafe(OutResult.GetComponent()), OutResult.Item, *OutResult.BoneName.ToString(), OutResult.Time, OutResult.Distance, *OutResult.Location.ToString(), OutResult.bBlockingHit ? 1 : 0, OutResult.bStartPenetrating ? 1 : 0); #endif // ENABLE_NAN_DIAGNOSTIC OutResult.Reset(); logOrEnsureNanError(TEXT("ConvertQueryImpactHit() received NaN/Inf for normal: %.2f %.2f %.2f"), PHit.normal.x, PHit.normal.y, PHit.normal.z); return EConvertQueryResult::Invalid; } FVector Normal = bUsePxNormal ? P2UVector(PHit.normal).GetSafeNormal() : -TraceStartToEnd.GetSafeNormal(); OutResult.Normal = Normal; OutResult.ImpactNormal = Normal; OutResult.TraceStart = StartLoc; OutResult.TraceEnd = EndLoc; #if ENABLE_CHECK_HIT_NORMAL CheckHitResultNormal(OutResult, TEXT("Invalid Normal from ConvertQueryImpactHit"), StartLoc, EndLoc, Geom); #endif // ENABLE_CHECK_HIT_NORMAL if (bUsePxNormal && !Normal.IsNormalized()) { // TraceStartToEnd should never be zero, because of the length restriction in the raycast and sweep tests. Normal = -TraceStartToEnd.GetSafeNormal(); OutResult.Normal = Normal; OutResult.ImpactNormal = Normal; } const PxGeometryType::Enum SweptGeometryType = Geom ? Geom->getType() : PxGeometryType::eINVALID; OutResult.ImpactNormal = FindGeomOpposingNormal(SweptGeometryType, PHit, TraceStartToEnd, Normal); // Fill in Actor, Component, material, etc. SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat); PxGeometryType::Enum PGeomType = PHit.shape->getGeometryType(); if(PGeomType == PxGeometryType::eHEIGHTFIELD) { // Lookup physical material for heightfields if (bReturnPhysMat && PHit.faceIndex != InvalidQueryHit.faceIndex) { PxMaterial* HitMaterial = PHit.shape->getMaterialFromInternalFaceIndex(PHit.faceIndex); if (HitMaterial != NULL) { OutResult.PhysMaterial = FPhysxUserData::Get<UPhysicalMaterial>(HitMaterial->userData); } } } else if (bReturnFaceIndex && PGeomType == PxGeometryType::eTRIANGLEMESH) { PxTriangleMeshGeometry PTriMeshGeom; if( PHit.shape->getTriangleMeshGeometry(PTriMeshGeom) && PTriMeshGeom.triangleMesh != NULL && PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles() ) { if (const PxU32* TriangleRemap = PTriMeshGeom.triangleMesh->getTrianglesRemap()) { OutResult.FaceIndex = TriangleRemap[PHit.faceIndex]; } } } return EConvertQueryResult::Valid; }
void ConvertQueryImpactHit(const PxLocationHit& PHit, FHitResult& OutResult, float CheckLength, const PxFilterData& QueryFilter, const FVector& StartLoc, const FVector& EndLoc, const PxGeometry* const Geom, const PxTransform& QueryTM, bool bReturnFaceIndex, bool bReturnPhysMat) { checkSlow(PHit.flags & PxHitFlag::eDISTANCE); if (Geom != NULL && PHit.hadInitialOverlap()) { ConvertOverlappedShapeToImpactHit(PHit, StartLoc, EndLoc, OutResult, *Geom, QueryTM, QueryFilter, bReturnPhysMat); return; } SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat); // calculate the hit time const float HitTime = PHit.distance/CheckLength; // figure out where the the "safe" location for this shape is by moving from the startLoc toward the ImpactPoint const FVector TraceStartToEnd = EndLoc - StartLoc; const FVector SafeLocationToFitShape = StartLoc + (HitTime * TraceStartToEnd); // Other info OutResult.Location = SafeLocationToFitShape; OutResult.ImpactPoint = (PHit.flags & PxHitFlag::ePOSITION) ? P2UVector(PHit.position) : StartLoc; OutResult.Normal = (PHit.flags & PxHitFlag::eNORMAL) ? P2UVector(PHit.normal) : -TraceStartToEnd.GetSafeNormal(); OutResult.ImpactNormal = OutResult.Normal; OutResult.TraceStart = StartLoc; OutResult.TraceEnd = EndLoc; OutResult.Time = HitTime; // See if this is a 'blocking' hit PxFilterData PShapeFilter = PHit.shape->getQueryFilterData(); PxSceneQueryHitType::Enum HitType = FPxQueryFilterCallback::CalcQueryHitType(QueryFilter, PShapeFilter); OutResult.bBlockingHit = (HitType == PxSceneQueryHitType::eBLOCK); OutResult.bStartPenetrating = (PxU32)(PHit.hadInitialOverlap()); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST || !WITH_EDITOR) CheckHitResultNormal(OutResult, TEXT("Invalid Normal from ConvertQueryImpactHit"), StartLoc, EndLoc, Geom); #endif PxGeometryType::Enum GeometryType = Geom ? Geom->getType() : PxGeometryType::eINVALID; // Special handling for swept-capsule results if (GeometryType == PxGeometryType::eCAPSULE || GeometryType == PxGeometryType::eSPHERE) { FindGeomOpposingNormal(PHit, TraceStartToEnd, OutResult.ImpactNormal); } if( PHit.shape->getGeometryType() == PxGeometryType::eHEIGHTFIELD) { // Lookup physical material for heightfields if (PHit.faceIndex != InvalidQueryHit.faceIndex) { PxMaterial* HitMaterial = PHit.shape->getMaterialFromInternalFaceIndex(PHit.faceIndex); if (HitMaterial != NULL) { OutResult.PhysMaterial = FPhysxUserData::Get<UPhysicalMaterial>(HitMaterial->userData); } } } else if(bReturnFaceIndex && PHit.shape->getGeometryType() == PxGeometryType::eTRIANGLEMESH) { PxTriangleMeshGeometry PTriMeshGeom; if( PHit.shape->getTriangleMeshGeometry(PTriMeshGeom) && PTriMeshGeom.triangleMesh != NULL && PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles() ) { OutResult.FaceIndex = PTriMeshGeom.triangleMesh->getTrianglesRemap()[PHit.faceIndex]; } else { OutResult.FaceIndex = INDEX_NONE; } } else { OutResult.FaceIndex = INDEX_NONE; } }