// VRM4U Copyright (c) 2021-2024 Haruyoshi Yamamoto. This software is released under the MIT License. #include "LoaderBPFunctionLibrary.h" //#include "VRM4U.h" #include "VrmSkeletalMesh.h" #include "VrmModelActor.h" #include "VrmAssetListObject.h" #include "VrmMetaObject.h" #include "VrmLicenseObject.h" #include "Vrm1LicenseObject.h" #include "VrmAsyncLoadAction.h" #include "VrmConvert.h" #include "VrmUtil.h" #include "VRM4ULoaderLog.h" #include "Components/SkeletalMeshComponent.h" #include "Rendering/SkeletalMeshLODRenderData.h" #include "Rendering/SkeletalMeshRenderData.h" #include "Animation/Skeleton.h" #include "Animation/MorphTarget.h" #include "Animation/NodeMappingContainer.h" #include "Animation/PoseAsset.h" //#include ".h" #include "RenderingThread.h" #include "Rendering/SkeletalMeshModel.h" #include "Rendering/SkeletalMeshLODModel.h" #include "PhysicsEngine/PhysicsAsset.h" #include "PhysicsEngine/PhysicsConstraintTemplate.h" #include "Misc/FeedbackContext.h" #include "Misc/FileHelper.h" #include "UObject/Package.h" #include "Engine/SkeletalMeshSocket.h" #include "EditorFramework/AssetImportData.h" #include "TextureResource.h" #include "Engine/Texture2D.h" #include "IImageWrapper.h" #include "IImageWrapperModule.h" #include "Engine/Blueprint.h" #if UE_VERSION_OLDER_THAN(4,26,0) #include "AssetRegistryModule.h" #else #include "AssetRegistry/AssetRegistryModule.h" #endif #include "UObject/Package.h" #include "Engine/Engine.h" //#include "Windows/WindowsSystemIncludes.h" #if UE_VERSION_OLDER_THAN(5,0,0) #else #include "EditorFramework/AssetImportData.h" #include "UObject/SavePackage.h" #if WITH_EDITOR #include "ContentBrowserModule.h" #include "IContentBrowserSingleton.h" #endif #endif #include "VrmRigHeader.h" #if UE_VERSION_OLDER_THAN(5,4,0) #else #include "Misc/FieldAccessor.h" #endif #if PLATFORM_WINDOWS #include "Windows/AllowWindowsPlatformTypes.h" #include #include "Windows/HideWindowsPlatformTypes.h" #endif #include #include #include #include #include #include #if UE_VERSION_OLDER_THAN(4,23,0) #define TRACE_CPUPROFILER_EVENT_SCOPE(a) #else #endif // tem namespace { UPackage *s_vrm_package = nullptr; FString baseFileName; } namespace { class RenderControl { bool tmp = false; public: RenderControl() { #if UE_VERSION_OLDER_THAN(5,0,0) tmp = GUseThreadedRendering; if (tmp) { StopRenderingThread(); GUseThreadedRendering = false; } #else #endif } ~RenderControl() { #if UE_VERSION_OLDER_THAN(5,0,0) if (tmp) { GUseThreadedRendering = true; StartRenderingThread(); } #else #endif } }; } static std::string GetExtAndSetModelTypeLocal(std::string e, const uint8* pDataLocal, size_t sizeLocal) { std::string e_tmp = e; VRMConverter::Options::Get().ClearModelType(); if (e.compare("vrm") == 0 || e.compare("glb") == 0 || e.compare("gltf") == 0) { VRMConverter::Options::Get().SetVRM0Model(true); extern bool VRMIsVRM10(const uint8 * pData, size_t size); if (VRMIsVRM10(pDataLocal, sizeLocal)) { VRMConverter::Options::Get().SetVRM10Model(true); } } if (e.compare("vrma") == 0) { VRMConverter::Options::Get().SetVRMAModel(true); VRMConverter::Options::Get().SetNoMesh(true); VRMConverter::Options::Get().SetVRM10Model(true); e_tmp = "vrm"; } if (e.compare("bvh") == 0) { VRMConverter::Options::Get().SetBVHModel(true); } if (e.compare("pmx") == 0) { VRMConverter::Options::Get().SetPMXModel(true); } return e_tmp; } static bool RemoveObject(UObject* u) { if (u == nullptr) return true; #if WITH_EDITOR u->ClearFlags(EObjectFlags::RF_Standalone); u->SetFlags(EObjectFlags::RF_Public | RF_Transient); u->ConditionalBeginDestroy(); #endif return true; } static bool RemoveAssetList(UVrmAssetListObject *&assetList) { if (assetList == nullptr) return false; #if WITH_EDITOR for (auto& t : assetList->Textures) { RemoveObject(t); } for (auto& t : assetList->Materials) { RemoveObject(t); } for (auto& t : assetList->OutlineMaterials) { RemoveObject(t); } if (assetList->SkeletalMesh) { RemoveObject(VRMGetSkeleton(assetList->SkeletalMesh)); RemoveObject(VRMGetPhysicsAsset(assetList->SkeletalMesh)); RemoveObject(assetList->SkeletalMesh); } RemoveObject(assetList->VrmMetaObject); RemoveObject(assetList->VrmLicenseObject); RemoveObject(assetList->Vrm1LicenseObject); RemoveObject(assetList->HumanoidSkeletalMesh); RemoveObject(assetList->HumanoidRig); RemoveObject(assetList); assetList = nullptr; #endif return true; } static bool RenewPkgAndSaveObject(UObject *u, bool bSave) { #if WITH_EDITOR if (u == nullptr) return false; //if (VRMConverter::Options::Get().IsSingleUAssetFile()) { if (VRMConverter::IsImportMode()) { u->PostEditChange(); } if (bSave) { s_vrm_package->MarkPackageDirty(); FAssetRegistryModule::AssetCreated(u); #if UE_VERSION_OLDER_THAN(5,0,0) bool bSaved = UPackage::SavePackage(s_vrm_package, u, EObjectFlags::RF_Standalone, *(s_vrm_package->GetName()), GError, nullptr, true, true, SAVE_NoError); #elif UE_VERSION_OLDER_THAN(5,1,0) FSavePackageArgs SaveArgs = { nullptr, EObjectFlags::RF_Standalone, SAVE_NoError, true, true, true, FDateTime::MinValue(), GError }; bool bSaved = UPackage::SavePackage(s_vrm_package, u, *(s_vrm_package->GetName()), SaveArgs); #else FSavePackageArgs SaveArgs; SaveArgs.TopLevelFlags = EObjectFlags::RF_Standalone; SaveArgs.SaveFlags = SAVE_NoError; SaveArgs.bForceByteSwapping = true; SaveArgs.bWarnOfLongFilename = true; SaveArgs.bSlowTask = true; SaveArgs.FinalTimeStamp = FDateTime::MinValue(); SaveArgs.Error = GError; bool bSaved = UPackage::SavePackage(s_vrm_package, u, *(s_vrm_package->GetName()), SaveArgs); #endif } /* }else{ FString objPath = u->GetPathName(); if (objPath.IsEmpty()) return false; const FString PackageName = FPackageName::ObjectPathToPackageName(objPath); const FString PackagePath = FPaths::GetPath(PackageName); const FString AssetName = FPaths::GetBaseFilename(PackageName); FString NewPackageName = FPaths::Combine(*PackagePath, *(u->GetFName().ToString())); UPackage* Pkg = CreatePackage(nullptr, *NewPackageName); u->Rename(nullptr, Pkg); if (VRMConverter::IsImportMode()) { u->PostEditChange(); } if (bSave) { UPackage::SavePackage(Pkg, u, RF_Standalone, *FPackageName::LongPackageNameToFilename(NewPackageName, FPackageName::GetAssetPackageExtension()), GError, nullptr, false, true, SAVE_NoError); } } */ #endif return true; } static UTexture2D* LocalGetTexture(const aiScene* mScenePtr, int texIndex) { if (texIndex < 0 || texIndex >= (int)mScenePtr->mNumTextures) { return nullptr; } IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); TSharedPtr ImageWrapper; // Note: PNG format. Other formats are supported ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG); auto& t = *mScenePtr->mTextures[texIndex]; int Width = t.mWidth; int Height = t.mHeight; #if UE_VERSION_OLDER_THAN(4,25,0) #else TArray RawData; #endif const TArray* pRawData = nullptr; if (Height == 0) { if (ImageWrapper->SetCompressed(t.pcData, t.mWidth)) { } Width = ImageWrapper->GetWidth(); Height = ImageWrapper->GetHeight(); if (Width == 0 || Height == 0) { return nullptr; } #if UE_VERSION_OLDER_THAN(4,25,0) ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, pRawData); #else ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, RawData); pRawData = &RawData; #endif } FString baseName; auto *NewTexture2D = VRMLoaderUtil::CreateTexture(Width, Height, FString(TEXT("T_")) + baseName, GetTransientPackage()); //UTexture2D* NewTexture2D = _CreateTransient(Width, Height, PF_B8G8R8A8, t.mFilename.C_Str()); // Fill in the base mip for the texture we created uint8* MipData = (uint8*)GetPlatformData(NewTexture2D)->Mips[0].BulkData.Lock(LOCK_READ_WRITE); if (pRawData) { FMemory::Memcpy(MipData, pRawData->GetData(), pRawData->Num()); } else { for (int32 y = 0; y < Height; y++) { const aiTexel* c = &(t.pcData[y * Width]); uint8* DestPtr = &MipData[y * Width * sizeof(FColor)]; for (int32 x = 0; x < Width; x++) { *DestPtr++ = c->b; *DestPtr++ = c->g; *DestPtr++ = c->r; *DestPtr++ = c->a; c++; } } } GetPlatformData(NewTexture2D)->Mips[0].BulkData.Unlock(); // Set options NewTexture2D->SRGB = true;// bUseSRGB; NewTexture2D->CompressionSettings = TC_Default; NewTexture2D->AddressX = TA_Wrap; NewTexture2D->AddressY = TA_Wrap; #if WITH_EDITORONLY_DATA NewTexture2D->CompressionNone = false; NewTexture2D->DeferCompression = true; // nomipmap for tmporary thumbnail //if (VRMConverter::Options::Get().IsMipmapGenerateMode()) { // NewTexture2D->MipGenSettings = TMGS_FromTextureGroup; //} else { NewTexture2D->MipGenSettings = TMGS_NoMipmaps; //} NewTexture2D->Source.Init(Width, Height, 1, 1, ETextureSourceFormat::TSF_BGRA8, pRawData->GetData()); //NewTexture2D->Source.Compress(); #endif // Update the remote texture data NewTexture2D->UpdateResource(); #if WITH_EDITOR NewTexture2D->PostEditChange(); #endif return NewTexture2D; } static void UpdateProgress(int prog) { #if WITH_EDITOR GWarn->UpdateProgress( prog, 100 ); #endif } //// static void ReTransformHumanoidBone(USkeleton *targetHumanoidSkeleton, const UVrmMetaObject *meta, const USkeleton *displaySkeleton) { FReferenceSkeleton &ReferenceSkeleton = const_cast(targetHumanoidSkeleton->GetReferenceSkeleton()); auto &allbone = const_cast &>(targetHumanoidSkeleton->GetReferenceSkeleton().GetRawRefBoneInfo()); //auto &humanoidTrans = humanoidSkeleton->GetReferenceSkeleton().GetRawRefBonePose(); FReferenceSkeletonModifier RefSkelModifier(ReferenceSkeleton, targetHumanoidSkeleton); for (int ind_target = 0; ind_target < targetHumanoidSkeleton->GetReferenceSkeleton().GetRawBoneNum(); ++ind_target) { FTransform t; t.SetIdentity(); RefSkelModifier.UpdateRefPoseTransform(ind_target, t); auto boneName = ReferenceSkeleton.GetBoneName(ind_target); //auto &info = targetHumanoidSkeleton->GetReferenceSkeleton().GetRefBoneInfo(); //auto &a = info[ind_target]; int32 ind_disp = 0; if (meta) { auto p = meta->humanoidBoneTable.Find(boneName.ToString()); if (p == nullptr) { continue; } ind_disp = displaySkeleton->GetReferenceSkeleton().FindBoneIndex(**p); }else { ind_disp = displaySkeleton->GetReferenceSkeleton().FindBoneIndex(boneName); } if (ind_disp == INDEX_NONE) { continue; } t = displaySkeleton->GetReferenceSkeleton().GetRefBonePose()[ind_disp]; auto parent = displaySkeleton->GetReferenceSkeleton().GetParentIndex(ind_disp); while (parent != INDEX_NONE) { auto s = displaySkeleton->GetReferenceSkeleton().GetBoneName(parent); if (meta) { if (meta->humanoidBoneTable.FindKey(s.ToString()) != nullptr) { // parent == humanoidBone break; } } //t.SetLocation(t.GetLocation() + displaySkeleton->GetReferenceSkeleton().GetRefBonePose()[parent].GetLocation());; parent = displaySkeleton->GetReferenceSkeleton().GetParentIndex(parent); } RefSkelModifier.UpdateRefPoseTransform(ind_target, t); } ReferenceSkeleton.RebuildRefSkeleton(targetHumanoidSkeleton, true); } bool ULoaderBPFunctionLibrary::VRMReTransformHumanoidBone(USkeletalMeshComponent *targetHumanoidSkeleton, const UVrmMetaObject *meta, const USkeletalMeshComponent *displaySkeleton) { if (targetHumanoidSkeleton == nullptr) return false; if (VRMGetSkinnedAsset(targetHumanoidSkeleton) == nullptr) return false; if (displaySkeleton == nullptr) return false; if (VRMGetSkinnedAsset(displaySkeleton) == nullptr) return false; // no meta. use default name. //if (meta == nullptr) return false; ReTransformHumanoidBone(VRMGetSkeleton( VRMGetSkinnedAsset(targetHumanoidSkeleton) ), meta, VRMGetSkeleton( VRMGetSkinnedAsset(displaySkeleton) )); auto *sk = VRMGetSkinnedAsset(targetHumanoidSkeleton); auto *k = VRMGetSkeleton(sk); VRMSetRefSkeleton(sk, k->GetReferenceSkeleton()); //sk->RefSkeleton.RebuildNameToIndexMap(); //sk->RefSkeleton.RebuildRefSkeleton(sk->Skeleton, true); //sk->Proc(); //out->RefSkeleton = sk->RefSkeleton; VRMSetSkeleton(sk, k); VRMSetRefSkeleton(sk, k->GetReferenceSkeleton()); sk->CalculateInvRefMatrices(); sk->CalculateExtendedBounds(); #if WITH_EDITORONLY_DATA sk->ConvertLegacyLODScreenSize(); #if UE_VERSION_OLDER_THAN(4,20,0) #else sk->UpdateGenerateUpToData(); #endif #endif #if WITH_EDITORONLY_DATA k->SetPreviewMesh(sk); #endif k->RecreateBoneTree(sk); return true; } void ULoaderBPFunctionLibrary::SetImportMode(bool bIm, class UPackage *p) { VRMConverter::SetImportMode(bIm); s_vrm_package = p; } namespace { #if PLATFORM_WINDOWS std::string utf_16_to_shift_jis(const std::wstring& str) { static_assert(sizeof(wchar_t) == 2, "this function is windows only"); const int len = ::WideCharToMultiByte(932/*CP_ACP*/, 0, str.c_str(), -1, nullptr, 0, nullptr, nullptr); std::string re(len * 2, '\0'); if (!::WideCharToMultiByte(CP_ACP, 0, str.c_str(), -1, &re[0], len, nullptr, nullptr)) { const auto ec = ::GetLastError(); switch (ec) { case ERROR_INSUFFICIENT_BUFFER: //throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: ERROR_INSUFFICIENT_BUFFER"); break; case ERROR_INVALID_FLAGS: //throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: ERROR_INVALID_FLAGS"); break; case ERROR_INVALID_PARAMETER: //throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: ERROR_INVALID_PARAMETER"); break; default: //throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: unknown(" + std::to_string(ec) + ')'); break; break; } } const std::size_t real_len = std::strlen(re.c_str()); re.resize(real_len); re.shrink_to_fit(); return re; } #endif } bool ULoaderBPFunctionLibrary::IsValidVRM4UFile(FString filepath) { UE_LOG(LogVRM4ULoader, Log, TEXT("IsValidVRM: OrigFileName=%s"), *filepath); const FString ext = FPaths::GetExtension(filepath); if (ext.Compare(TEXT("vrm"), ESearchCase::IgnoreCase) && ext.Compare(TEXT("vrma"), ESearchCase::IgnoreCase)) { // vrm, vrma以外は素通し return true; } std::string file; #if PLATFORM_WINDOWS file = utf_16_to_shift_jis(*filepath); #else file = TCHAR_TO_UTF8(*filepath); #endif UE_LOG(LogVRM4ULoader, Log, TEXT("IsValidVRM: std::stringFileName=%hs"), file.c_str()); TArray Res; if (FFileHelper::LoadFileToArray(Res, *filepath)) { UE_LOG(LogVRM4ULoader, Log, TEXT("IsValidVRM: filesize=%d"), Res.Num()); extern bool VRMIsValid(const uint8_t * pData, size_t size); return VRMIsValid(Res.GetData(), Res.Num()); } return false; } void ULoaderBPFunctionLibrary::GetVRMMeta(FString filepath, UVrmLicenseObject*& a, UVrm1LicenseObject*& b) { UE_LOG(LogVRM4ULoader, Log, TEXT("GetVRMMeta:OrigFileName=%s"), *filepath); std::string file; #if PLATFORM_WINDOWS file = utf_16_to_shift_jis(*filepath); #else file = TCHAR_TO_UTF8(*filepath); #endif UE_LOG(LogVRM4ULoader, Log, TEXT("GetVRMMeta:std::stringFileName=%hs"), file.c_str()); Assimp::Importer mImporter; mImporter.SetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, false); const aiScene *mScenePtr = nullptr; // delete by Assimp::Importer::~Importer VRMConverter vc; { TArray Res; if (FFileHelper::LoadFileToArray(Res, *filepath)) { } UE_LOG(LogVRM4ULoader, Log, TEXT("GetVRMMeta: filesize=%d"), Res.Num()); const FString ext = FPaths::GetExtension(filepath); #if PLATFORM_WINDOWS std::string e = utf_16_to_shift_jis(*ext); #else std::string e = TCHAR_TO_UTF8(*ext); #endif e = GetExtAndSetModelTypeLocal(e, Res.GetData(), Res.Num()); mScenePtr = mImporter.ReadFileFromMemory(Res.GetData(), Res.Num(), aiProcess_Triangulate | aiProcess_MakeLeftHanded | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals | aiProcess_OptimizeMeshes, e.c_str()); UE_LOG(LogVRM4ULoader, Log, TEXT("GetVRMMeta: mScenePtr=%p"), mScenePtr); if (mScenePtr == nullptr) { return; } //UE_LOG(LogVRM4ULoader, Log, TEXT("VRM:(%3.3lf secs) ReadFileFromMemory"), FPlatformTime::Seconds() - StartTime); { // vrm version check extern bool VRMIsVRM10(const uint8 * pData, size_t size); if (VRMIsVRM10(Res.GetData(), Res.Num())) { VRMConverter::Options::Get().SetVRM10Model(true); vc.Init(Res.GetData(), Res.Num(), nullptr); } } } UTexture2D* NewTexture2D = nullptr; if (VRMConverter::Options::Get().IsVRM10Model()) { int texIndex = vc.GetThumbnailTextureIndex(); NewTexture2D = LocalGetTexture(mScenePtr, texIndex); }else{ VRM::VRMMetadata* meta = reinterpret_cast(mScenePtr->mVRMMeta); if (meta) { for (int i = 0; i < meta->license.licensePairNum; ++i) { auto& p = meta->license.licensePair[i]; if (FString(TEXT("texture")) != p.Key.C_Str()) { continue; } unsigned int texIndex = FCString::Atoi(*FString(p.Value.C_Str())); NewTexture2D = LocalGetTexture(mScenePtr, texIndex); if (NewTexture2D) { break; } } } } UVrmLicenseObject* m = nullptr; UVrm1LicenseObject* m1 = nullptr; vc.GetVRMMeta(mScenePtr, m, m1); if (m) m->thumbnail = NewTexture2D; if (m1) m1->thumbnail = NewTexture2D; a = m; b = m1; } bool ULoaderBPFunctionLibrary::VRMSetLoadMaterialType(EVRMImportMaterialType type) { VRMConverter::Options::Get().SetMaterialType(type); return true; } bool ULoaderBPFunctionLibrary::LoadVRMFile(const UVrmAssetListObject *InVrmAsset, UVrmAssetListObject *&OutVrmAsset, const FString filepath, const FImportOptionData &OptionForRuntimeLoad) { VRMConverter::Options::Get().SetVrmOption(&OptionForRuntimeLoad); OutVrmAsset = nullptr; return LoadVRMFileLocal(InVrmAsset, OutVrmAsset, filepath); } void ULoaderBPFunctionLibrary::LoadVRMFileAsync(const UObject* WorldContextObject, const class UVrmAssetListObject* InVrmAsset, class UVrmAssetListObject*& OutVrmAsset, const FString filepath, const FImportOptionData& OptionForRuntimeLoad, struct FLatentActionInfo LatentInfo) { VRMConverter::Options::Get().SetVrmOption(&OptionForRuntimeLoad); OutVrmAsset = nullptr; if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)) { FLatentActionManager& LatentActionManager = World->GetLatentActionManager(); if (LatentActionManager.FindExistingAction(LatentInfo.CallbackTarget, LatentInfo.UUID) == NULL) { FVrmAsyncLoadActionParam p = { InVrmAsset, OutVrmAsset, OptionForRuntimeLoad, filepath, nullptr, 0 }; LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FVrmAsyncLoadAction(LatentInfo, p)); } } return; } bool ULoaderBPFunctionLibrary::LoadVRMFileLocal(const UVrmAssetListObject* InVrmAsset, UVrmAssetListObject*& OutVrmAsset, const FString filepath) { TArray Res; if (FFileHelper::LoadFileToArray(Res, *filepath)) { } return LoadVRMFileFromMemory(InVrmAsset, OutVrmAsset, filepath, Res.GetData(), Res.Num()); } bool ULoaderBPFunctionLibrary::LoadVRMFileFromMemoryDefaultOption(UVrmAssetListObject*& OutVrmAsset, const FString filepath, const uint8* pData, size_t dataSize) { #if UE_VERSION_OLDER_THAN(5,0,0) TAssetPtr c; #else TSoftObjectPtr c; #endif if (c == nullptr) { FSoftObjectPath r(TEXT("/VRM4U/VrmAssetListObjectBP.VrmAssetListObjectBP")); UObject* u = r.TryLoad(); if (u) { c = (UClass*)(Cast(u)->GeneratedClass); } } if (c == nullptr) { c = UVrmAssetListObject::StaticClass(); } #if UE_VERSION_OLDER_THAN(5,0,0) TAssetPtr m = NewObject((UObject*)GetTransientPackage(), c.Get()); #else TSoftObjectPtr m = NewObject((UObject*)GetTransientPackage(), c.Get()); #endif return LoadVRMFileFromMemory(m.Get(), OutVrmAsset, filepath, pData, dataSize); } bool ULoaderBPFunctionLibrary::LoadVRMFileFromMemory(const UVrmAssetListObject *InVrmAsset, UVrmAssetListObject *&OutVrmAsset, const FString filepath, const uint8 *pFileDataData, size_t dataSize) { TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("LoadVRMFileFromMemory")) OutVrmAsset = nullptr; RenderControl _dummy_control; if (InVrmAsset == nullptr) { return false; } Assimp::Importer mImporter; mImporter.SetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, false); const aiScene* mScenePtr = nullptr; // delete by Assimp::Importer::~Importer if (filepath.IsEmpty()) { } double StartTime = FPlatformTime::Seconds(); auto LogAndUpdate = [&](FString logname) { UE_LOG(LogVRM4ULoader, Log, TEXT("VRM:(%02.2lf secs) %s"), FPlatformTime::Seconds() - StartTime, *logname); StartTime = FPlatformTime::Seconds(); }; VRMConverter::Options::Get().SetVRM0Model(true); { TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("AssImpLoader")) const FString ext = FPaths::GetExtension(filepath).ToLower(); #if PLATFORM_WINDOWS std::string e = utf_16_to_shift_jis(*ext); std::string e_imp = utf_16_to_shift_jis(*ext); #else std::string e = TCHAR_TO_UTF8(*ext); std::string e_imp = TCHAR_TO_UTF8(*ext); #endif e_imp = GetExtAndSetModelTypeLocal(e, pFileDataData, dataSize); mScenePtr = mImporter.ReadFileFromMemory(pFileDataData, dataSize, aiProcess_Triangulate | aiProcess_MakeLeftHanded | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals | aiProcess_OptimizeMeshes | aiProcess_PopulateArmatureData, e_imp.c_str()); if (mScenePtr == nullptr) { std::string file; #if PLATFORM_WINDOWS file = utf_16_to_shift_jis(*filepath); #else file = TCHAR_TO_UTF8(*filepath); #endif mScenePtr = mImporter.ReadFile(file, aiProcess_Triangulate | aiProcess_MakeLeftHanded | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals | aiProcess_OptimizeMeshes | aiProcess_PopulateArmatureData); } UE_LOG(LogVRM4ULoader, Log, TEXT("VRM:(%3.3lf secs) ReadFileFromMemory"), FPlatformTime::Seconds() - StartTime); StartTime = FPlatformTime::Seconds(); } UpdateProgress(20); if (mScenePtr == nullptr) { UE_LOG(LogVRM4ULoader, Warning, TEXT("VRM4U: read failure.\n")); return false; } { FString fullpath = FPaths::GameUserDeveloperDir() + TEXT("VRM/"); FString basepath = FPackageName::FilenameToLongPackageName(fullpath); //FPackageName::RegisterMountPoint("/VRMImportData/", fullpath); baseFileName = FPaths::GetBaseFilename(filepath); if (s_vrm_package == nullptr) { s_vrm_package = GetTransientPackage(); } } UVrmAssetListObject *out = OutVrmAsset; if (OutVrmAsset == nullptr) { if (s_vrm_package == GetTransientPackage()) { out = Cast(StaticDuplicateObject(InVrmAsset, s_vrm_package, NAME_None)); } else { if (InVrmAsset->ReimportBase) { out = InVrmAsset->ReimportBase; } else { out = VRM4U_NewObject(s_vrm_package, *(FString(TEXT("VA_")) + VRMConverter::NormalizeFileName(baseFileName) + FString(TEXT("_VrmAssetList"))), EObjectFlags::RF_Public | EObjectFlags::RF_Standalone); } InVrmAsset->CopyMember(out); } OutVrmAsset = out; } if (out == nullptr) { UE_LOG(LogVRM4ULoader, Warning, TEXT("VRM4U: no UVrmAssetListObject.\n")); return false; } #if WITH_EDITORONLY_DATA { out->AssetImportData = NewObject(out, TEXT("AssetImportData")); out->AssetImportData->Update(filepath); } #endif out->FileFullPathName = filepath; out->OrigFileName = baseFileName; out->BaseFileName = VRMConverter::NormalizeFileName(baseFileName); out->Package = s_vrm_package; { bool ret = true; VRMConverter vc; vc.Init(pFileDataData, dataSize, mScenePtr); vc.ConvertVrmFirst(out, pFileDataData, dataSize); LogAndUpdate(TEXT("Begin convert")); ret &= vc.NormalizeBoneName(mScenePtr); LogAndUpdate(TEXT("NormalizeBoneName")); { TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("VRM Texture and Material")) ret &= vc.ConvertTextureAndMaterial(out); LogAndUpdate(TEXT("ConvertTextureAndMaterial")); } UpdateProgress(40); { bool r = vc.ConvertVrmMeta(out, mScenePtr, pFileDataData, dataSize); // use texture. if (VRMConverter::Options::Get().IsVRMModel() == true) { ret &= r; } } LogAndUpdate(TEXT("ConvertVrmMeta")); UpdateProgress(60); { TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("VRM Skeleton")) ret &= vc.ConvertModel(out); LogAndUpdate(TEXT("ConvertModel")); } //meta rename vc.ConvertVrmMetaPost(out, mScenePtr, pFileDataData, dataSize); ret &= vc.ConvertRig(out); LogAndUpdate(TEXT("ConvertRig")); ret &= vc.ConvertIKRig(out); LogAndUpdate(TEXT("ConvertIKRig")); if (out->bSkipMorphTarget == false) { ret &= vc.ConvertMorphTarget(out); LogAndUpdate(TEXT("ConvertMorphTarget")); } ret &= vc.ConvertPose(out); LogAndUpdate(TEXT("ConvertPose")); ret &= vc.ConvertHumanoid(out); LogAndUpdate(TEXT("ConvertHumanoid")); UpdateProgress(80); OutVrmAsset->MeshReturnedData = nullptr; if (ret == false) { RemoveAssetList(out); return false; } { // 後処理。UE5.7ではCreateMeshDescription を呼ぶため必要 if (out->SkeletalMesh) { out->SkeletalMesh->PostLoad(); } } } out->VrmMetaObject->SkeletalMesh = out->SkeletalMesh; VRMSetPhysicsAsset(out->VrmMetaObject->SkeletalMesh, nullptr); { TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("VRM Save")) LogAndUpdate(TEXT("BeginSave")); bool b = out->bAssetSave; RenewPkgAndSaveObject(out, b); for (auto &t : out->Textures) { RenewPkgAndSaveObject(t, b); } for (auto &t : out->Materials) { RenewPkgAndSaveObject(t, b); } for (auto &t : out->OutlineMaterials) { RenewPkgAndSaveObject(t, b); } RenewPkgAndSaveObject(out->SkeletalMesh, b); RenewPkgAndSaveObject(VRMGetSkeleton(out->SkeletalMesh), b); RenewPkgAndSaveObject(VRMGetPhysicsAsset(out->SkeletalMesh), b); RenewPkgAndSaveObject(out->VrmMetaObject, b); RenewPkgAndSaveObject(out->VrmHumanoidMetaObject, b); RenewPkgAndSaveObject(out->VrmMannequinMetaObject, b); RenewPkgAndSaveObject(out->VrmLicenseObject, b); RenewPkgAndSaveObject(out->Vrm1LicenseObject, b); RenewPkgAndSaveObject(out->HumanoidSkeletalMesh, b); RenewPkgAndSaveObject(out->HumanoidRig, b); LogAndUpdate(TEXT("Save")); } if (VRMConverter::IsImportMode()){ #if WITH_EDITOR #if UE_VERSION_OLDER_THAN(5,0,0) #else // refresh content browser FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); { TArray a = { OutVrmAsset }; //ContentBrowserModule.Get().SyncBrowserToAssets(a); } { auto path = FPaths::GetPath(OutVrmAsset->GetPathName()); const TArray b = { path }; ContentBrowserModule.Get().SyncBrowserToFolders(b); ContentBrowserModule.Get().SetSelectedPaths(b, true); } #endif #endif } UpdateProgress(100); return true; } bool ULoaderBPFunctionLibrary::CopyPhysicsAsset(USkeletalMesh *dstMesh, const USkeletalMesh *srcMesh, bool bResetCollisionTransform){ //GetTransientPackage #if WITH_EDITOR #if UE_VERSION_OLDER_THAN(5,4,0) if (srcMesh == nullptr || dstMesh == nullptr) return false; auto *srcPA = VRMGetPhysicsAsset(srcMesh); UPackage *pk = dstMesh->GetOutermost(); //FString name = srcPA->GetFName().ToString() + TEXT("_copy"); FString name = dstMesh->GetFName().ToString() + TEXT("_copy"); name.RemoveFromStart(TEXT("SK_")); name = TEXT("PHYS_") + name; UPhysicsAsset *dstPA = VRM4U_NewObject(pk, *name, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone); dstPA->Modify(); dstPA->SetPreviewMesh(dstMesh); TArray addBoneNameList; for (auto &a : srcPA->SkeletalBodySetups) { auto rigName = VRMGetSkeleton(srcMesh)->GetRigNodeNameFromBoneName(a->BoneName); if (rigName.IsNone()) { rigName = a->BoneName; } auto dstBoneName = VRMGetSkeleton(dstMesh)->GetRigBoneMapping(rigName); if (dstBoneName.IsNone()) { dstBoneName = rigName; } if (rigName.IsNone()==false && dstBoneName.IsNone()==false){ if (addBoneNameList.Find(dstBoneName) < 0) { USkeletalBodySetup *bs = Cast(StaticDuplicateObject(a, dstPA, NAME_None)); bs->BoneName = dstBoneName; addBoneNameList.Add(dstBoneName); auto srcIndex = VRMGetRefSkeleton(srcMesh).FindBoneIndex(a->BoneName); auto dstIndex = VRMGetRefSkeleton(dstMesh).FindBoneIndex(dstBoneName); FTransform srcTrans, dstTrans; while (srcIndex >= 0) { srcIndex = VRMGetRefSkeleton(srcMesh).GetParentIndex(srcIndex); if (srcIndex < 0) { break; } srcTrans = VRMGetRefSkeleton(srcMesh).GetRefBonePose()[srcIndex].GetRelativeTransform(srcTrans); } while (dstIndex >= 0) { dstIndex = VRMGetRefSkeleton(dstMesh).GetParentIndex(dstIndex); if (dstIndex < 0) { break; } dstTrans = VRMGetRefSkeleton(dstMesh).GetRefBonePose()[dstIndex].GetRelativeTransform(dstTrans); } if (bResetCollisionTransform) { for (int i = 0; i < bs->AggGeom.SphylElems.Num(); ++i) { bs->AggGeom.SphylElems[i].Center.Set(0, 0, 0); bs->AggGeom.SphylElems[i].Rotation = FRotator::ZeroRotator; //bs->AggGeom.SphylElems[i].Center.X = 0;// -v.Z; //bs->AggGeom.SphylElems[i].Center.Y = 0;// v.Y; //bs->AggGeom.SphylElems[i].Center.Z = 0;// v.X; } for (auto &b : bs->AggGeom.BoxElems) { b.Center.Set(0, 0, 0); b.Rotation = FRotator::ZeroRotator; } } dstPA->SkeletalBodySetups.Add(bs); } } } for (auto &a : srcPA->ConstraintSetup) { FName n[3]; { const FName nn[] = { a->DefaultInstance.ConstraintBone1, a->DefaultInstance.ConstraintBone2, a->DefaultInstance.JointName, }; for (int i = 0; i < 3; ++i) { n[i] = VRMGetSkeleton(srcMesh)->GetRigNodeNameFromBoneName(nn[i]); if (n[i].IsNone()) { n[i] = nn[i]; } } } const auto rigName1 = n[0]; const auto rigName2 = n[1]; const auto rigNameJ = n[2]; //const auto rigName1 = srcMesh->Skeleton->GetRigNodeNameFromBoneName(a->DefaultInstance.ConstraintBone1); //const auto rigName2 = srcMesh->Skeleton->GetRigNodeNameFromBoneName(a->DefaultInstance.ConstraintBone2); //const auto rigNameJ = srcMesh->Skeleton->GetRigNodeNameFromBoneName(a->DefaultInstance.JointName); UPhysicsConstraintTemplate *ct = Cast(StaticDuplicateObject(a, dstPA, NAME_None)); ct->DefaultInstance.ConstraintBone1 = VRMGetSkeleton(dstMesh)->GetRigBoneMapping(rigName1); ct->DefaultInstance.ConstraintBone2 = VRMGetSkeleton(dstMesh)->GetRigBoneMapping(rigName2); ct->DefaultInstance.JointName = VRMGetSkeleton(dstMesh)->GetRigBoneMapping(rigNameJ); { auto ind1 = VRMGetRefSkeleton(dstMesh).FindBoneIndex(ct->DefaultInstance.ConstraintBone1); auto ind2 = VRMGetRefSkeleton(dstMesh).FindBoneIndex(ct->DefaultInstance.ConstraintBone2); if (ind1 >= 0 && ind2 >= 0) { auto t = VRMGetRefSkeleton(dstMesh).GetRefBonePose()[ind1]; while (1) { ind1 = VRMGetRefSkeleton(dstMesh).GetParentIndex(ind1); if (ind1 == ind2) { break; } if (ind1 < 0) { break; } auto v = VRMGetRefSkeleton(dstMesh).GetRefBonePose()[ind1].GetLocation(); t.SetLocation(t.GetLocation() + v); } //auto indParent = dstMesh->RefSkeleton.GetParentIndex(ind1); ct->DefaultInstance.SetRefFrame(EConstraintFrame::Frame1, FTransform::Identity); ct->DefaultInstance.SetRefFrame(EConstraintFrame::Frame2, t); } } { /* auto s1 = a->DefaultInstance->ProfileInstance.ConeLimit.Swing1Motion; auto s2 = a->DefaultInstance->ProfileInstance.ConeLimit.Swing1LimitDegrees; ct->DefaultInstance.SetAngularTwistLimit(s1, s2); s1 = a->DefaultInstance->ProfileInstance.ConeLimit.Swing2Motion; s2 = a->DefaultInstance->ProfileInstance.ConeLimit.Swing2LimitDegrees; //ct->DefaultInstance.SetAngularSwing1Limit(EAngularConstraintMotion::ACM_Limited, 10); //ct->DefaultInstance.SetAngularSwing2Limit(EAngularConstraintMotion::ACM_Limited, 10); */ } dstPA->ConstraintSetup.Add(ct); } dstPA->UpdateBodySetupIndexMap(); dstPA->RefreshPhysicsAssetChange(); dstPA->UpdateBoundsBodiesArray(); #endif // 5.4 #endif // editor return true; } bool ULoaderBPFunctionLibrary::CopyVirtualBone(USkeletalMesh *dstMesh, const USkeletalMesh *srcMesh) { if (dstMesh == nullptr || srcMesh == nullptr) { return false; } #if UE_VERSION_OLDER_THAN(5,4,0) // virtual bone { const TArray& vTable = VRMGetSkeleton(srcMesh)->GetVirtualBones(); for (auto &t : vTable) { int32 id[] = { VRMGetRefSkeleton(dstMesh).FindBoneIndex(t.SourceBoneName), VRMGetRefSkeleton(dstMesh).FindBoneIndex(t.TargetBoneName), }; FName n[2] = { t.SourceBoneName, t.TargetBoneName, }; #if WITH_EDITOR { auto t1 = VRMGetSkeleton(srcMesh)->GetRigNodeNameFromBoneName(t.SourceBoneName); auto t2 = VRMGetSkeleton(srcMesh)->GetRigNodeNameFromBoneName(t.TargetBoneName); if (t1.IsNone()==false && t2.IsNone()==false) { auto r1 = VRMGetSkeleton(dstMesh)->GetRigBoneMapping(t1); auto r2 = VRMGetSkeleton(dstMesh)->GetRigBoneMapping(t2); if (r1.IsNone()==false && r2.IsNone()==false) { id[0] = VRMGetRefSkeleton(dstMesh).FindBoneIndex(r1); id[1] = VRMGetRefSkeleton(dstMesh).FindBoneIndex(r2); n[0] = r1; n[1] = r2; } } } #endif if (id[0] < 0) { n[0] = VRMGetRefSkeleton(dstMesh).GetBoneName(0); } if (id[1] < 0) { if (id[0] < 0) { continue; //n[1] = VRMGetRefSkeleton(dstMesh).GetBoneName(1); } } FName newName; if (VRMGetSkeleton(dstMesh)->AddNewVirtualBone(n[0], n[1], newName)) { VRMGetSkeleton(dstMesh)->RenameVirtualBone(newName, t.VirtualBoneName); } } } // socket { for (auto &t : VRMGetSkeleton(srcMesh)->Sockets) { int32 id = VRMGetRefSkeleton(dstMesh).FindBoneIndex(t->BoneName); FName n = t->BoneName; FRotator rot = t->RelativeRotation; FVector vec = t->RelativeLocation; #if WITH_EDITOR { auto t1 = VRMGetSkeleton(srcMesh)->GetRigNodeNameFromBoneName(t->BoneName); if (t1.IsNone() == false) { auto r1 = VRMGetSkeleton(dstMesh)->GetRigBoneMapping(t1); if (r1.IsNone() == false) { id = VRMGetRefSkeleton(dstMesh).FindBoneIndex(r1); n = r1; } } } #endif if (id < 0) { n = VRMGetRefSkeleton(dstMesh).GetBoneName(0); } bool bNewSocket = false; USkeletalMeshSocket *s = nullptr; s = VRMGetSkeleton(dstMesh)->FindSocket(t->SocketName); if (s == nullptr) { s = NewObject(dstMesh); bNewSocket = true; } { int32 bone1 = VRMGetRefSkeleton(srcMesh).FindBoneIndex(t->BoneName); int32 bone2 = VRMGetRefSkeleton(dstMesh).FindBoneIndex(n); auto getBoneTransform = [](auto &mesh, int32 bone) { auto &f = VRMGetRefSkeleton(mesh).GetRefBonePose(); if (bone < f.Num()) return f[bone]; return FTransform(); }; FTransform f1; while (bone1 >= 0) { f1 = f1 * getBoneTransform(srcMesh, bone1); bone1 = VRMGetRefSkeleton(srcMesh).GetParentIndex(bone1); } FTransform f2; while (bone2 >= 0) { f2 = f2 * getBoneTransform(dstMesh, bone2); bone2 = VRMGetRefSkeleton(dstMesh).GetParentIndex(bone2); } FTransform org; org.SetLocation(vec); f1.SetLocation(FVector::ZeroVector); f2.SetLocation(FVector::ZeroVector); FTransform tmp = f2 * f1.Inverse() * org * f1 * f2.Inverse(); vec = tmp.GetLocation(); } s->SocketName = t->SocketName; s->BoneName = n; s->RelativeLocation = vec; s->RelativeRotation = rot; s->RelativeScale = t->RelativeScale; s->bForceAlwaysAnimated = t->bForceAlwaysAnimated; if (bNewSocket) { VRMGetSkeleton(dstMesh)->Sockets.Add(s); } } } VRMGetSkeleton(dstMesh)->MarkPackageDirty(); #else // todo #endif return true; } bool ULoaderBPFunctionLibrary::CreateTailBone(USkeletalMesh *skeletalMesh, const TArray &boneName) { if (skeletalMesh == nullptr) { return false; } TArray tail; for (auto &b : boneName) { int first = VRMGetRefSkeleton(skeletalMesh).FindBoneIndex(*b); if (first < 0) { continue; } tail.AddUnique(first); } if (tail.Num() == 0) { return false; } USkeletalMesh *sk_tmp = skeletalMesh;// NewObject(GetTransientPackage(), NAME_None, EObjectFlags::RF_Public | RF_Transient); USkeleton *k_tmp = VRMGetSkeleton(skeletalMesh);//NewObject(GetTransientPackage(), NAME_None, EObjectFlags::RF_Public | RF_Transient); /* k_tmp->RecreateBoneTree(skeletalMesh); sk_tmp->Skeleton = k_tmp; sk_tmp->RefSkeleton = k_tmp->GetReferenceSkeleton(); sk_tmp->Skeleton->MergeAllBonesToBoneTree(sk_tmp); */ for (int i = 0; i < tail.Num(); ++i) { TArray children; VRMUtil::GetDirectChildBones(VRMGetRefSkeleton(sk_tmp), tail[i], children); if (children.Num()) { tail.RemoveAt(i); tail.Append(children); --i; } } bool bAdd = false; FReferenceSkeletonModifier RefSkelModifier(VRMGetRefSkeleton(sk_tmp), VRMGetSkeleton(sk_tmp)); for (int i = 0; i < tail.Num(); ++i) { FMeshBoneInfo b; FTransform t; b.ParentIndex = tail[i]; b.Name = VRMGetRefSkeleton(sk_tmp).GetBoneName(tail[i]); FString text = TEXT("_vrm4u_dummy_tail"); if (b.Name.ToString().Contains(text)) { continue; } b.Name = *(b.Name.ToString() + text); t = VRMGetRefSkeleton(sk_tmp).GetRefBonePose()[tail[i]]; RefSkelModifier.Add(b, t); bAdd = true; } if (bAdd) { VRMGetRefSkeleton(sk_tmp).RebuildRefSkeleton(VRMGetSkeleton(sk_tmp), true); VRMGetSkeleton(skeletalMesh)->ClearCacheData(); VRMGetSkeleton(skeletalMesh)->MergeAllBonesToBoneTree(sk_tmp); //skeletalMesh->RefSkeleton.RebuildRefSkeleton(skeletalMesh->Skeleton, true); { FSkeletalMeshLODRenderData &rd = skeletalMesh->GetResourceForRendering()->LODRenderData[0]; { int boneNum = VRMGetSkeleton(skeletalMesh)->GetReferenceSkeleton().GetRawBoneNum(); rd.RequiredBones.SetNum(boneNum); rd.ActiveBoneIndices.SetNum(boneNum); #if WITH_EDITOR FSkeletalMeshLODModel *p = &(skeletalMesh->GetImportedModel()->LODModels[0]); p->ActiveBoneIndices.SetNum(boneNum); p->RequiredBones.SetNum(boneNum); #endif for (int i = 0; i < boneNum; ++i) { rd.RequiredBones[i] = i; rd.ActiveBoneIndices[i] = i; #if WITH_EDITOR p->ActiveBoneIndices[i] = i; p->RequiredBones[i] = i; #endif } } } skeletalMesh->Modify(); VRMGetSkeleton(skeletalMesh)->Modify(); #if WITH_EDITOR skeletalMesh->PostEditChange(); VRMGetSkeleton(skeletalMesh)->PostEditChange(); #endif } return true; } #if WITH_EDITOR #if UE_VERSION_OLDER_THAN(5,0,0) #else static void LocalEpicSkeletonSetup(UIKRigController *rigcon) { if (rigcon == nullptr) return; rigcon->SetRetargetRoot(TEXT("Pelvis")); while (rigcon->GetRetargetChains().Num()) { rigcon->RemoveRetargetChain(rigcon->GetRetargetChains()[0].ChainName); } VRMAddRetargetChain(rigcon, TEXT("root"), TEXT("root"), TEXT("root")); int sol_index = 0; #if UE_VERSION_OLDER_THAN(5,2,0) auto* sol = rigcon->GetSolver(sol_index); #else auto* sol = rigcon->GetSolverAtIndex(sol_index); #endif if (sol == nullptr) { #if UE_VERSION_OLDER_THAN(5,2,0) sol_index = rigcon->AddSolver(UIKRigPBIKSolver::StaticClass()); sol = rigcon->GetSolver(sol_index); #elif UE_VERSION_OLDER_THAN(5,6,0) sol_index = rigcon->AddSolver(UIKRigFBIKSolver::StaticClass()); sol = rigcon->GetSolverAtIndex(sol_index); #else sol_index = rigcon->AddSolver(FIKRigFullBodyIKSolver::StaticStruct()); sol = rigcon->GetSolverAtIndex(sol_index); #endif } if (sol == nullptr) return; #if UE_VERSION_OLDER_THAN(5,6,0) sol->SetRootBone(TEXT("root")); #else sol->SetStartBone(TEXT("root")); #endif { TArray a = { TEXT("Hand_L"), TEXT("Hand_R"), TEXT("ball_l"), TEXT("ball_r"), }; for (int i = 0; i < a.Num(); ++i) { #if UE_VERSION_OLDER_THAN(5,2,0) auto* goal = rigcon->AddNewGoal(*(a[i] + TEXT("_Goal")), *a[i]); if (goal) { rigcon->ConnectGoalToSolver(*goal, sol_index); } #else auto goal = rigcon->AddNewGoal(*(a[i] + TEXT("_Goal")), *a[i]); if (goal != NAME_None) { rigcon->ConnectGoalToSolver(goal, sol_index); } #endif } } } #endif #endif //void ULoaderBPFunctionLibrary::VRMGenerateEpicSkeletonToHumanoidIKRig(USkeletalMesh *skeletalMesh, UObject*& rig_o, UObject*& ikr_o){ void ULoaderBPFunctionLibrary::VRMGenerateEpicSkeletonToHumanoidIKRig(USkeletalMesh * srcSkeletalMesh, UObject * &outRigIK, UObject * &outIKRetargeter, UObject * targetRigIK){ #if WITH_EDITOR #if UE_VERSION_OLDER_THAN(5,0,0) #else USkeletalMesh* sk = srcSkeletalMesh; if (sk == nullptr) { return; } auto LocalGetController = [](UIKRigDefinition * rig) { #if UE_VERSION_OLDER_THAN(5,2,0) return UIKRigController::GetIKRigController(rig); #else return UIKRigController::GetController(rig); #endif }; { const FString PkgPath = sk->GetPathName();// GetPackage().pathn const FString SavePackagePath = FPaths::GetPath(PkgPath); UIKRigDefinition* rig = nullptr; { FString name = FString(TEXT("IK_")) + sk->GetName() + TEXT("_VrmHumanoid"); UPackage* pkg = CreatePackage(*(SavePackagePath +"/"+ name)); { auto* a = FindObject(NULL, *(SavePackagePath + "/" + name + TEXT(".") + name)); if (a != nullptr) { rig = a; } } if (rig == nullptr) { rig = VRM4U_NewObject(pkg, *name, RF_Public | RF_Standalone); } UIKRigController* rigcon = LocalGetController(rig); rigcon->SetSkeletalMesh(sk); LocalEpicSkeletonSetup(rigcon); // bone chain for (auto& modelName : VRMUtil::table_ue4_vrm) { if (modelName.BoneVRM == "") continue; //if (modelName.BoneUE4 == "") continue; // commentout add as none // spine int type = 0; if (modelName.BoneVRM == TEXT("spine")) { type = 1; } if (modelName.BoneVRM == TEXT("chest") || modelName.BoneVRM == TEXT("upperChest")) { type = 2; } switch (type) { case 0: VRMAddRetargetChain(rigcon, *modelName.BoneVRM, *modelName.BoneUE4, *modelName.BoneUE4); break; case 1: if (sk->GetRefSkeleton().FindBoneIndex(TEXT("spine_05")) != INDEX_NONE) { VRMAddRetargetChain(rigcon, TEXT("spine"), TEXT("spine_01"), TEXT("spine_05")); } else { VRMAddRetargetChain(rigcon, TEXT("spine"), TEXT("spine_01"), TEXT("spine_03")); } break; default: break; } } //VRMAddRetargetChain(rigcon, TEXT("leftEye"), NAME_None, NAME_None); //VRMAddRetargetChain(rigcon, TEXT("rightEye"), NAME_None, NAME_None); } { // sub epic bone UIKRigDefinition* rig_epic = nullptr; FString name = FString(TEXT("IK_")) + sk->GetName() + TEXT("_MannequinBone"); UPackage* pkg = CreatePackage(*(SavePackagePath + "/" + name)); { auto* a = FindObject(NULL, *(SavePackagePath + "/" + name + TEXT(".") + name)); if (a != nullptr) { rig_epic = a; } } if (rig_epic == nullptr) { rig_epic = VRM4U_NewObject(pkg, *name, RF_Public | RF_Standalone); } UIKRigController* rigcon = LocalGetController(rig_epic); rigcon->SetSkeletalMesh(sk); LocalEpicSkeletonSetup(rigcon); // bone chain for (auto& modelName : VRMUtil::table_ue4_vrm) { if (modelName.BoneVRM == "") continue; if (modelName.BoneUE4 == "") continue; VRMAddRetargetChain(rigcon, *modelName.BoneUE4, *modelName.BoneUE4, *modelName.BoneUE4); } } /* { // sub ik UIKRigDefinition* ik = nullptr; { FString name = FString(TEXT("IK_")) + sk->GetName() + TEXT("_EpicSkeleton_IK"); UPackage* pkg = CreatePackage(*(SavePackagePath + "/" + name)); { auto* a = FindObject(NULL, *(SavePackagePath + "/" + name + TEXT(".") + name)); if (a != nullptr) { ik = a; } } if (ik == nullptr) { ik = VRM4U_NewObject(pkg, *name, RF_Public | RF_Standalone); } UIKRigController* rigcon = UIKRigController::GetIKRigController(ik); rigcon->SetSkeletalMesh(sk); LocalEpicSkeletonSetup(rigcon); struct TT { FString s1; FString s2; FString s3; }; TArray table = { {TEXT("pelvis"), TEXT("spine_03"), TEXT(""),}, {TEXT("neck_01"), TEXT("head"), TEXT(""),}, {TEXT("clavicle_l"), TEXT("hand_l"), TEXT("hand_l_Goal"),}, {TEXT("clavicle_r"), TEXT("hand_r"), TEXT("hand_r_Goal"),}, {TEXT("thigh_l"), TEXT("ball_l"), TEXT("ball_l_Goal"),}, {TEXT("thigh_r"), TEXT("ball_r"), TEXT("ball_r_Goal"),}, {TEXT("thumb_01_l"), TEXT("thumb_03_l"), TEXT(""),}, {TEXT("index_01_l"), TEXT("index_03_l"), TEXT(""),}, {TEXT("middle_01_l"), TEXT("middle_03_l"), TEXT(""),}, {TEXT("ring_01_l"), TEXT("ring_03_l"), TEXT(""),}, {TEXT("pinky_01_l"), TEXT("pinky_03_l"), TEXT(""),}, {TEXT("thumb_01_r"), TEXT("thumb_03_r"), TEXT(""),}, {TEXT("index_01_r"), TEXT("index_03_r"), TEXT(""),}, {TEXT("middle_01_r"), TEXT("middle_03_r"), TEXT(""),}, {TEXT("ring_01_r"), TEXT("ring_03_r"), TEXT(""),}, {TEXT("pinky_01_r"), TEXT("pinky_03_r"), TEXT(""),}, }; for (auto& a : table) { rigcon->AddRetargetChain(*a.s1, *a.s1, *a.s2); if (a.s3 != "") { rigcon->SetRetargetChainGoal(*a.s1, *a.s3); } } } } */ UIKRetargeter* ikr = nullptr; { FString name = FString(TEXT("RTG_")) + sk->GetName(); UPackage* pkg = CreatePackage(*(SavePackagePath +"/"+ name)); { auto* a = FindObject(NULL, *(SavePackagePath + "/" + name + TEXT(".") + name)); if (a != nullptr) { ikr = a; } } if (ikr == nullptr) { ikr = VRM4U_NewObject(pkg, *name, RF_Public | RF_Standalone); } UIKRetargeterController* c = UIKRetargeterController::GetController(ikr); #if UE_VERSION_OLDER_THAN(5,2,0) c->SetSourceIKRig(rig); #else c->SetIKRig(ERetargetSourceOrTarget::Source, rig); #endif } rig->PostEditChange(); ikr->PostEditChange(); outRigIK = rig; outIKRetargeter = ikr; } #endif #endif // editor return; } void ULoaderBPFunctionLibrary::VRMGenerateIKRetargeterPose(UObject* IKRetargeter, UObject* targetRigIK, UPoseAsset* targetPose) { #if WITH_EDITOR #if UE_VERSION_OLDER_THAN(5,0,0) #else if (targetPose == nullptr) return; UIKRetargeter* ikr = Cast(IKRetargeter); if (ikr == nullptr) return; UIKRetargeterController* c = UIKRetargeterController::GetController(ikr); if (c == nullptr) return; #if UE_VERSION_OLDER_THAN(5,1,0) // setup A-Pose if (targetRigIK) { c->SetTargetIKRig(Cast(targetRigIK)); } if (c->GetAsset()->GetTargetIKRig() && targetPose) { //UIKRigDefinition* d = Cast(targetRigIK); USkeletalMesh* targetSK = targetPose->GetPreviewMesh(); UPoseAsset* pose = targetPose; for (int poseNum = 0; poseNum < 10; ++poseNum) { auto poseName = pose->GetPoseNameByIndex(poseNum); if (poseName == NAME_None) break; // add pose if (poseName == c->MakePoseNameUnique(poseName)) { c->AddRetargetPose(poseName); } c->SetCurrentRetargetPose(poseName); c->ResetRetargetPose(poseName); // A-pose TArray outTrans; pose->GetFullPose(poseNum, outTrans); auto& rsk = targetSK->GetRefSkeleton(); for (int i = 0; i < rsk.GetRawBoneNum(); ++i) { if (outTrans.IsValidIndex(i) == false) { continue; } auto q = outTrans[i].GetRotation(); c->SetRotationOffsetForRetargetPoseBone(targetSK->GetRefSkeleton().GetBoneName(i), q); } } // set A-pose as current pose FName poseName_A = pose->GetPoseNameByIndex(1); c->SetCurrentRetargetPose(poseName_A); } #else #endif #endif #endif // editor }