Files
2026-05-19 13:42:41 +07:00

1640 lines
47 KiB
C++

// 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 <windows.h>
#include "Windows/HideWindowsPlatformTypes.h"
#endif
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#include <assimp/GltfMaterial.h>
#include <assimp/vrm/vrmmeta.h>
#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<IImageWrapperModule>(FName("ImageWrapper"));
TSharedPtr<IImageWrapper> 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<uint8> RawData;
#endif
const TArray<uint8>* 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<FReferenceSkeleton&>(targetHumanoidSkeleton->GetReferenceSkeleton());
auto &allbone = const_cast<TArray<FMeshBoneInfo> &>(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<uint8> 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<uint8> 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<VRM::VRMMetadata*>(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<FVrmAsyncLoadAction>(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<uint8> 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<UClass> c;
#else
TSoftObjectPtr<UClass> c;
#endif
if (c == nullptr) {
FSoftObjectPath r(TEXT("/VRM4U/VrmAssetListObjectBP.VrmAssetListObjectBP"));
UObject* u = r.TryLoad();
if (u) {
c = (UClass*)(Cast<UBlueprint>(u)->GeneratedClass);
}
}
if (c == nullptr) {
c = UVrmAssetListObject::StaticClass();
}
#if UE_VERSION_OLDER_THAN(5,0,0)
TAssetPtr<UVrmAssetListObject> m = NewObject<UVrmAssetListObject>((UObject*)GetTransientPackage(), c.Get());
#else
TSoftObjectPtr<UVrmAssetListObject> m = NewObject<UVrmAssetListObject>((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<UVrmAssetListObject>(StaticDuplicateObject(InVrmAsset, s_vrm_package, NAME_None));
} else {
if (InVrmAsset->ReimportBase) {
out = InVrmAsset->ReimportBase;
}
else {
out = VRM4U_NewObject<UVrmAssetListObject>(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<UAssetImportData>(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<FContentBrowserModule>("ContentBrowser");
{
TArray<UObject*> a = { OutVrmAsset };
//ContentBrowserModule.Get().SyncBrowserToAssets(a);
}
{
auto path = FPaths::GetPath(OutVrmAsset->GetPathName());
const TArray<FString> 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<UPhysicsAsset>(pk, *name, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone);
dstPA->Modify();
dstPA->SetPreviewMesh(dstMesh);
TArray<FName> 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<USkeletalBodySetup>(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<UPhysicsConstraintTemplate>(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<FVirtualBone>& 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<USkeletalMeshSocket>(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<FString> &boneName) {
if (skeletalMesh == nullptr) {
return false;
}
TArray<int32> 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<USkeletalMesh>(GetTransientPackage(), NAME_None, EObjectFlags::RF_Public | RF_Transient);
USkeleton *k_tmp = VRMGetSkeleton(skeletalMesh);//NewObject<UVrmSkeleton>(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<int32> 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<FString> 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<UIKRigDefinition>(NULL, *(SavePackagePath + "/" + name + TEXT(".") + name));
if (a != nullptr) {
rig = a;
}
}
if (rig == nullptr) {
rig = VRM4U_NewObject<UIKRigDefinition>(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<UIKRigDefinition>(NULL, *(SavePackagePath + "/" + name + TEXT(".") + name));
if (a != nullptr) {
rig_epic = a;
}
}
if (rig_epic == nullptr) {
rig_epic = VRM4U_NewObject<UIKRigDefinition>(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<UIKRigDefinition>(NULL, *(SavePackagePath + "/" + name + TEXT(".") + name));
if (a != nullptr) {
ik = a;
}
}
if (ik == nullptr) {
ik = VRM4U_NewObject<UIKRigDefinition>(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<TT> 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<UIKRetargeter>(NULL, *(SavePackagePath + "/" + name + TEXT(".") + name));
if (a != nullptr) {
ikr = a;
}
}
if (ikr == nullptr) {
ikr = VRM4U_NewObject<UIKRetargeter>(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<UIKRetargeter>(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<UIKRigDefinition>(targetRigIK));
}
if (c->GetAsset()->GetTargetIKRig() && targetPose) {
//UIKRigDefinition* d = Cast<UIKRigDefinition>(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<FTransform> 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
}