void FPhysScene::SetKinematicTarget(FBodyInstance * BodyInstance, const FTransform & TargetTransform) { TargetTransform.DiagnosticCheckNaN_All(); #if WITH_PHYSX if (PxRigidDynamic * PRigidDynamic = BodyInstance->GetPxRigidDynamic()) { #if WITH_SUBSTEPPING if (IsSubstepping()) { FPhysSubstepTask * PhysSubStepper = PhysSubSteppers[SceneType(BodyInstance)]; PhysSubStepper->SetKinematicTarget(BodyInstance, TargetTransform); } else #endif { const PxTransform PNewPose = U2PTransform(TargetTransform); check(PNewPose.isValid()); SCOPED_SCENE_WRITE_LOCK(PRigidDynamic->getScene()); PRigidDynamic->setKinematicTarget(PNewPose); } } #endif }
/** Interpolates kinematic actor transform - Assumes caller has obtained writer lock */ void FPhysSubstepTask::InterpolateKinematicActor_AssumesLocked(const FPhysTarget& PhysTarget, FBodyInstance* BodyInstance, float InAlpha) { #if WITH_PHYSX PxRigidDynamic * PRigidDynamic = BodyInstance->GetPxRigidDynamic_AssumesLocked(); InAlpha = FMath::Clamp(InAlpha, 0.f, 1.f); /** Interpolate kinematic actors */ if (PhysTarget.bKinematicTarget) { //It's possible that the actor is no longer kinematic and is now simulating. In that case do nothing if (!BodyInstance->IsNonKinematic()) { const FKinematicTarget& KinematicTarget = PhysTarget.KinematicTarget; const FTransform& TargetTM = KinematicTarget.TargetTM; const FTransform& StartTM = KinematicTarget.OriginalTM; FTransform InterTM = FTransform::Identity; InterTM.SetLocation(FMath::Lerp(StartTM.GetLocation(), TargetTM.GetLocation(), InAlpha)); InterTM.SetRotation(FMath::Lerp(StartTM.GetRotation(), TargetTM.GetRotation(), InAlpha)); const PxTransform PNewPose = U2PTransform(InterTM); check(PNewPose.isValid()); PRigidDynamic->setKinematicTarget(PNewPose); } } #endif }
/** Interpolates kinematic actor transform - Assumes caller has obtained writer lock */ void FPhysSubstepTask::InterpolateKinematicActor(const FPhysTarget & PhysTarget, FBodyInstance * BodyInstance, float Alpha) { #if WITH_PHYSX PxRigidDynamic * PRigidDynamic = BodyInstance->GetPxRigidDynamic(); Alpha = FMath::Clamp(Alpha, 0.f, 1.f); /** Interpolate kinematic actors */ if (PhysTarget.bKinematicTarget) { //We should only be interpolating kinematic actors check(!IsRigidDynamicNonKinematic(PRigidDynamic)); const FKinematicTarget & KinematicTarget = PhysTarget.KinematicTarget; const FTransform & TargetTM = KinematicTarget.TargetTM; const FTransform & StartTM = KinematicTarget.OriginalTM; FTransform InterTM = FTransform::Identity; InterTM.SetLocation(FMath::Lerp(StartTM.GetLocation(), TargetTM.GetLocation(), Alpha)); InterTM.SetRotation(FMath::Lerp(StartTM.GetRotation(), TargetTM.GetRotation(), Alpha)); const PxTransform PNewPose = U2PTransform(InterTM); check(PNewPose.isValid()); PRigidDynamic->setKinematicTarget(PNewPose); } #endif }
void AddConvexElemsToRigidActor_AssumesLocked() const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); for (int32 i = 0; i < BodySetup->AggGeom.ConvexElems.Num(); i++) { const FKConvexElem& ConvexElem = BodySetup->AggGeom.ConvexElems[i]; if (ConvexElem.ConvexMesh) { PxTransform PLocalPose; bool bUseNegX = CalcMeshNegScaleCompensation(Scale3D, PLocalPose); PxConvexMeshGeometry PConvexGeom; PConvexGeom.convexMesh = bUseNegX ? ConvexElem.ConvexMeshNegX : ConvexElem.ConvexMesh; PConvexGeom.scale.scale = U2PVector(Scale3DAbs * ConvexElem.GetTransform().GetScale3D().GetAbs()); FTransform ConvexTransform = ConvexElem.GetTransform(); if (ConvexTransform.GetScale3D().X < 0 || ConvexTransform.GetScale3D().Y < 0 || ConvexTransform.GetScale3D().Z < 0) { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has negative scale. Not currently supported"), *GetPathNameSafe(BodySetup->GetOuter()), i); } if (ConvexTransform.IsValid()) { PxTransform PElementTransform = U2PTransform(ConvexTransform * RelativeTM); PLocalPose.q *= PElementTransform.q; PLocalPose.p = PElementTransform.p; PLocalPose.p.x *= Scale3D.X; PLocalPose.p.y *= Scale3D.Y; PLocalPose.p.z *= Scale3D.Z; if (PConvexGeom.isValid()) { PxVec3 PBoundsExtents = PConvexGeom.convexMesh->getLocalBounds().getExtents(); ensure(PLocalPose.isValid()); { const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PBoundsExtents.minElement()); AttachShape_AssumesLocked(PConvexGeom, PLocalPose, ContactOffset); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] invalid"), *GetPathNameSafe(BodySetup->GetOuter()), i); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has invalid transform"), *GetPathNameSafe(BodySetup->GetOuter()), i); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: ConvexElem is missing ConvexMesh (%d: %s)"), i, *BodySetup->GetPathName()); } } }
void UBodySetup::AddTriMeshToRigidActor(PxRigidActor* PDestActor, const FVector& Scale3D, const FVector& Scale3DAbs) const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); PxMaterial* PDefaultMat = GetDefaultPhysMaterial(); if(TriMesh || TriMeshNegX) { PxTransform PLocalPose; bool bUseNegX = CalcMeshNegScaleCompensation(Scale3D, PLocalPose); // Only case where TriMeshNegX should be null is BSP, which should not require negX version if (bUseNegX && TriMeshNegX == NULL) { UE_LOG(LogPhysics, Log, TEXT("AddTriMeshToRigidActor: Want to use NegX but it doesn't exist! %s"), *GetPathName()); } PxTriangleMesh* UseTriMesh = bUseNegX ? TriMeshNegX : TriMesh; if (UseTriMesh != NULL) { PxTriangleMeshGeometry PTriMeshGeom; PTriMeshGeom.triangleMesh = bUseNegX ? TriMeshNegX : TriMesh; PTriMeshGeom.scale.scale = U2PVector(Scale3DAbs); if (bDoubleSidedGeometry) { PTriMeshGeom.meshFlags |= PxMeshGeometryFlag::eDOUBLE_SIDED; } if (PTriMeshGeom.isValid()) { ensure(PLocalPose.isValid()); // Create without 'sim shape' flag, problematic if it's kinematic, and it gets set later anyway. PxShape* NewShape = PDestActor->createShape(PTriMeshGeom, *PDefaultMat, PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eVISUALIZATION); if (NewShape) { NewShape->setLocalPose(PLocalPose); NewShape->setContactOffset(MaxContactOffset); } else { UE_LOG(LogPhysics, Log, TEXT("Can't create new mesh shape in AddShapesToRigidActor")); } } else { UE_LOG(LogPhysics, Log, TEXT("AddTriMeshToRigidActor: TriMesh invalid")); } } } }
void NpBatchQuery::sweep( const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, PxU16 maxTouchHits, PxHitFlags hitFlags, const PxQueryFilterData& fd, void* userData, const PxQueryCache* cache, const PxReal inflation) { PX_CHECK_AND_RETURN(pose.isValid(), "Batch sweep input check: pose is not valid."); PX_CHECK_AND_RETURN(unitDir.isFinite(), "Batch sweep input check: unitDir is not valid."); PX_CHECK_AND_RETURN(unitDir.isNormalized(), "Batch sweep input check: direction must be normalized"); PX_CHECK_AND_RETURN(distance >= 0.0f, "Batch sweep input check: distance cannot be negative"); PX_CHECK_AND_RETURN(distance != 0.0f || !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP), "Batch sweep input check: zero-length sweep only valid without the PxHitFlag::eASSUME_NO_INITIAL_OVERLAP flag"); if (mNbSweeps >= mDesc.queryMemory.getMaxSweepsPerExecute()) { PX_CHECK_AND_RETURN(mNbSweeps < mDesc.queryMemory.getMaxSweepsPerExecute(), "PxBatchQuery: number of sweep() calls exceeds PxBatchQueryMemory::sweepResultBufferSize, query discarded"); return; } CHECK_RUNNING("PxBatchQuery::sweep: This batch is still executing, skipping query.") mNbSweeps++; writeBatchHeader(BatchStreamHeader(hitFlags, cache, fd, userData, maxTouchHits, QTypeROS::eSWEEP)); //set the MTD flag mHasMtdSweep |= !!(hitFlags & PxHitFlag::eMTD); if((hitFlags & PxHitFlag::ePRECISE_SWEEP) && (hitFlags & PxHitFlag::eMTD)) { Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support MTD. Perform MTD with default sweep"); hitFlags &= ~PxHitFlag::ePRECISE_SWEEP; } if((hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP) && (hitFlags & PxHitFlag::eMTD)) { Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " eMTD cannot be used in conjunction with eASSUME_NO_INITIAL_OVERLAP. eASSUME_NO_INITIAL_OVERLAP will be ignored"); hitFlags &= ~PxHitFlag::eASSUME_NO_INITIAL_OVERLAP; } PxReal realInflation = inflation; if((hitFlags & PxHitFlag::ePRECISE_SWEEP)&& inflation > 0.f) { realInflation = 0.f; Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support inflation, inflation will be overwritten to be zero"); } writeQueryInput(mStream, MultiQueryInput(&geometry, &pose, unitDir, distance, realInflation)); Ps::atomicExchange(&mBatchQueryIsRunning, 0); }
void AddTriMeshToRigidActor_AssumesLocked() const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); if (BodySetup->TriMesh || BodySetup->TriMeshNegX) { PxTransform PLocalPose; bool bUseNegX = CalcMeshNegScaleCompensation(Scale3D, PLocalPose); // Only case where TriMeshNegX should be null is BSP, which should not require negX version if (bUseNegX && BodySetup->TriMeshNegX == NULL) { UE_LOG(LogPhysics, Log, TEXT("AddTriMeshToRigidActor: Want to use NegX but it doesn't exist! %s"), *BodySetup->GetPathName()); } PxTriangleMesh* UseTriMesh = bUseNegX ? BodySetup->TriMeshNegX : BodySetup->TriMesh; if (UseTriMesh != NULL) { PxTriangleMeshGeometry PTriMeshGeom; PTriMeshGeom.triangleMesh = bUseNegX ? BodySetup->TriMeshNegX : BodySetup->TriMesh; PTriMeshGeom.scale.scale = U2PVector(Scale3DAbs); if (BodySetup->bDoubleSidedGeometry) { PTriMeshGeom.meshFlags |= PxMeshGeometryFlag::eDOUBLE_SIDED; } if (PTriMeshGeom.isValid()) { ensure(PLocalPose.isValid()); // Create without 'sim shape' flag, problematic if it's kinematic, and it gets set later anyway. { if (!AttachShape_AssumesLocked(PTriMeshGeom, PLocalPose, MaxContactOffset, PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eVISUALIZATION)) { UE_LOG(LogPhysics, Log, TEXT("Can't create new mesh shape in AddShapesToRigidActor")); } } } else { UE_LOG(LogPhysics, Log, TEXT("AddTriMeshToRigidActor: TriMesh invalid")); } } } }
void NpArticulationLink::setGlobalPose(const PxTransform& pose, bool autowake) { NpScene* scene = NpActor::getOwnerScene(*this); PX_CHECK_AND_RETURN(pose.isValid(), "NpArticulationLink::setGlobalPose pose is not valid."); NP_WRITE_CHECK(scene); #if PX_CHECKED if(scene) scene->checkPositionSanity(*this, pose, "PxArticulationLink::setGlobalPose"); #endif PxTransform body2World = pose * getScbBodyFast().getBody2Actor(); getScbBodyFast().setBody2World(body2World, false); if (scene && autowake) mRoot->wakeUpInternal(false, true); }
void NpBatchQuery::overlap( const PxGeometry& geometry, const PxTransform& pose, PxU16 maxTouchHits, const PxQueryFilterData& fd, void* userData, const PxQueryCache* cache) { PX_CHECK_AND_RETURN(pose.isValid(), "NpBatchQuery::overlapMultiple pose is not valid."); if (mNbOverlaps >= mDesc.queryMemory.getMaxOverlapsPerExecute()) { PX_CHECK_AND_RETURN(mNbOverlaps < mDesc.queryMemory.getMaxOverlapsPerExecute(), "PxBatchQuery: number of overlap() calls exceeds PxBatchQueryMemory::overlapResultBufferSize, query discarded"); return; } CHECK_RUNNING("PxBatchQuery::overlap: This batch is still executing, skipping query.") mNbOverlaps++; writeBatchHeader(BatchStreamHeader(PxHitFlags(), cache, fd, userData, maxTouchHits, QTypeROS::eOVERLAP)); writeQueryInput(mStream, MultiQueryInput(&geometry, &pose)); Ps::atomicExchange(&mBatchQueryIsRunning, 0); }
void FPhysScene::SetKinematicTarget_AssumesLocked(FBodyInstance* BodyInstance, const FTransform& TargetTransform, bool bAllowSubstepping) { TargetTransform.DiagnosticCheckNaN_All(); #if WITH_PHYSX if (PxRigidDynamic * PRigidDynamic = BodyInstance->GetPxRigidDynamic_AssumesLocked()) { #if WITH_SUBSTEPPING uint32 BodySceneType = SceneType_AssumesLocked(BodyInstance); if (bAllowSubstepping && IsSubstepping(BodySceneType)) { FPhysSubstepTask * PhysSubStepper = PhysSubSteppers[BodySceneType]; PhysSubStepper->SetKinematicTarget_AssumesLocked(BodyInstance, TargetTransform); } else #endif { const PxTransform PNewPose = U2PTransform(TargetTransform); check(PNewPose.isValid()); PRigidDynamic->setKinematicTarget(PNewPose); } } #endif }
bool physx::PxMeshQuery::sweep( const PxVec3& unitDir, const PxReal maxDistance, const PxGeometry& geom, const PxTransform& pose, PxU32 triangleCount, const PxTriangle* triangles, PxSweepHit& sweepHit, PxHitFlags hintFlags_, const PxU32* cachedIndex, const PxReal inflation, bool doubleSided) { PX_SIMD_GUARD; PX_CHECK_AND_RETURN_VAL(pose.isValid(), "Gu::GeometryQuery::sweep(): pose is not valid.", false); PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "Gu::GeometryQuery::sweep(): unitDir is not valid.", false); PX_CHECK_AND_RETURN_VAL(PxIsFinite(maxDistance), "Gu::GeometryQuery::sweep(): distance is not valid.", false); PX_CHECK_AND_RETURN_VAL(maxDistance > 0, "Gu::GeometryQuery::sweep(): sweep distance must be greater than 0.", false); const PxReal distance = PxMin(maxDistance, PX_MAX_SWEEP_DISTANCE); // PT: the doc says that validity flags are not used, but internally some functions still check them. So // to make sure the code works even when no validity flags are passed, we set them all here. const PxHitFlags hintFlags = hintFlags_ | PxHitFlag::ePOSITION|PxHitFlag::eNORMAL|PxHitFlag::eDISTANCE; switch(geom.getType()) { case PxGeometryType::eSPHERE: { const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom); // PT: TODO: technically this capsule with 0.0 half-height is invalid ("isValid" returns false) const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f); return SweepCapsuleTriangles( triangleCount, triangles, doubleSided, capsuleGeom, pose, unitDir, distance, cachedIndex, sweepHit.position, sweepHit.normal, sweepHit.distance, sweepHit.faceIndex, inflation, hintFlags); } case PxGeometryType::eCAPSULE: { const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom); return SweepCapsuleTriangles( triangleCount, triangles, doubleSided, capsuleGeom, pose, unitDir, distance, cachedIndex, sweepHit.position, sweepHit.normal, sweepHit.distance, sweepHit.faceIndex, inflation, hintFlags); } case PxGeometryType::eBOX: { const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom); if(!PX_IS_SPU && (hintFlags & PxHitFlag::ePRECISE_SWEEP)) { return sweepCCTBoxTriangles(triangleCount, triangles, doubleSided, boxGeom, pose, unitDir, distance, sweepHit.position, sweepHit.normal, sweepHit.distance, sweepHit.faceIndex, cachedIndex, inflation, hintFlags); } else { return SweepBoxTriangles( triangleCount, triangles, doubleSided, boxGeom, pose, unitDir, distance, sweepHit.position, sweepHit.normal, sweepHit.distance, sweepHit.faceIndex, cachedIndex, inflation, hintFlags); } } case PxGeometryType::ePLANE: case PxGeometryType::eCONVEXMESH: case PxGeometryType::eTRIANGLEMESH: case PxGeometryType::eHEIGHTFIELD: case PxGeometryType::eGEOMETRY_COUNT: case PxGeometryType::eINVALID: default : PX_CHECK_MSG(false, "Gu::GeometryQuery::sweep(): geometry object parameter must be sphere, capsule or box geometry."); } return false; }
void D6Joint::setDrivePosition(const PxTransform& pose) { PX_CHECK_AND_RETURN(pose.isValid(), "PxD6Joint::setDrivePosition: pose invalid"); data().drivePosition = pose; markDirty(); }
void UBodySetup::AddConvexElemsToRigidActor(PxRigidActor* PDestActor, const FTransform& RelativeTM, const FVector& Scale3D, const FVector& Scale3DAbs, TArray<PxShape*>* NewShapes) const { float ContactOffsetFactor, MaxContactOffset; GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset); PxMaterial* PDefaultMat = GetDefaultPhysMaterial(); for (int32 i = 0; i < AggGeom.ConvexElems.Num(); i++) { const FKConvexElem& ConvexElem = AggGeom.ConvexElems[i]; if (ConvexElem.ConvexMesh) { PxTransform PLocalPose; bool bUseNegX = CalcMeshNegScaleCompensation(Scale3D, PLocalPose); PxConvexMeshGeometry PConvexGeom; PConvexGeom.convexMesh = bUseNegX ? ConvexElem.ConvexMeshNegX : ConvexElem.ConvexMesh; PConvexGeom.scale.scale = U2PVector(Scale3DAbs * ConvexElem.GetTransform().GetScale3D().GetAbs()); FTransform ConvexTransform = ConvexElem.GetTransform(); if (ConvexTransform.GetScale3D().X < 0 || ConvexTransform.GetScale3D().Y < 0 || ConvexTransform.GetScale3D().Z < 0) { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has negative scale. Not currently supported"), *GetPathNameSafe(GetOuter()), i); } if (ConvexTransform.IsValid()) { PxTransform PElementTransform = U2PTransform(ConvexTransform * RelativeTM); PLocalPose.q *= PElementTransform.q; PLocalPose.p = PElementTransform.p; PLocalPose.p.x *= Scale3D.X; PLocalPose.p.y *= Scale3D.Y; PLocalPose.p.z *= Scale3D.Z; if (PConvexGeom.isValid()) { PxVec3 PBoundsExtents = PConvexGeom.convexMesh->getLocalBounds().getExtents(); ensure(PLocalPose.isValid()); PxShape* NewShape = PDestActor->createShape(PConvexGeom, *PDefaultMat, PLocalPose); if (NewShapes) { NewShapes->Add(NewShape); } const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PBoundsExtents.minElement()); NewShape->setContactOffset(ContactOffset); } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] invalid"), *GetPathNameSafe(GetOuter()), i); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has invalid transform"), *GetPathNameSafe(GetOuter()), i); } } else { UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: ConvexElem is missing ConvexMesh (%d: %s)"), i, *GetPathName()); } } }
void USkeletalMeshComponent::UpdateKinematicBonesToAnim(const TArray<FTransform>& InSpaceBases, ETeleportType Teleport, bool bNeedsSkinning) { SCOPE_CYCLE_COUNTER(STAT_UpdateRBBones); // This below code produces some interesting result here // - below codes update physics data, so if you don't update pose, the physics won't have the right result // - but if we just update physics bone without update current pose, it will have stale data // If desired, pass the animation data to the physics joints so they can be used by motors. // See if we are going to need to update kinematics const bool bUpdateKinematics = (KinematicBonesUpdateType != EKinematicBonesUpdateToPhysics::SkipAllBones); const bool bTeleport = Teleport == ETeleportType::TeleportPhysics; // If desired, update physics bodies associated with skeletal mesh component to match. if(!bUpdateKinematics && !(bTeleport && IsAnySimulatingPhysics())) { // nothing to do return; } // Get the scene, and do nothing if we can't get one. FPhysScene* PhysScene = nullptr; if (GetWorld() != nullptr) { PhysScene = GetWorld()->GetPhysicsScene(); } if(PhysScene == nullptr) { return; } const FTransform& CurrentLocalToWorld = ComponentToWorld; // Gracefully handle NaN if(CurrentLocalToWorld.ContainsNaN()) { return; } // If desired, draw the skeleton at the point where we pass it to the physics. if (bShowPrePhysBones && SkeletalMesh && InSpaceBases.Num() == SkeletalMesh->RefSkeleton.GetNum()) { for (int32 i = 1; i<InSpaceBases.Num(); i++) { FVector ThisPos = CurrentLocalToWorld.TransformPosition(InSpaceBases[i].GetLocation()); int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(i); FVector ParentPos = CurrentLocalToWorld.TransformPosition(InSpaceBases[ParentIndex].GetLocation()); GetWorld()->LineBatcher->DrawLine(ThisPos, ParentPos, AnimSkelDrawColor, SDPG_Foreground); } } // warn if it has non-uniform scale const FVector& MeshScale3D = CurrentLocalToWorld.GetScale3D(); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if( !MeshScale3D.IsUniform() ) { UE_LOG(LogPhysics, Log, TEXT("USkeletalMeshComponent::UpdateKinematicBonesToAnim : Non-uniform scale factor (%s) can cause physics to mismatch for %s SkelMesh: %s"), *MeshScale3D.ToString(), *GetFullName(), SkeletalMesh ? *SkeletalMesh->GetFullName() : TEXT("NULL")); } #endif if (bEnablePerPolyCollision == false) { const UPhysicsAsset* const PhysicsAsset = GetPhysicsAsset(); if (PhysicsAsset && SkeletalMesh && Bodies.Num() > 0) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (!ensure(PhysicsAsset->BodySetup.Num() == Bodies.Num())) { // related to TTP 280315 UE_LOG(LogPhysics, Warning, TEXT("Mesh (%s) has PhysicsAsset(%s), and BodySetup(%d) and Bodies(%d) don't match"), *SkeletalMesh->GetName(), *PhysicsAsset->GetName(), PhysicsAsset->BodySetup.Num(), Bodies.Num()); return; } #endif #if WITH_PHYSX // Lock the scenes we need (flags set in InitArticulated) if(bHasBodiesInSyncScene) { SCENE_LOCK_WRITE(PhysScene->GetPhysXScene(PST_Sync)) } if (bHasBodiesInAsyncScene) { SCENE_LOCK_WRITE(PhysScene->GetPhysXScene(PST_Async)) } #endif // Iterate over each body for (int32 i = 0; i < Bodies.Num(); i++) { // If we have a physics body, and its kinematic... FBodyInstance* BodyInst = Bodies[i]; check(BodyInst); if (bTeleport || (BodyInst->IsValidBodyInstance() && !BodyInst->IsInstanceSimulatingPhysics())) { const int32 BoneIndex = BodyInst->InstanceBoneIndex; // If we could not find it - warn. if (BoneIndex == INDEX_NONE || BoneIndex >= GetNumSpaceBases()) { const FName BodyName = PhysicsAsset->BodySetup[i]->BoneName; UE_LOG(LogPhysics, Log, TEXT("UpdateRBBones: WARNING: Failed to find bone '%s' need by PhysicsAsset '%s' in SkeletalMesh '%s'."), *BodyName.ToString(), *PhysicsAsset->GetName(), *SkeletalMesh->GetName()); } else { #if WITH_PHYSX // update bone transform to world const FTransform BoneTransform = InSpaceBases[BoneIndex] * CurrentLocalToWorld; if(BoneTransform.ContainsNaN()) { const FName BodyName = PhysicsAsset->BodySetup[i]->BoneName; UE_LOG(LogPhysics, Warning, TEXT("UpdateKinematicBonesToAnim: Trying to set transform with bad data %s on PhysicsAsset '%s' in SkeletalMesh '%s' for bone '%s'"), *BoneTransform.ToHumanReadableString(), *PhysicsAsset->GetName(), *SkeletalMesh->GetName(), *BodyName.ToString()); continue; } // If kinematic and not teleporting, set kinematic target PxRigidDynamic* PRigidDynamic = BodyInst->GetPxRigidDynamic_AssumesLocked(); if (!IsRigidBodyNonKinematic_AssumesLocked(PRigidDynamic) && !bTeleport) { PhysScene->SetKinematicTarget_AssumesLocked(BodyInst, BoneTransform, true); } // Otherwise, set global pose else { const PxTransform PNewPose = U2PTransform(BoneTransform); ensure(PNewPose.isValid()); PRigidDynamic->setGlobalPose(PNewPose); } #endif // now update scale // if uniform, we'll use BoneTranform if (MeshScale3D.IsUniform()) { // @todo UE4 should we update scale when it's simulated? BodyInst->UpdateBodyScale(BoneTransform.GetScale3D()); } else { // @note When you have non-uniform scale on mesh base, // hierarchical bone transform can update scale too often causing performance issue // So we just use mesh scale for all bodies when non-uniform // This means physics representation won't be accurate, but // it is performance friendly by preventing too frequent physics update BodyInst->UpdateBodyScale(MeshScale3D); } } } else { //make sure you have physics weight or blendphysics on, otherwise, you'll have inconsistent representation of bodies // @todo make this to be kismet log? But can be too intrusive if (!bBlendPhysics && BodyInst->PhysicsBlendWeight <= 0.f && BodyInst->BodySetup.IsValid()) { UE_LOG(LogPhysics, Warning, TEXT("%s(Mesh %s, PhysicsAsset %s, Bone %s) is simulating, but no blending. "), *GetName(), *GetNameSafe(SkeletalMesh), *GetNameSafe(PhysicsAsset), *BodyInst->BodySetup.Get()->BoneName.ToString()); } } } #if WITH_PHYSX // Unlock the scenes if (bHasBodiesInSyncScene) { SCENE_UNLOCK_WRITE(PhysScene->GetPhysXScene(PST_Sync)) } if (bHasBodiesInAsyncScene) { SCENE_UNLOCK_WRITE(PhysScene->GetPhysXScene(PST_Async)) } #endif } } else { //per poly update requires us to update all vertex positions if (MeshObject)