Skip to content

Commit

Permalink
- secured adding virtual bones through source and target bone name ch…
Browse files Browse the repository at this point in the history
…ecks

- it seems to be that unparented virtual bones create issues while adding unweighted bones through the TTToolbox preview v0.4
- removed some todos and improved error messages
  • Loading branch information
AchimTuran committed Jul 18, 2022
1 parent 991a6e5 commit c6aa3ba
Showing 1 changed file with 88 additions and 17 deletions.
105 changes: 88 additions & 17 deletions Source/TTToolbox/Private/TTToolboxBlueprintLibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,27 @@ bool UTTToolboxBlueprintLibrary::AddVirtualBone(
return false;
}

// check if the source and target bones exists already in the skeleton
bool boneMissingInSkeleton = false;
if (Skeleton->GetReferenceSkeleton().FindBoneIndex(SourceBoneName) == INDEX_NONE)
{
UE_LOG(LogTemp, Error, TEXT("Skeleton \"%s\" does not provide the SourceBone \"%s\". Adding the virtual bone \"%s\" is impossible."),
*Skeleton->GetPathName(), *SourceBoneName.ToString(), *VirtualBoneName.ToString());
boneMissingInSkeleton = true;
}

if (Skeleton->GetReferenceSkeleton().FindBoneIndex(TargetBoneName) == INDEX_NONE)
{
UE_LOG(LogTemp, Error, TEXT("Skeleton \"%s\" does not provide the TargetBone \"%s\". Adding the virtual bone \"%s\" is impossible."),
*Skeleton->GetPathName(), *TargetBoneName.ToString(), *VirtualBoneName.ToString());
boneMissingInSkeleton = true;
}

if (boneMissingInSkeleton)
{
return false;
}

// check if virtual bone already exists
for (auto& virtualBone : Skeleton->GetVirtualBones())
{
Expand Down Expand Up @@ -439,6 +460,8 @@ bool UTTToolboxBlueprintLibrary::CheckForMissingCurveNames(const TArray<FName>&

static TArray<USkeletalMesh*> getAllSkeletalMeshes(USkeleton* Skeleton)
{
check(IsValid(Skeleton));

TArray<USkeletalMesh*> skeletalMeshes;

FARFilter filter;
Expand Down Expand Up @@ -551,14 +574,13 @@ bool UTTToolboxBlueprintLibrary::AddUnweightedBone(const TArray<FTTNewBone_BP>&
{
if(!IsValid(Skeleton))
{
//! @todo error message
UE_LOG(LogTemp, Error, TEXT("Invalid input. AddUnweightedBone was called with an invalid skeleton asset. Adding unweighted bones will be aborted."));
return false;
}

if (NewBones.Num() <= 0)
{
//! @todo error message
UE_LOG(LogTemp, Error, TEXT("Invalid input. No new bones were given to AddUnweightedBone."));
UE_LOG(LogTemp, Error, TEXT("Invalid input. No new bones were given to AddUnweightedBone. Adding unweighted bones will be aborted."));
return false;
}

Expand All @@ -569,39 +591,77 @@ bool UTTToolboxBlueprintLibrary::AddUnweightedBone(const TArray<FTTNewBone_BP>&
if (Skeleton->GetReferenceSkeleton().FindBoneIndex(newBone.NewBoneName) != INDEX_NONE)
{
//! @todo error message
UE_LOG(LogTemp, Error, TEXT("NewBoneName already present %s"), *newBone.NewBoneName.ToString());
UE_LOG(LogTemp, Error, TEXT("The unweighted bone \"%s\" already exists in the skeleton."), *newBone.NewBoneName.ToString());
errorsOccured = true;
}

if (Skeleton->GetReferenceSkeleton().FindBoneIndex(newBone.ParentBone) != INDEX_NONE)
{
foundParent = true;
//! @todo error message
UE_LOG(LogTemp, Error, TEXT("ParentBone found %s"), *newBone.ParentBone.ToString());
UE_LOG(LogTemp, Display, TEXT("ParentBone found \"%s\""), *newBone.ParentBone.ToString());
}
else
{
bool boneIsANewBone = false;
for (auto& boneToTest : NewBones)
{
if (newBone.ParentBone == boneToTest.NewBoneName)
{
boneIsANewBone = true;
break;
}
}

if (!boneIsANewBone)
{
UE_LOG(LogTemp, Error, TEXT("ParentBone \"%s\" for child bone \"%s\" not found. Adding the unweighted bones is impossible as no correct parent bone setup exists."), *newBone.ParentBone.ToString(), *newBone.NewBoneName.ToString());
errorsOccured = true;
}
}
}

if (!foundParent)
{
//! @todo error message
UE_LOG(LogTemp, Error, TEXT("Invalid input. At least one parent bone needs to be configured but none where found"));
UE_LOG(LogTemp, Error, TEXT("Invalid input. No parent bone found for the new unweighted bones. Please check you configuration. Adding unweighted bones will be aborted."));
return false;
}

if (errorsOccured)
{
UE_LOG(LogTemp, Error, TEXT("Invalid input. At least one error occured, for details see the error message(s) above. Adding unweighted bones will be aborted."));
return false;
}

TArray<USkeletalMesh*> skeletalMeshes = getAllSkeletalMeshes(Skeleton);
if (skeletalMeshes.IsEmpty())
{
//! @todo error message
UE_LOG(LogTemp, Error, TEXT("no skeletal meshes found"));
UE_LOG(LogTemp, Error, TEXT("Aborting adding unweighted bones as no skeletal meshes found that are connected to \"%s\""), *Skeleton->GetPathName());
return false;
}

// Sadly, the implementation does have some issues with wrong bone indices,
// see https://github.com/tuatec/TTToolbox/issues/5#issuecomment-1184052765 for the details.
// That's why all virtual bones get removed (same state if a skeletal mesh is imported through an fbx file)
// and later added again. This step needs to be done anyways as the bone tree needs to be regenerated,
// sadly again there is no public API that can trigger this. BUT! It is possible to trigger the regeneration process
// through adding a virtual bone.
// Long story short, adding virtual bones makes it possible to introduce unweighted bones in a save way. ;-)
const auto savedVirtualBones = Skeleton->GetVirtualBones();
{
TArray<FName> virtualBoneNamesToDelete;
for (auto& virtualBone : savedVirtualBones) {
virtualBoneNamesToDelete.Add(virtualBone.VirtualBoneName);
}
if (virtualBoneNamesToDelete.Num() > 0) {
Skeleton->RemoveVirtualBones(virtualBoneNamesToDelete);
}
}

uint32 modifiedSkeletalMeshes = 0;
//! @todo @ffs release renderer ressources
//! It seems to be that the renderer does not need to be flushed.
//! Still wondering a lot why it creates assertions. Mabye a future version of Unreal needs it
//for (auto skeletalMesh : skeletalMeshes)
//{
// skeletalMesh->FlushRenderState();
Expand All @@ -611,9 +671,9 @@ bool UTTToolboxBlueprintLibrary::AddUnweightedBone(const TArray<FTTNewBone_BP>&
{
UE_LOG(LogTemp, Warning, TEXT("skeletal mesh found %s"), *skeletalMesh->GetFullName());

//! @todo validate if this check is still relevant
if (Skeleton == skeletalMesh->GetSkeleton())
{
//! @todo @ffs release renderer ressources
//skeletalMesh->FlushRenderState();
//skeletalMesh->ReleaseResources();
//skeletalMesh->ReleaseResourcesFence.Wait();
Expand Down Expand Up @@ -682,8 +742,9 @@ bool UTTToolboxBlueprintLibrary::AddUnweightedBone(const TArray<FTTNewBone_BP>&
}
}

//! @todo @ffs release renderer ressources
//skeletalMesh->PostEditChange();
////skeletalMesh->InitResources();
//skeletalMesh->InitResources();

// the mesh got new bones and now it is necessary to merge those bones into the USkeleton asset as well
if (!(Skeleton->MergeAllBonesToBoneTree(skeletalMesh)))
Expand Down Expand Up @@ -727,8 +788,7 @@ bool UTTToolboxBlueprintLibrary::AddUnweightedBone(const TArray<FTTNewBone_BP>&
//
// But happily adding and removing virtual bones call internall USkeleton::HandleVirtualBoneChanges,
// which should rebuild the mapping table ;-)
FName virtualBoneName = "Name";
// NewBones[0].ParentBone + "_delete_me"))
FName virtualBoneName = *(NewBones[0].ParentBone.ToString() + "_delete_me");
if (!Skeleton->AddNewVirtualBone(NewBones[0].ParentBone, NewBones[0].ParentBone, virtualBoneName))
{
UE_LOG(LogTemp, Error, TEXT("failed to add dirty virtual bone hack to force the rebuild of the bone mapping table of skeleton <todo-name>"));
Expand All @@ -742,6 +802,18 @@ bool UTTToolboxBlueprintLibrary::AddUnweightedBone(const TArray<FTTNewBone_BP>&
}
}

// finally readd the virtual bones again to savely store everything
if (savedVirtualBones.Num() > 0)
{
for (auto& virtualBone : savedVirtualBones)
{
if (!UTTToolboxBlueprintLibrary::AddVirtualBone(virtualBone.VirtualBoneName, virtualBone.SourceBoneName, virtualBone.TargetBoneName, Skeleton))
{
UE_LOG(LogTemp, Error, TEXT("Internal error! Failed to add virtual bone \"%s\" again please raise a issue here: https://github.com/tuatec/TTToolbox/issues."), *virtualBone.VirtualBoneName.ToString());
}
}
}

if (modifiedSkeletalMeshes > 0)
{
Skeleton->Modify();
Expand Down Expand Up @@ -772,7 +844,7 @@ void UTTToolboxBlueprintLibrary::RequestAnimationRecompress(USkeleton* Skeleton)

bool UTTToolboxBlueprintLibrary::ConstraintBonesForSkeletonPose(const TArray<FTTConstraintBone_BP>& ConstraintBones, USkeleton* Skeleton)
{
//! @todo implement
//! @todo @ffs implement
return false;
}

Expand Down Expand Up @@ -960,11 +1032,10 @@ bool UTTToolboxBlueprintLibrary::AddRootBone(USkeleton* Skeleton)
//
// But happily adding and removing virtual bones call internall USkeleton::HandleVirtualBoneChanges,
// which should rebuild the mapping table ;-)
FName virtualBoneName = "Name";
// NewBones[0].ParentBone + "_delete_me"))
FName virtualBoneName = *(gs_rootBoneName.ToString() + "_delete_me");
if (!Skeleton->AddNewVirtualBone(gs_rootBoneName, gs_rootBoneName, virtualBoneName))
{
UE_LOG(LogTemp, Error, TEXT("failed to add dirty virtual bone hack to force the rebuild of the bone mapping table of skeleton <todo-name>"));
UE_LOG(LogTemp, Error, TEXT("failed to add dirty virtual bone hack to force the rebuild of the bone mapping table of skeleton \"%s\""), *Skeleton->GetPathName());
}
Skeleton->RemoveVirtualBones({ virtualBoneName });

Expand Down

0 comments on commit c6aa3ba

Please sign in to comment.