Files
UnrealEngineUWP/Engine/Source/Editor/MeshPaint/Private/MeshPaintSkeletalMeshAdapter.cpp
Josie Yang 6b15506e58 Replace direct access to SkeletalMesh object from USkinnedMeshComponent with GetSkeletalMesh function
#rb kriss.gossart
#preflight 62aafc9ada0af39a4783930a

[CL 20686007 by Josie Yang in ue5-main branch]
2022-06-16 09:14:04 -04:00

530 lines
20 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MeshPaintSkeletalMeshAdapter.h"
#include "Engine/SkeletalMesh.h"
#include "PhysicsEngine/BodySetup.h"
#include "MeshPaintHelpers.h"
#include "MeshPaintTypes.h"
#include "ComponentReregisterContext.h"
#include "Rendering/SkeletalMeshRenderData.h"
#include "Rendering/SkeletalMeshModel.h"
#include "Factories/FbxSkeletalMeshImportData.h"
DEFINE_LOG_CATEGORY_STATIC(LogMeshPaintSkeletalMeshAdapter, Log, All);
//////////////////////////////////////////////////////////////////////////
// FMeshPaintGeometryAdapterForSkeletalMeshes
FMeshPaintGeometryAdapterForSkeletalMeshes::FMeshToComponentMap FMeshPaintGeometryAdapterForSkeletalMeshes::MeshToComponentMap;
//HACK for 4.24.2 we cannot change public API so we use this global function to remap and propagate the vertex color data to the imported model when the user release the mouse
void PropagateVertexPaintToAsset(USkeletalMesh* SkeletalMesh, int32 LODIndex)
{
struct FMatchFaceData
{
int32 SoftVerticeIndexes[3];
};
if (!SkeletalMesh || SkeletalMesh->IsLODImportedDataEmpty(LODIndex) || !SkeletalMesh->IsLODImportedDataBuildAvailable(LODIndex))
{
//We do not propagate vertex color for old asset
return;
}
auto GetMatchKey = [](const FVector& PositionA, const FVector& PositionB, const FVector& PositionC)->FSHAHash
{
FSHA1 SHA;
FSHAHash SHAHash;
SHA.Update((const uint8*)&PositionA, sizeof(FVector));
SHA.Update((const uint8*)&PositionB, sizeof(FVector));
SHA.Update((const uint8*)&PositionC, sizeof(FVector));
SHA.Final();
SHA.GetHash(&SHAHash.Hash[0]);
return SHAHash;
};
FSkeletalMeshLODModel& LODModel = SkeletalMesh->GetImportedModel()->LODModels[LODIndex];
const TArray<uint32>& SrcIndexBuffer = LODModel.IndexBuffer;
TArray<FSoftSkinVertex> SrcVertices;
LODModel.GetVertices(SrcVertices);
FSkeletalMeshImportData ImportData;
SkeletalMesh->LoadLODImportedData(LODIndex, ImportData);
TMap<FSHAHash, FMatchFaceData> MatchTriangles;
MatchTriangles.Reserve(ImportData.Wedges.Num());
for (int32 IndexBufferIndex = 0, SrcIndexBufferNum = SrcIndexBuffer.Num(); IndexBufferIndex < SrcIndexBufferNum; IndexBufferIndex += 3)
{
FVector PositionA = (FVector)SrcVertices[SrcIndexBuffer[IndexBufferIndex]].Position;
FVector PositionB = (FVector)SrcVertices[SrcIndexBuffer[IndexBufferIndex + 1]].Position;
FVector PositionC = (FVector)SrcVertices[SrcIndexBuffer[IndexBufferIndex + 2]].Position;
FSHAHash Key = GetMatchKey(PositionA, PositionB, PositionC);
FMatchFaceData MatchFaceData;
MatchFaceData.SoftVerticeIndexes[0] = SrcIndexBuffer[IndexBufferIndex];
MatchFaceData.SoftVerticeIndexes[1] = SrcIndexBuffer[IndexBufferIndex + 1];
MatchFaceData.SoftVerticeIndexes[2] = SrcIndexBuffer[IndexBufferIndex + 2];
MatchTriangles.Add(Key, MatchFaceData);
}
bool bAllVertexFound = true;
for (int32 FaceIndex = 0, FaceNum = ImportData.Faces.Num(); FaceIndex < FaceNum; ++FaceIndex)
{
const SkeletalMeshImportData::FTriangle& Triangle = ImportData.Faces[FaceIndex];
SkeletalMeshImportData::FVertex& WedgeA = ImportData.Wedges[Triangle.WedgeIndex[0]];
SkeletalMeshImportData::FVertex& WedgeB = ImportData.Wedges[Triangle.WedgeIndex[1]];
SkeletalMeshImportData::FVertex& WedgeC = ImportData.Wedges[Triangle.WedgeIndex[2]];
FVector PositionA = (FVector)ImportData.Points[WedgeA.VertexIndex];
FVector PositionB = (FVector)ImportData.Points[WedgeB.VertexIndex];
FVector PositionC = (FVector)ImportData.Points[WedgeC.VertexIndex];
const FSHAHash Key = GetMatchKey(PositionA, PositionB, PositionC);
if (FMatchFaceData* MatchFaceData = MatchTriangles.Find(Key))
{
WedgeA.Color = SrcVertices[MatchFaceData->SoftVerticeIndexes[0]].Color;
WedgeB.Color = SrcVertices[MatchFaceData->SoftVerticeIndexes[1]].Color;
WedgeC.Color = SrcVertices[MatchFaceData->SoftVerticeIndexes[2]].Color;
}
else if(bAllVertexFound)
{
bAllVertexFound = false;
FString SkeletalMeshName(SkeletalMesh->GetName());
UE_LOG(LogMeshPaintSkeletalMeshAdapter, Warning, TEXT("Some vertex color data could not be applied to the %s SkeletalMesh asset."), *SkeletalMeshName);
}
}
SkeletalMesh->SaveLODImportedData(LODIndex, ImportData);
}
bool FMeshPaintGeometryAdapterForSkeletalMeshes::Construct(UMeshComponent* InComponent, int32 InMeshLODIndex)
{
SkeletalMeshComponent = Cast<USkeletalMeshComponent>(InComponent);
if (SkeletalMeshComponent != nullptr)
{
SkeletalMeshChangedHandle = SkeletalMeshComponent->RegisterOnSkeletalMeshPropertyChanged(USkeletalMeshComponent::FOnSkeletalMeshPropertyChanged::CreateRaw(this, &FMeshPaintGeometryAdapterForSkeletalMeshes::OnSkeletalMeshChanged));
if (SkeletalMeshComponent->GetSkeletalMesh() != nullptr)
{
ReferencedSkeletalMesh = SkeletalMeshComponent->GetSkeletalMesh();
MeshLODIndex = InMeshLODIndex;
const bool bSuccess = Initialize();
return bSuccess;
}
}
return false;
}
FMeshPaintGeometryAdapterForSkeletalMeshes::~FMeshPaintGeometryAdapterForSkeletalMeshes()
{
if (SkeletalMeshComponent != nullptr)
{
SkeletalMeshComponent->UnregisterOnSkeletalMeshPropertyChanged(SkeletalMeshChangedHandle);
}
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::OnSkeletalMeshChanged()
{
OnRemoved();
ReferencedSkeletalMesh = SkeletalMeshComponent->GetSkeletalMesh();
if (SkeletalMeshComponent->GetSkeletalMesh() != nullptr)
{
Initialize();
OnAdded();
}
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::OnPostMeshCached(USkeletalMesh* SkeletalMesh)
{
if (ReferencedSkeletalMesh == SkeletalMesh)
{
OnSkeletalMeshChanged();
}
}
bool FMeshPaintGeometryAdapterForSkeletalMeshes::Initialize()
{
check(ReferencedSkeletalMesh == SkeletalMeshComponent->GetSkeletalMesh());
bool bInitializationResult = false;
MeshResource = ReferencedSkeletalMesh->GetResourceForRendering();
if (MeshResource != nullptr)
{
LODData = &MeshResource->LODRenderData[MeshLODIndex];
checkf(ReferencedSkeletalMesh->GetImportedModel()->LODModels.IsValidIndex(MeshLODIndex), TEXT("Invalid Imported Model index for vertex painting"));
LODModel = &ReferencedSkeletalMesh->GetImportedModel()->LODModels[MeshLODIndex];
bInitializationResult = FBaseMeshPaintGeometryAdapter::Initialize();
}
return bInitializationResult;
}
bool FMeshPaintGeometryAdapterForSkeletalMeshes::InitializeVertexData()
{
// Retrieve mesh vertex and index data
const int32 NumVertices = LODData->GetNumVertices();
MeshVertices.Reset();
MeshVertices.AddDefaulted(NumVertices);
for (int32 Index = 0; Index < NumVertices; Index++)
{
const FVector3f& Position = LODData->StaticVertexBuffers.PositionVertexBuffer.VertexPosition(Index);
MeshVertices[Index] = (FVector)Position;
}
MeshIndices.Reserve(LODData->MultiSizeIndexContainer.GetIndexBuffer()->Num());
LODData->MultiSizeIndexContainer.GetIndexBuffer(MeshIndices);
return (MeshVertices.Num() >= 0 && MeshIndices.Num() > 0);
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::InitializeAdapterGlobals()
{
static bool bInitialized = false;
if (!bInitialized)
{
bInitialized = true;
MeshToComponentMap.Empty();
}
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::CleanupGlobals()
{
for (TPair<USkeletalMesh*, FSkeletalMeshReferencers>& Pair : MeshToComponentMap)
{
if (Pair.Key && Pair.Value.RestoreBodySetup)
{
Pair.Key->SetBodySetup(Pair.Value.RestoreBodySetup);
}
}
MeshToComponentMap.Empty();
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::OnAdded()
{
checkf(SkeletalMeshComponent, TEXT("Invalid SkeletalMesh Component"));
checkf(ReferencedSkeletalMesh, TEXT("Invalid reference to Skeletal Mesh"));
checkf(ReferencedSkeletalMesh == SkeletalMeshComponent->GetSkeletalMesh(), TEXT("Referenced Skeletal Mesh does not match one in Component"));
FSkeletalMeshReferencers& SkeletalMeshReferencers = MeshToComponentMap.FindOrAdd(ReferencedSkeletalMesh);
checkf(!SkeletalMeshReferencers.Referencers.ContainsByPredicate(
[=](const FSkeletalMeshReferencers::FReferencersInfo& Info)
{
return Info.SkeletalMeshComponent == this->SkeletalMeshComponent;
}), TEXT("This Skeletal Mesh Component has already been Added"));
// If this is the first attempt to add a temporary body setup to the mesh, do it
if (SkeletalMeshReferencers.Referencers.Num() == 0)
{
// Remember the old body setup (this will be added as a GC reference so that it doesn't get destroyed)
const USkeletalMesh* ReferencedSkeletalMeshConst = ReferencedSkeletalMesh;
SkeletalMeshReferencers.RestoreBodySetup = ReferencedSkeletalMeshConst->GetBodySetup();
if (SkeletalMeshReferencers.RestoreBodySetup)
{
// Create a new body setup from the mesh's main body setup. This has to have the skeletal mesh as its outer,
// otherwise the body instance will not be created correctly.
UBodySetup* TempBodySetupRaw = DuplicateObject<UBodySetup>(ReferencedSkeletalMeshConst->GetBodySetup(), ReferencedSkeletalMesh);
TempBodySetupRaw->ClearFlags(RF_Transactional);
// Set collide all flag so that the body creates physics meshes using ALL elements from the mesh not just the collision mesh.
TempBodySetupRaw->bMeshCollideAll = true;
// This forces it to recreate the physics mesh.
TempBodySetupRaw->InvalidatePhysicsData();
// Force it to use high detail tri-mesh for collisions.
TempBodySetupRaw->CollisionTraceFlag = CTF_UseComplexAsSimple;
TempBodySetupRaw->AggGeom.ConvexElems.Empty();
// Set as new body setup
ReferencedSkeletalMesh->SetBodySetup(TempBodySetupRaw);
}
}
SkeletalMeshComponent->bUseRefPoseOnInitAnim = true;
SkeletalMeshComponent->InitAnim(true);
ECollisionEnabled::Type CachedCollisionType = SkeletalMeshComponent->BodyInstance.GetCollisionEnabled();
SkeletalMeshReferencers.Referencers.Emplace(SkeletalMeshComponent, CachedCollisionType);
// Force the collision type to not be 'NoCollision' without it the line trace will always fail.
if (CachedCollisionType == ECollisionEnabled::NoCollision)
{
SkeletalMeshComponent->BodyInstance.SetCollisionEnabled(ECollisionEnabled::QueryOnly, false);
}
// Set new physics state for the component
SkeletalMeshComponent->RecreatePhysicsState();
// Register callback for when the skeletal mesh is cached underneath us
ReferencedSkeletalMesh->OnPostMeshCached().AddRaw(this, &FMeshPaintGeometryAdapterForSkeletalMeshes::OnPostMeshCached);
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::OnRemoved()
{
// If the referenced skeletal mesh has been destroyed (and nulled by GC), don't try to do anything more.
// It should be in the process of removing all global geometry adapters if it gets here in this situation.
if (!ReferencedSkeletalMesh || !SkeletalMeshComponent)
{
return;
}
PropagateVertexPaintToAsset(ReferencedSkeletalMesh, MeshLODIndex);
// Remove a reference from the skeletal mesh map
FSkeletalMeshReferencers* SkeletalMeshReferencers = MeshToComponentMap.Find(ReferencedSkeletalMesh);
checkf(SkeletalMeshReferencers, TEXT("Could not find Reference to Skeletal Mesh"));
checkf(SkeletalMeshReferencers->Referencers.Num() > 0, TEXT("Skeletal Mesh does not have any referencers"));
const int32 Index = SkeletalMeshReferencers->Referencers.IndexOfByPredicate(
[=](const FSkeletalMeshReferencers::FReferencersInfo& Info)
{
return Info.SkeletalMeshComponent == this->SkeletalMeshComponent;
}
);
check(Index != INDEX_NONE);
SkeletalMeshComponent->bUseRefPoseOnInitAnim = false;
SkeletalMeshComponent->InitAnim(true);
SkeletalMeshComponent->BodyInstance.SetCollisionEnabled(SkeletalMeshReferencers->Referencers[Index].CachedCollisionType, false);
SkeletalMeshComponent->RecreatePhysicsState();
SkeletalMeshReferencers->Referencers.RemoveAtSwap(Index);
// If the last reference was removed, restore the body setup for the static mesh
if (SkeletalMeshReferencers->Referencers.Num() == 0)
{
if (SkeletalMeshReferencers->RestoreBodySetup != nullptr)
{
ReferencedSkeletalMesh->SetBodySetup(SkeletalMeshReferencers->RestoreBodySetup);
}
verify(MeshToComponentMap.Remove(ReferencedSkeletalMesh) == 1);
}
ReferencedSkeletalMesh->OnPostMeshCached().RemoveAll(this);
}
bool LegacySegmentTriangleIntersection(const FVector& StartPoint, const FVector& EndPoint, const FVector& A, const FVector& B, const FVector& C, FVector& OutIntersectPoint, FVector& OutTriangleNormal)
{
const FVector BA = A - B;
const FVector CB = B - C;
const FVector TriNormal = BA ^ CB;
bool bCollide = FMath::SegmentPlaneIntersection(StartPoint, EndPoint, FPlane(A, TriNormal), OutIntersectPoint);
if (!bCollide)
{
return false;
}
FVector BaryCentric = FMath::ComputeBaryCentric2D(OutIntersectPoint, A, B, C);
if (BaryCentric.X > 0.0f && BaryCentric.Y > 0.0f && BaryCentric.Z > 0.0f)
{
OutTriangleNormal = TriNormal;
return true;
}
return false;
}
bool FMeshPaintGeometryAdapterForSkeletalMeshes::LineTraceComponent(struct FHitResult& OutHit, const FVector Start, const FVector End, const struct FCollisionQueryParams& Params) const
{
const bool bHitBounds = FMath::LineSphereIntersection(Start, End.GetSafeNormal(), (End - Start).SizeSquared(), SkeletalMeshComponent->Bounds.Origin, SkeletalMeshComponent->Bounds.SphereRadius);
const float SqrRadius = FMath::Square(SkeletalMeshComponent->Bounds.SphereRadius);
const bool bInsideBounds = (SkeletalMeshComponent->Bounds.ComputeSquaredDistanceFromBoxToPoint(Start) <= SqrRadius) || (SkeletalMeshComponent->Bounds.ComputeSquaredDistanceFromBoxToPoint(End) <= SqrRadius);
const bool bHitPhysicsBodies = SkeletalMeshComponent->LineTraceComponent(OutHit, Start, End, Params);
bool bHitTriangle = false;
if ((bHitBounds || bInsideBounds) && !bHitPhysicsBodies)
{
const int32 NumTriangles = MeshIndices.Num() / 3;
const FTransform& ComponentTransform = SkeletalMeshComponent->GetComponentTransform();
const FTransform InverseComponentTransform = ComponentTransform.Inverse();
const FVector LocalStart = InverseComponentTransform.TransformPosition(Start);
const FVector LocalEnd = InverseComponentTransform.TransformPosition(End);
float MinDistance = FLT_MAX;
FVector Intersect;
FVector Normal;
for (int32 TriangleIndex = 0; TriangleIndex < NumTriangles; ++TriangleIndex)
{
// Compute the normal of the triangle
const FVector& P0 = MeshVertices[MeshIndices[(TriangleIndex * 3) + 0]];
const FVector& P1 = MeshVertices[MeshIndices[(TriangleIndex * 3) + 1]];
const FVector& P2 = MeshVertices[MeshIndices[(TriangleIndex * 3) + 2]];
const FVector TriNorm = (P1 - P0) ^ (P2 - P0);
//check collinearity of A,B,C
if (TriNorm.SizeSquared() > SMALL_NUMBER)
{
FVector IntersectPoint;
FVector HitNormal;
bool bHit = LegacySegmentTriangleIntersection(LocalStart, LocalEnd, P0, P1, P2, IntersectPoint, HitNormal);
if (bHit)
{
const float Distance = (LocalStart - IntersectPoint).SizeSquared();
if (Distance < MinDistance)
{
MinDistance = Distance;
Intersect = IntersectPoint;
Normal = HitNormal;
}
}
}
}
if (MinDistance != FLT_MAX)
{
OutHit.Component = SkeletalMeshComponent;
OutHit.Normal = Normal.GetSafeNormal();
OutHit.Location = ComponentTransform.TransformPosition(Intersect);
OutHit.bBlockingHit = true;
bHitTriangle = true;
}
}
return bHitPhysicsBodies || bHitTriangle;
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::QueryPaintableTextures(int32 MaterialIndex, int32& OutDefaultIndex, TArray<struct FPaintableTexture>& InOutTextureList)
{
DefaultQueryPaintableTextures(MaterialIndex, SkeletalMeshComponent, OutDefaultIndex, InOutTextureList);
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::ApplyOrRemoveTextureOverride(UTexture* SourceTexture, UTexture* OverrideTexture) const
{
DefaultApplyOrRemoveTextureOverride(SkeletalMeshComponent, SourceTexture, OverrideTexture);
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::AddReferencedObjectsGlobals(FReferenceCollector& Collector)
{
for (TPair<USkeletalMesh*, FSkeletalMeshReferencers>& Pair : MeshToComponentMap)
{
Collector.AddReferencedObject(Pair.Key);
Collector.AddReferencedObject(Pair.Value.RestoreBodySetup);
for (FSkeletalMeshReferencers::FReferencersInfo& ReferencerInfo : Pair.Value.Referencers)
{
Collector.AddReferencedObject(ReferencerInfo.SkeletalMeshComponent);
}
}
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::AddReferencedObjects(FReferenceCollector& Collector)
{
Collector.AddReferencedObject(ReferencedSkeletalMesh);
Collector.AddReferencedObject(SkeletalMeshComponent);
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::GetTextureCoordinate(int32 VertexIndex, int32 ChannelIndex, FVector2D& OutTextureCoordinate) const
{
OutTextureCoordinate = FVector2D(LODData->StaticVertexBuffers.StaticMeshVertexBuffer.GetVertexUV(VertexIndex, ChannelIndex));
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::PreEdit()
{
FlushRenderingCommands();
SkeletalMeshComponent->Modify();
ReferencedSkeletalMesh->SetFlags(RF_Transactional);
ReferencedSkeletalMesh->Modify();
ReferencedSkeletalMesh->SetHasVertexColors(true);
ReferencedSkeletalMesh->SetVertexColorGuid(FGuid::NewGuid());
// Release the static mesh's resources.
ReferencedSkeletalMesh->ReleaseResources();
// Flush the resource release commands to the rendering thread to ensure that the build doesn't occur while a resource is still
// allocated, and potentially accessing the UStaticMesh.
ReferencedSkeletalMesh->ReleaseResourcesFence.Wait();
if (LODData->StaticVertexBuffers.ColorVertexBuffer.GetNumVertices() == 0)
{
// Mesh doesn't have a color vertex buffer yet! We'll create one now.
LODData->StaticVertexBuffers.ColorVertexBuffer.InitFromSingleColor(FColor(255, 255, 255, 255), LODData->GetNumVertices());
ReferencedSkeletalMesh->SetHasVertexColors(true);
ReferencedSkeletalMesh->SetVertexColorGuid(FGuid::NewGuid());
BeginInitResource(&LODData->StaticVertexBuffers.ColorVertexBuffer);
}
//Make sure we change the import data so the re-import do not replace the new data
if (ReferencedSkeletalMesh->GetAssetImportData())
{
UFbxSkeletalMeshImportData* ImportData = Cast<UFbxSkeletalMeshImportData>(ReferencedSkeletalMesh->GetAssetImportData());
if (ImportData && ImportData->VertexColorImportOption != EVertexColorImportOption::Ignore)
{
ImportData->SetFlags(RF_Transactional);
ImportData->Modify();
ImportData->VertexColorImportOption = EVertexColorImportOption::Ignore;
}
}
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::PostEdit()
{
TUniquePtr< FSkinnedMeshComponentRecreateRenderStateContext > RecreateRenderStateContext = MakeUnique<FSkinnedMeshComponentRecreateRenderStateContext>(ReferencedSkeletalMesh);
ReferencedSkeletalMesh->InitResources();
ReferencedSkeletalMesh->GetOnMeshChanged().Broadcast();
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::GetVertexColor(int32 VertexIndex, FColor& OutColor, bool bInstance /*= true*/) const
{
if (LODData->StaticVertexBuffers.ColorVertexBuffer.GetNumVertices() > 0)
{
check((int32)LODData->StaticVertexBuffers.ColorVertexBuffer.GetNumVertices() > VertexIndex);
OutColor = LODData->StaticVertexBuffers.ColorVertexBuffer.VertexColor(VertexIndex);
}
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::SetVertexColor(int32 VertexIndex, FColor Color, bool bInstance /*= true*/)
{
if (LODData->StaticVertexBuffers.ColorVertexBuffer.GetNumVertices() > 0)
{
LODData->StaticVertexBuffers.ColorVertexBuffer.VertexColor(VertexIndex) = Color;
int32 SectionIndex = INDEX_NONE;
int32 SectionVertexIndex = INDEX_NONE;
LODModel->GetSectionFromVertexIndex(VertexIndex, SectionIndex, SectionVertexIndex);
LODModel->Sections[SectionIndex].SoftVertices[SectionVertexIndex].Color = Color;
if (!ReferencedSkeletalMesh->GetLODInfo(MeshLODIndex)->bHasPerLODVertexColors)
{
ReferencedSkeletalMesh->GetLODInfo(MeshLODIndex)->bHasPerLODVertexColors = true;
}
}
}
FMatrix FMeshPaintGeometryAdapterForSkeletalMeshes::GetComponentToWorldMatrix() const
{
return SkeletalMeshComponent->GetComponentToWorld().ToMatrixWithScale();
}
//////////////////////////////////////////////////////////////////////////
// FMeshPaintGeometryAdapterForSkeletalMeshesFactory
TSharedPtr<IMeshPaintGeometryAdapter> FMeshPaintGeometryAdapterForSkeletalMeshesFactory::Construct(class UMeshComponent* InComponent, int32 InMeshLODIndex) const
{
if (USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(InComponent))
{
if (SkeletalMeshComponent->GetSkeletalMesh() != nullptr)
{
TSharedRef<FMeshPaintGeometryAdapterForSkeletalMeshes> Result = MakeShareable(new FMeshPaintGeometryAdapterForSkeletalMeshes());
if (Result->Construct(InComponent, InMeshLODIndex))
{
return Result;
}
}
}
return nullptr;
}