209 lines
7.0 KiB
C++
209 lines
7.0 KiB
C++
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||
|
|
|
||
|
|
#include "ApexClothingUtils.h"
|
||
|
|
#include "Components/SkeletalMeshComponent.h"
|
||
|
|
#include "Misc/MessageDialog.h"
|
||
|
|
#include "Misc/FileHelper.h"
|
||
|
|
#include "Misc/Paths.h"
|
||
|
|
#include "Modules/ModuleManager.h"
|
||
|
|
#include "UObject/UObjectHash.h"
|
||
|
|
#include "UObject/UObjectIterator.h"
|
||
|
|
#include "EditorDirectories.h"
|
||
|
|
#include "IDesktopPlatform.h"
|
||
|
|
#include "DesktopPlatformModule.h"
|
||
|
|
#include "Framework/Application/SlateApplication.h"
|
||
|
|
#include "Rendering/SkeletalMeshModel.h"
|
||
|
|
|
||
|
|
#include "ClothingAssetFactory.h"
|
||
|
|
#include "PhysicsPublic.h"
|
||
|
|
#include "PhysXIncludes.h"
|
||
|
|
|
||
|
|
DEFINE_LOG_CATEGORY_STATIC(LogApexClothingUtils, Log, All);
|
||
|
|
|
||
|
|
#define LOCTEXT_NAMESPACE "ApexClothingUtils"
|
||
|
|
|
||
|
|
namespace ApexClothingUtils
|
||
|
|
{
|
||
|
|
|
||
|
|
// enforces a call of "OnRegister" to update vertex factories
|
||
|
|
void ReregisterSkelMeshComponents(USkeletalMesh* SkelMesh)
|
||
|
|
{
|
||
|
|
for (TObjectIterator<USkeletalMeshComponent> It; It; ++It)
|
||
|
|
{
|
||
|
|
USkeletalMeshComponent* MeshComponent = *It;
|
||
|
|
if (MeshComponent &&
|
||
|
|
!MeshComponent->IsTemplate() &&
|
||
|
|
MeshComponent->SkeletalMesh == SkelMesh)
|
||
|
|
{
|
||
|
|
MeshComponent->ReregisterComponent();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void RefreshSkelMeshComponents(USkeletalMesh* SkelMesh)
|
||
|
|
{
|
||
|
|
for (TObjectIterator<USkeletalMeshComponent> It; It; ++It)
|
||
|
|
{
|
||
|
|
USkeletalMeshComponent* MeshComponent = *It;
|
||
|
|
if (MeshComponent &&
|
||
|
|
!MeshComponent->IsTemplate() &&
|
||
|
|
MeshComponent->SkeletalMesh == SkelMesh)
|
||
|
|
{
|
||
|
|
MeshComponent->RecreateRenderState_Concurrent();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#if WITH_APEX_CLOTHING
|
||
|
|
FString PromptForClothingFile()
|
||
|
|
{
|
||
|
|
if (IDesktopPlatform* Platform = FDesktopPlatformModule::Get())
|
||
|
|
{
|
||
|
|
const void* ParentWindowWindowHandle = FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr);
|
||
|
|
|
||
|
|
TArray<FString> OpenFilenames;
|
||
|
|
FString OpenFilePath = FEditorDirectories::Get().GetLastDirectory(ELastDirectory::MESH_IMPORT_EXPORT);
|
||
|
|
|
||
|
|
if (Platform->OpenFileDialog(
|
||
|
|
ParentWindowWindowHandle,
|
||
|
|
*LOCTEXT("ImportClothing_ChooseFile", "Choose clothing asset source file").ToString(),
|
||
|
|
OpenFilePath,
|
||
|
|
TEXT(""),
|
||
|
|
TEXT("APEX clothing asset(*.apx,*.apb)|*.apx;*.apb|All files (*.*)|*.*"),
|
||
|
|
EFileDialogFlags::None,
|
||
|
|
OpenFilenames))
|
||
|
|
{
|
||
|
|
return OpenFilenames[0];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Nothing picked, empty path
|
||
|
|
return FString();
|
||
|
|
}
|
||
|
|
|
||
|
|
void PromptAndImportClothing(USkeletalMesh* SkelMesh)
|
||
|
|
{
|
||
|
|
ensure(SkelMesh);
|
||
|
|
|
||
|
|
FString Filename = PromptForClothingFile();
|
||
|
|
|
||
|
|
if (!Filename.IsEmpty())
|
||
|
|
{
|
||
|
|
FEditorDirectories::Get().SetLastDirectory(ELastDirectory::MESH_IMPORT_EXPORT, Filename);
|
||
|
|
|
||
|
|
UClothingAssetFactory* Factory = UClothingAssetFactory::StaticClass()->GetDefaultObject<UClothingAssetFactory>(); // TODO: Rename this class to UClothingAssetFactoryNv
|
||
|
|
|
||
|
|
if (Factory && Factory->CanImport(Filename))
|
||
|
|
{
|
||
|
|
Factory->Import(Filename, SkelMesh);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
apex::ClothingAsset* CreateApexClothingAssetFromPxStream(physx::PxFileBuf& Stream)
|
||
|
|
{
|
||
|
|
// Peek into the buffer to see what kind of data it is (binary or xml)
|
||
|
|
NvParameterized::Serializer::SerializeType SerializeType = GApexSDK->getSerializeType(Stream);
|
||
|
|
// Create an NvParameterized serializer for the correct data type
|
||
|
|
NvParameterized::Serializer* Serializer = GApexSDK->createSerializer(SerializeType);
|
||
|
|
|
||
|
|
if (!Serializer)
|
||
|
|
{
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
// Deserialize into a DeserializedData buffer
|
||
|
|
NvParameterized::Serializer::DeserializedData DeserializedData;
|
||
|
|
NvParameterized::Serializer::ErrorType Error = Serializer->deserialize(Stream, DeserializedData);
|
||
|
|
|
||
|
|
apex::Asset* ApexAsset = NULL;
|
||
|
|
if (DeserializedData.size() > 0)
|
||
|
|
{
|
||
|
|
// The DeserializedData has something in it, so create an APEX asset from it
|
||
|
|
ApexAsset = GApexSDK->createAsset(DeserializedData[0], NULL);
|
||
|
|
}
|
||
|
|
// Release the serializer
|
||
|
|
Serializer->release();
|
||
|
|
|
||
|
|
return (apex::ClothingAsset*)ApexAsset;
|
||
|
|
}
|
||
|
|
|
||
|
|
apex::ClothingAsset* CreateApexClothingAssetFromBuffer(const uint8* Buffer, int32 BufferSize)
|
||
|
|
{
|
||
|
|
apex::ClothingAsset* ApexClothingAsset = NULL;
|
||
|
|
|
||
|
|
// Wrap Buffer with the APEX read stream class
|
||
|
|
physx::PxFileBuf* Stream = GApexSDK->createMemoryReadStream(Buffer, BufferSize);
|
||
|
|
|
||
|
|
if (Stream != NULL)
|
||
|
|
{
|
||
|
|
ApexClothingAsset = ApexClothingUtils::CreateApexClothingAssetFromPxStream(*Stream);
|
||
|
|
// Release our stream
|
||
|
|
GApexSDK->releaseMemoryReadStream(*Stream);
|
||
|
|
}
|
||
|
|
|
||
|
|
return ApexClothingAsset;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif // #if WITH_APEX_CLOTHING
|
||
|
|
|
||
|
|
void RestoreAllClothingSections(USkeletalMesh* SkelMesh, uint32 LODIndex, uint32 AssetIndex)
|
||
|
|
{
|
||
|
|
if (FSkeletalMeshModel* Resource = SkelMesh->GetImportedModel())
|
||
|
|
{
|
||
|
|
for (FSkeletalMeshLODModel& LodModel: Resource->LODModels)
|
||
|
|
{
|
||
|
|
for (FSkelMeshSection& Section: LodModel.Sections)
|
||
|
|
{
|
||
|
|
if (Section.HasClothingData())
|
||
|
|
{
|
||
|
|
ClothingAssetUtils::ClearSectionClothingData(Section);
|
||
|
|
if (FSkelMeshSourceSectionUserData* UserSectionData = LodModel.UserSectionsData.Find(Section.OriginalDataSectionIndex))
|
||
|
|
{
|
||
|
|
UserSectionData->CorrespondClothAssetIndex = INDEX_NONE;
|
||
|
|
UserSectionData->ClothingData.AssetLodIndex = INDEX_NONE;
|
||
|
|
UserSectionData->ClothingData.AssetGuid = FGuid();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void RemoveAssetFromSkeletalMesh(USkeletalMesh* SkelMesh, uint32 AssetIndex, bool bReleaseAsset, bool bRecreateSkelMeshComponent)
|
||
|
|
{
|
||
|
|
FSkeletalMeshModel* ImportedResource = SkelMesh->GetImportedModel();
|
||
|
|
int32 NumLODs = ImportedResource->LODModels.Num();
|
||
|
|
|
||
|
|
for (int32 LODIdx = 0; LODIdx < NumLODs; LODIdx++)
|
||
|
|
{
|
||
|
|
RestoreAllClothingSections(SkelMesh, LODIdx, AssetIndex);
|
||
|
|
}
|
||
|
|
|
||
|
|
#if WITH_APEX_CLOTHING
|
||
|
|
apex::ClothingAsset* ApexClothingAsset = SkelMesh->ClothingAssets_DEPRECATED[AssetIndex].ApexClothingAsset; // Can't delete apex asset until after apex actors so we save this for now and reregister component (which will trigger the actor delete)
|
||
|
|
#endif // WITH_APEX_CLOTHING
|
||
|
|
SkelMesh->ClothingAssets_DEPRECATED.RemoveAt(AssetIndex); // have to remove the asset from the array so that new actors are not created for asset pending deleting
|
||
|
|
|
||
|
|
SkelMesh->PostEditChange(); // update derived data
|
||
|
|
|
||
|
|
ReregisterSkelMeshComponents(SkelMesh);
|
||
|
|
|
||
|
|
#if WITH_APEX_CLOTHING
|
||
|
|
if (bReleaseAsset)
|
||
|
|
{
|
||
|
|
// Now we can actually delete the asset
|
||
|
|
GPhysCommandHandler->DeferredRelease(ApexClothingAsset);
|
||
|
|
}
|
||
|
|
#endif // WITH_APEX_CLOTHING
|
||
|
|
|
||
|
|
if (bRecreateSkelMeshComponent)
|
||
|
|
{
|
||
|
|
// Refresh skeletal mesh components
|
||
|
|
RefreshSkelMeshComponents(SkelMesh);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace ApexClothingUtils
|
||
|
|
|
||
|
|
#undef LOCTEXT_NAMESPACE
|