void USkeletalMeshComponent::SetSkeletalMesh(USkeletalMesh* InSkelMesh)
{
	if (InSkelMesh == SkeletalMesh)
	{
		// do nothing if the input mesh is the same mesh we're already using.
		return;
	}

	UPhysicsAsset* OldPhysAsset = GetPhysicsAsset();

	Super::SetSkeletalMesh(InSkelMesh);

#if WITH_EDITOR
	ValidateAnimation();
#endif

	if (GetPhysicsAsset() != OldPhysAsset && IsPhysicsStateCreated())
	{
		RecreatePhysicsState();
	}

	UpdateHasValidBodies();

	InitAnim(false);

#if WITH_APEX_CLOTHING
	ValidateClothingActors();
#endif
}
FVector USkeletalMeshComponent::GetClosestCollidingRigidBodyLocation(const FVector& TestLocation) const
{
	float BestDistSq = BIG_NUMBER;
	FVector Best = TestLocation;

	UPhysicsAsset* PhysicsAsset = GetPhysicsAsset();
	if( PhysicsAsset )
	{
		for (int32 i=0; i<Bodies.Num(); i++)
		{
			FBodyInstance* BodyInstance = Bodies[i];
			if( BodyInstance && BodyInstance->IsValidBodyInstance() && (BodyInstance->GetCollisionEnabled() != ECollisionEnabled::NoCollision) )
			{
				const FVector BodyLocation = BodyInstance->GetUnrealWorldTransform().GetTranslation();
				const float DistSq = (BodyLocation - TestLocation).SizeSquared();
				if( DistSq < BestDistSq )
				{
					Best = BodyLocation;
					BestDistSq = DistSq;
				}
			}
		}
	}

	return Best;
}
bool UDebugSkelMeshComponent::CheckIfBoundsAreCorrrect()
{
	if (GetPhysicsAsset())
	{
		bool bWasUsingInGameBounds = IsUsingInGameBounds();
		FTransform TempTransform = FTransform::Identity;
		UseInGameBounds(true);
		FBoxSphereBounds InGameBounds = CalcBounds(TempTransform);
		UseInGameBounds(false);
		FBoxSphereBounds PreviewBounds = CalcBounds(TempTransform);
		UseInGameBounds(bWasUsingInGameBounds);
		// calculate again to have bounds as requested
		CalcBounds(TempTransform);
		// if in-game bounds are of almost same size as preview bounds or bigger, it seems to be fine
		if (! InGameBounds.GetSphere().IsInside(PreviewBounds.GetSphere(), PreviewBounds.GetSphere().W * 0.1f) && // for spheres: A.IsInside(B) checks if A is inside of B
			! PreviewBounds.GetBox().IsInside(InGameBounds.GetBox().ExpandBy(PreviewBounds.GetSphere().W * 0.1f))) // for boxes: A.IsInside(B) checks if B is inside of A
		{
			return true;
		}
	}
	return false;
}
Пример #4
0
void UPhATEdSkeletalMeshComponent::RenderAssetTools(const FSceneView* View, class FPrimitiveDrawInterface* PDI, bool bHitTest)
{
	check(SharedData);

	UPhysicsAsset* const PhysicsAsset = GetPhysicsAsset();
	check(PhysicsAsset);

	bool bHitTestAndBodyMode = bHitTest && SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit;
	bool bHitTestAndConstraintMode = bHitTest && SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit;

	FPhATSharedData::EPhATRenderMode CollisionViewMode = SharedData->GetCurrentCollisionViewMode();

#if DEBUG_CLICK_VIEWPORT
	PDI->DrawLine(SharedData->LastClickOrigin, SharedData->LastClickOrigin + SharedData->LastClickDirection * 5000.0f, FLinearColor(1, 1, 0, 1), SDPG_Foreground);
	PDI->DrawPoint(SharedData->LastClickOrigin, FLinearColor(1, 0, 0), 5, SDPG_Foreground);
#endif
	// Draw bodies
	for (int32 i = 0; i <PhysicsAsset->BodySetup.Num(); ++i)
	{
		int32 BoneIndex = GetBoneIndex(PhysicsAsset->BodySetup[i]->BoneName);

		// If we found a bone for it, draw the collision.
		// The logic is as follows; always render in the ViewMode requested when not in hit mode - but if we are in hit mode and the right editing mode, render as solid
		if (BoneIndex != INDEX_NONE)
		{
			FTransform BoneTM = GetBoneTransform(BoneIndex);
			float Scale = BoneTM.GetScale3D().GetAbsMax();
			FVector VectorScale(Scale);
			BoneTM.RemoveScaling();

			FKAggregateGeom* AggGeom = &PhysicsAsset->BodySetup[i]->AggGeom;

			for (int32 j = 0; j <AggGeom->SphereElems.Num(); ++j)
			{
				if (bHitTest)
				{
					PDI->SetHitProxy(new HPhATEdBoneProxy(i, KPT_Sphere, j));
				}

				FTransform ElemTM = GetPrimitiveTransform(BoneTM, i, KPT_Sphere, j, Scale);

				//solids are drawn if it's the ViewMode and we're not doing a hit, or if it's hitAndBodyMode
				if( (CollisionViewMode == FPhATSharedData::PRM_Solid && !bHitTest) || bHitTestAndBodyMode)
				{
					UMaterialInterface*	PrimMaterial = GetPrimitiveMaterial(i, KPT_Sphere, j, bHitTestAndBodyMode);
					AggGeom->SphereElems[j].DrawElemSolid(PDI, ElemTM, VectorScale, PrimMaterial->GetRenderProxy(0));
				}

				//wires are never used during hit
				if(!bHitTest)
				{
					if (CollisionViewMode == FPhATSharedData::PRM_Solid || CollisionViewMode == FPhATSharedData::PRM_Wireframe)
					{
						AggGeom->SphereElems[j].DrawElemWire(PDI, ElemTM, VectorScale, GetPrimitiveColor(i, KPT_Sphere, j));
					}
				}

				if (bHitTest)
				{
					PDI->SetHitProxy(NULL);
				}
				
			}

			for (int32 j = 0; j <AggGeom->BoxElems.Num(); ++j)
			{
				if (bHitTest)
				{
					PDI->SetHitProxy(new HPhATEdBoneProxy(i, KPT_Box, j));
				}

				FTransform ElemTM = GetPrimitiveTransform(BoneTM, i, KPT_Box, j, Scale);

				if ( (CollisionViewMode == FPhATSharedData::PRM_Solid && !bHitTest) || bHitTestAndBodyMode)
				{
					UMaterialInterface*	PrimMaterial = GetPrimitiveMaterial(i, KPT_Box, j, bHitTestAndBodyMode);
					AggGeom->BoxElems[j].DrawElemSolid(PDI, ElemTM, VectorScale, PrimMaterial->GetRenderProxy(0));
				}

				if(!bHitTest)
				{
					if (CollisionViewMode == FPhATSharedData::PRM_Solid || CollisionViewMode == FPhATSharedData::PRM_Wireframe)
					{
						AggGeom->BoxElems[j].DrawElemWire(PDI, ElemTM, VectorScale, GetPrimitiveColor(i, KPT_Box, j));
					}
				}
				

				if (bHitTest) 
				{
					PDI->SetHitProxy(NULL);
				}
			}

			for (int32 j = 0; j <AggGeom->SphylElems.Num(); ++j)
			{
				if (bHitTest) 
				{
					PDI->SetHitProxy(new HPhATEdBoneProxy(i, KPT_Sphyl, j));
				}

				FTransform ElemTM = GetPrimitiveTransform(BoneTM, i, KPT_Sphyl, j, Scale);

				if ( (CollisionViewMode == FPhATSharedData::PRM_Solid && !bHitTest) || bHitTestAndBodyMode)
				{
					UMaterialInterface*	PrimMaterial = GetPrimitiveMaterial(i, KPT_Sphyl, j, bHitTestAndBodyMode);
					AggGeom->SphylElems[j].DrawElemSolid(PDI, ElemTM, VectorScale, PrimMaterial->GetRenderProxy(0));
				}

				if(!bHitTest)
				{
					if (CollisionViewMode == FPhATSharedData::PRM_Solid || CollisionViewMode == FPhATSharedData::PRM_Wireframe)
					{
						AggGeom->SphylElems[j].DrawElemWire(PDI, ElemTM, VectorScale, GetPrimitiveColor(i, KPT_Sphyl, j));
					}
				}

				if (bHitTest) 
				{
					PDI->SetHitProxy(NULL);
				}
			}

			for (int32 j = 0; j <AggGeom->ConvexElems.Num(); ++j)
			{
				if (bHitTest) 
				{
					PDI->SetHitProxy(new HPhATEdBoneProxy(i, KPT_Convex, j));
				}

				FTransform ElemTM = GetPrimitiveTransform(BoneTM, i, KPT_Convex, j, Scale);

				//convex doesn't have solid draw so render lines if we're in hitTestAndBodyMode
				if(!bHitTest || bHitTestAndBodyMode)
				{
					if (CollisionViewMode == FPhATSharedData::PRM_Solid || CollisionViewMode == FPhATSharedData::PRM_Wireframe)
					{
						AggGeom->ConvexElems[j].DrawElemWire(PDI, ElemTM, Scale, GetPrimitiveColor(i, KPT_Convex, j));
					}
				}
				

				if (bHitTest) 
				{
					PDI->SetHitProxy(NULL);
				}
			}

			if (!bHitTest && SharedData->bShowCOM && Bodies.IsValidIndex(i))
			{
				Bodies[i]->DrawCOMPosition(PDI, COMRenderSize, SharedData->COMRenderColor);
			}
		}
	}

	// Draw Constraints
	FPhATSharedData::EPhATConstraintViewMode ConstraintViewMode = SharedData->GetCurrentConstraintViewMode();
	if (ConstraintViewMode != FPhATSharedData::PCV_None)
	{
		for (int32 i = 0; i <PhysicsAsset->ConstraintSetup.Num(); ++i)
		{
			int32 BoneIndex1 = GetBoneIndex(PhysicsAsset->ConstraintSetup[i]->DefaultInstance.ConstraintBone1);
			int32 BoneIndex2 = GetBoneIndex(PhysicsAsset->ConstraintSetup[i]->DefaultInstance.ConstraintBone2);
			// if bone doesn't exist, do not draw it. It crashes in random points when we try to manipulate. 
			if (BoneIndex1 != INDEX_NONE && BoneIndex2 != INDEX_NONE)
			{
				if (bHitTest) 
				{
					PDI->SetHitProxy(new HPhATEdConstraintProxy(i));
				}

				if(bHitTestAndConstraintMode || !bHitTest)
				{
					DrawConstraint(i, View, PDI, SharedData->EditorSimOptions->bShowConstraintsAsPoints);
				}
					
				if (bHitTest) 
				{
					PDI->SetHitProxy(NULL);
				}
			}
		}
	}

	if (!bHitTest && SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit && SharedData->bShowInfluences)
	{
		DrawCurrentInfluences(PDI);
	}

	// If desired, draw bone hierarchy.
	if (!bHitTest && SharedData->bShowHierarchy)
	{
		DrawHierarchy(PDI, false);
	}

	// If desired, draw animation skeleton.
	if (!bHitTest && SharedData->bShowAnimSkel)
	{
		DrawHierarchy(PDI, SharedData->bRunningSimulation);
	}
}
Пример #5
0
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)
Пример #6
0
void USkeletalMeshComponent::PerformBlendPhysicsBones(const TArray<FBoneIndexType>& InRequiredBones, TArray<FTransform>& InLocalAtoms)
{
	// Get drawscale from Owner (if there is one)
	FVector TotalScale3D = ComponentToWorld.GetScale3D();
	FVector RecipScale3D = TotalScale3D.Reciprocal();

	UPhysicsAsset * const PhysicsAsset = GetPhysicsAsset();
	check( PhysicsAsset );

	if (GetNumSpaceBases() == 0)
	{
		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;
	}

	// Make sure scratch space is big enough.
	TArray<FAssetWorldBoneTM> WorldBoneTMs;
	WorldBoneTMs.Reset();
	WorldBoneTMs.AddZeroed(GetNumSpaceBases());
	
	FTransform LocalToWorldTM = ComponentToWorld;
	LocalToWorldTM.RemoveScaling();

	TArray<FTransform>& EditableSpaceBases = GetEditableSpaceBases();

	struct FBodyTMPair
	{
		FBodyInstance* BI;
		FTransform TM;
	};

	TArray<FBodyTMPair> PendingBodyTMs;

#if WITH_PHYSX
	// Lock the scenes we need (flags set in InitArticulated)
	if (bHasBodiesInSyncScene)
	{
		SCENE_LOCK_READ(PhysScene->GetPhysXScene(PST_Sync))
	}

	if (bHasBodiesInAsyncScene)
	{
		SCENE_LOCK_READ(PhysScene->GetPhysXScene(PST_Async))
	}
#endif

	// For each bone - see if we need to provide some data for it.
	for(int32 i=0; i<InRequiredBones.Num(); i++)
	{
		int32 BoneIndex = InRequiredBones[i];

		// See if this is a physics bone..
		int32 BodyIndex = PhysicsAsset ? PhysicsAsset->FindBodyIndex(SkeletalMesh->RefSkeleton.GetBoneName(BoneIndex)) : INDEX_NONE;
		// need to update back to physX so that physX knows where it was after blending
		bool bUpdatePhysics = false;
		FBodyInstance* BodyInstance = NULL;

		// If so - get its world space matrix and its parents world space matrix and calc relative atom.
		if(BodyIndex != INDEX_NONE )
		{	
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
			// tracking down TTP 280421. Remove this if this doesn't happen. 
			if ( !ensure(Bodies.IsValidIndex(BodyIndex)) )
			{
				UE_LOG(LogPhysics, Warning, TEXT("%s(Mesh %s, PhysicsAsset %s)"), 
					*GetName(), *GetNameSafe(SkeletalMesh), *GetNameSafe(PhysicsAsset));
				if ( PhysicsAsset )
				{
					UE_LOG(LogPhysics, Warning, TEXT(" - # of BodySetup (%d), # of Bodies (%d), Invalid BodyIndex(%d)"), 
						PhysicsAsset->BodySetup.Num(), Bodies.Num(), BodyIndex);
				}
				continue;
			}
#endif
			BodyInstance = Bodies[BodyIndex];

			//if simulated body copy back and blend with animation
			if(BodyInstance->IsInstanceSimulatingPhysics())
			{
				FTransform PhysTM = BodyInstance->GetUnrealWorldTransform_AssumesLocked();

				// Store this world-space transform in cache.
				WorldBoneTMs[BoneIndex].TM = PhysTM;
				WorldBoneTMs[BoneIndex].bUpToDate = true;

				float UsePhysWeight = (bBlendPhysics)? 1.f : BodyInstance->PhysicsBlendWeight;

				// Find this bones parent matrix.
				FTransform ParentWorldTM;

				// if we wan't 'full weight' we just find 
				if(UsePhysWeight > 0.f)
				{
					if(BoneIndex == 0)
					{
						ParentWorldTM = LocalToWorldTM;
					}
					else
					{
						// If not root, get parent TM from cache (making sure its up-to-date).
						int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex);
						UpdateWorldBoneTM(WorldBoneTMs, ParentIndex, this, LocalToWorldTM, TotalScale3D);
						ParentWorldTM = WorldBoneTMs[ParentIndex].TM;
					}


					// Then calc rel TM and convert to atom.
					FTransform RelTM = PhysTM.GetRelativeTransform(ParentWorldTM);
					RelTM.RemoveScaling();
					FQuat RelRot(RelTM.GetRotation());
					FVector RelPos =  RecipScale3D * RelTM.GetLocation();
					FTransform PhysAtom = FTransform(RelRot, RelPos, InLocalAtoms[BoneIndex].GetScale3D());

					// Now blend in this atom. See if we are forcing this bone to always be blended in
					InLocalAtoms[BoneIndex].Blend( InLocalAtoms[BoneIndex], PhysAtom, UsePhysWeight );

					if(BoneIndex == 0)
					{
						//We must update RecipScale3D based on the atom scale of the root
						TotalScale3D *= InLocalAtoms[0].GetScale3D();
						RecipScale3D = TotalScale3D.Reciprocal();
					}

					if (UsePhysWeight < 1.f)
					{
						bUpdatePhysics = true;
					}
				}
			}
		}

		// Update SpaceBases entry for this bone now
		if( BoneIndex == 0 )
		{
			EditableSpaceBases[0] = InLocalAtoms[0];
		}
		else
		{
			const int32 ParentIndex	= SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex);
			EditableSpaceBases[BoneIndex] = InLocalAtoms[BoneIndex] * EditableSpaceBases[ParentIndex];

			/**
			* Normalize rotations.
			* We want to remove any loss of precision due to accumulation of error.
			* i.e. A componentSpace transform is the accumulation of all of its local space parents. The further down the chain, the greater the error.
			* SpaceBases are used by external systems, we feed this to PhysX, send this to gameplay through bone and socket queries, etc.
			* So this is a good place to make sure all transforms are normalized.
			*/
			EditableSpaceBases[BoneIndex].NormalizeRotation();
		}

		if (bUpdatePhysics && BodyInstance)
		{
			//This is extremely inefficient. We need to obtain a write lock which will block other threads from blending
			//For now I'm juts deferring it to the end of this loop, but in general we need to move it all out of here and do it when the blend task is done
			FBodyTMPair* BodyTMPair = new (PendingBodyTMs) FBodyTMPair;
			BodyTMPair->BI = BodyInstance;
			BodyTMPair->TM = EditableSpaceBases[BoneIndex] * ComponentToWorld;
		}
	}

#if WITH_PHYSX
	//See above for read lock instead of write lock
	// Unlock the scenes 
	if (bHasBodiesInSyncScene)
	{
		SCENE_UNLOCK_READ(PhysScene->GetPhysXScene(PST_Sync))
	}

	if (bHasBodiesInAsyncScene)
	{
		SCENE_UNLOCK_READ(PhysScene->GetPhysXScene(PST_Async))
	}

	if(PendingBodyTMs.Num())
	{
		//This is extremely inefficient. We need to obtain a write lock which will block other threads from blending
		//For now I'm juts deferring it to the end of this loop, but in general we need to move it all out of here and do it when the blend task is done

		if (bHasBodiesInSyncScene)
		{
			SCENE_LOCK_WRITE(PhysScene->GetPhysXScene(PST_Sync))
		}

		if (bHasBodiesInAsyncScene)
		{
			SCENE_LOCK_WRITE(PhysScene->GetPhysXScene(PST_Async))
		}

		for (const FBodyTMPair& BodyTMPair : PendingBodyTMs)
		{
			BodyTMPair.BI->SetBodyTransform(BodyTMPair.TM, ETeleportType::TeleportPhysics);
		}

		if (bHasBodiesInSyncScene)
		{
			SCENE_UNLOCK_WRITE(PhysScene->GetPhysXScene(PST_Sync))
		}

		if (bHasBodiesInAsyncScene)
		{
			SCENE_UNLOCK_WRITE(PhysScene->GetPhysXScene(PST_Async))
		}
    }
#endif
	

	// Transforms updated, cached local bounds are now out of date.
	InvalidateCachedBounds();
}
void USkeletalMeshComponent::RecalcRequiredBones(int32 LODIndex)
{
	if (!SkeletalMesh)
	{
		return;
	}

	FSkeletalMeshResource* SkelMeshResource = GetSkeletalMeshResource();
	check(SkelMeshResource);

	// The list of bones we want is taken from the predicted LOD level.
	FStaticLODModel& LODModel = SkelMeshResource->LODModels[LODIndex];
	RequiredBones = LODModel.RequiredBones;
	
	const UPhysicsAsset* const PhysicsAsset = GetPhysicsAsset();
	// If we have a PhysicsAsset, we also need to make sure that all the bones used by it are always updated, as its used
	// by line checks etc. We might also want to kick in the physics, which means having valid bone transforms.
	if(PhysicsAsset)
	{
		TArray<FBoneIndexType> PhysAssetBones;
		for(int32 i=0; i<PhysicsAsset->BodySetup.Num(); i++ )
		{
			int32 PhysBoneIndex = SkeletalMesh->RefSkeleton.FindBoneIndex( PhysicsAsset->BodySetup[i]->BoneName );
			if(PhysBoneIndex != INDEX_NONE)
			{
				PhysAssetBones.Add(PhysBoneIndex);
			}	
		}

		// Then sort array of required bones in hierarchy order
		PhysAssetBones.Sort();

		// Make sure all of these are in RequiredBones.
		MergeInBoneIndexArrays(RequiredBones, PhysAssetBones);
	}

	// Make sure that bones with per-poly collision are also always updated.
	// TODO UE4

	// Purge invisible bones and their children
	// this has to be done before mirror table check/phsysics body checks
	// mirror table/phys body ones has to be calculated
	if (ShouldUpdateBoneVisibility())
	{
		check(BoneVisibilityStates.Num() == SpaceBases.Num());

		int32 VisibleBoneWriteIndex = 0;
		for (int32 i = 0; i < RequiredBones.Num(); ++i)
		{
			FBoneIndexType CurBoneIndex = RequiredBones[i];

			// Current bone visible?
			if (BoneVisibilityStates[CurBoneIndex] == BVS_Visible)
			{
				RequiredBones[VisibleBoneWriteIndex++] = CurBoneIndex;
			}
		}

		// Remove any trailing junk in the RequiredBones array
		const int32 NumBonesHidden = RequiredBones.Num() - VisibleBoneWriteIndex;
		if (NumBonesHidden > 0)
		{
			RequiredBones.RemoveAt(VisibleBoneWriteIndex, NumBonesHidden);
		}
	}

	// Add in any bones that may be required when mirroring.
	// JTODO: This is only required if there are mirroring nodes in the tree, but hard to know...
	if(SkeletalMesh->SkelMirrorTable.Num() > 0 && 
		SkeletalMesh->SkelMirrorTable.Num() == LocalAtoms.Num())
	{
		TArray<FBoneIndexType> MirroredDesiredBones;
		MirroredDesiredBones.AddUninitialized(RequiredBones.Num());

		// Look up each bone in the mirroring table.
		for(int32 i=0; i<RequiredBones.Num(); i++)
		{
			MirroredDesiredBones[i] = SkeletalMesh->SkelMirrorTable[RequiredBones[i]].SourceIndex;
		}

		// Sort to ensure strictly increasing order.
		MirroredDesiredBones.Sort();

		// Make sure all of these are in RequiredBones, and 
		MergeInBoneIndexArrays(RequiredBones, MirroredDesiredBones);
	}

	// Ensure that we have a complete hierarchy down to those bones.
	FAnimationRuntime::EnsureParentsPresent(RequiredBones, SkeletalMesh);

	// make sure animation requiredBone to mark as dirty
	if (AnimScriptInstance)
	{
		AnimScriptInstance->RecalcRequiredBones();
	}

	bRequiredBonesUpToDate = true;

	// Invalidate cached bones.
	CachedLocalAtoms.Empty();
	CachedSpaceBases.Empty();
}