Files
UnrealEngineUWP/Engine/Source/Editor/MeshPaint/Private/MeshPaintSkeletalMeshAdapter.cpp
Ryan Vance 7c51ff94af Merging //UE4/Dev-Main to Dev-VR (//UE4/Dev-VR)
CL 1 of 8
#rb integration

[CL 4748712 by Ryan Vance in Dev-VR branch]
2019-01-17 18:54:05 -05:00

412 lines
16 KiB
C++

// Copyright 1998-2019 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"
//////////////////////////////////////////////////////////////////////////
// FMeshPaintGeometryAdapterForSkeletalMeshes
FMeshPaintGeometryAdapterForSkeletalMeshes::FMeshToComponentMap FMeshPaintGeometryAdapterForSkeletalMeshes::MeshToComponentMap;
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->SkeletalMesh != nullptr)
{
ReferencedSkeletalMesh = SkeletalMeshComponent->SkeletalMesh;
MeshLODIndex = InMeshLODIndex;
const bool bSuccess = Initialize();
return bSuccess;
}
}
return false;
}
FMeshPaintGeometryAdapterForSkeletalMeshes::~FMeshPaintGeometryAdapterForSkeletalMeshes()
{
if (SkeletalMeshComponent != nullptr)
{
SkeletalMeshComponent->UnregisterOnSkeletalMeshPropertyChanged(SkeletalMeshChangedHandle);
}
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::OnSkeletalMeshChanged()
{
OnRemoved();
ReferencedSkeletalMesh = SkeletalMeshComponent->SkeletalMesh;
if (SkeletalMeshComponent->SkeletalMesh != nullptr)
{
Initialize();
OnAdded();
}
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::OnPostMeshCached(USkeletalMesh* SkeletalMesh)
{
if (ReferencedSkeletalMesh == SkeletalMesh)
{
OnSkeletalMeshChanged();
}
}
bool FMeshPaintGeometryAdapterForSkeletalMeshes::Initialize()
{
check(ReferencedSkeletalMesh == SkeletalMeshComponent->SkeletalMesh);
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 FVector& Position = LODData->StaticVertexBuffers.PositionVertexBuffer.VertexPosition(Index);
MeshVertices[Index] = 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::OnAdded()
{
checkf(SkeletalMeshComponent, TEXT("Invalid SkeletalMesh Component"));
checkf(ReferencedSkeletalMesh, TEXT("Invalid reference to Skeletal Mesh"));
checkf(ReferencedSkeletalMesh == SkeletalMeshComponent->SkeletalMesh, 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)
SkeletalMeshReferencers.RestoreBodySetup = ReferencedSkeletalMesh->BodySetup;
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>(ReferencedSkeletalMesh->BodySetup, 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->BodySetup = 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()
{
checkf(SkeletalMeshComponent, TEXT("Invalid SkeletalMesh Component"));
// 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)
{
return;
}
// 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->BodySetup = SkeletalMeshReferencers->RestoreBodySetup;
}
verify(MeshToComponentMap.Remove(ReferencedSkeletalMesh) == 1);
}
ReferencedSkeletalMesh->OnPostMeshCached().RemoveAll(this);
}
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 = FMath::SegmentTriangleIntersection(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::AddReferencedObjects(FReferenceCollector& Collector)
{
if (!ReferencedSkeletalMesh)
{
return;
}
FSkeletalMeshReferencers* SkeletalMeshReferencers = MeshToComponentMap.Find(ReferencedSkeletalMesh);
checkf(SkeletalMeshReferencers, TEXT("No references found for Skeletal Mesh"));
if (SkeletalMeshReferencers->RestoreBodySetup != nullptr)
{
Collector.AddReferencedObject(SkeletalMeshReferencers->RestoreBodySetup);
}
for (auto& Info : SkeletalMeshReferencers->Referencers)
{
Collector.AddReferencedObject(Info.SkeletalMeshComponent);
}
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::GetTextureCoordinate(int32 VertexIndex, int32 ChannelIndex, FVector2D& OutTextureCoordinate) const
{
OutTextureCoordinate = LODData->StaticVertexBuffers.StaticMeshVertexBuffer.GetVertexUV(VertexIndex, ChannelIndex);
}
void FMeshPaintGeometryAdapterForSkeletalMeshes::PreEdit()
{
FlushRenderingCommands();
SkeletalMeshComponent->Modify();
ReferencedSkeletalMesh->SetFlags(RF_Transactional);
ReferencedSkeletalMesh->Modify();
ReferencedSkeletalMesh->bHasVertexColors = true;
ReferencedSkeletalMesh->VertexColorGuid = 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->bHasVertexColors = true;
ReferencedSkeletalMesh->VertexColorGuid = 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->AssetImportData)
{
UFbxSkeletalMeshImportData* ImportData = Cast<UFbxSkeletalMeshImportData>(ReferencedSkeletalMesh->AssetImportData);
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();
}
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->SkeletalMesh != nullptr)
{
TSharedRef<FMeshPaintGeometryAdapterForSkeletalMeshes> Result = MakeShareable(new FMeshPaintGeometryAdapterForSkeletalMeshes());
if (Result->Construct(InComponent, InMeshLODIndex))
{
return Result;
}
}
}
return nullptr;
}