Files
UnrealEngineUWP/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Private/GeometryCollection/GeometryCollectionDebugDrawComponent.cpp
Michael Lentine ea5ca985d4 Copying //UE4/Dev-Physics to //UE4/Dev-Main.
#rb none
#lockdown Nick.Penwarden

[CL 4653110 by Michael Lentine in Main branch]
2018-12-12 11:25:29 -05:00

495 lines
18 KiB
C++

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "GeometryCollection/GeometryCollectionDebugDrawComponent.h"
#if GEOMETRYCOLLECTION_DEBUG_DRAW
#include "GeometryCollection/GeometryCollectionRenderLevelSetActor.h"
#include "GeometryCollection/GeometryCollectionComponent.h"
#include "GeometryCollection/GeometryCollectionObject.h"
#include "GeometryCollection/GeometryCollectionAlgo.h"
#include "GeometryCollection/GeometryCollectionDebugDrawActor.h"
#include "GeometryCollection/GeometryCollectionPhysicsProxy.h"
#if INCLUDE_CHAOS
#include "PBDRigidsSolver.h"
#endif // #if INCLUDE_CHAOS
#endif // #if GEOMETRYCOLLECTION_DEBUG_DRAW
#include "EngineUtils.h"
#include "HAL/IConsoleManager.h"
DEFINE_LOG_CATEGORY_STATIC(UGCCDD_LOG, All, All);
// Constants
static const FLinearColor DarkerColorFactor(1.0f, 1.0f, 0.7f); // Darker HSV multiplier
static const FLinearColor LighterColorFactor(1.0f, 1.0f, 3.0f); // Lighter HSV multiplier
static const FLinearColor VertexColorDefault(0.2f, 0.4f, 0.6f, 1.0f); // Blue
static const FLinearColor FaceColorDefault(0.4f, 0.2f, 0.6f, 1.0f); // Purple
static const FLinearColor GeometryColorDefault(0.6, 0.4f, 0.2f, 1.0f); // Orange
static const FLinearColor BreakingColorDefault(0.4f, 0.6f, 0.2f, 1.0f); // Green
UGeometryCollectionDebugDrawComponent::UGeometryCollectionDebugDrawComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, GeometryCollectionDebugDrawActor(nullptr)
, GeometryCollectionRenderLevelSet(nullptr)
, bDebugDrawLevelSet(false)
, bRenderLevelSetAtOrigin(false)
, LevelSetIndex(0)
, bDebugDrawTransform(false)
, bDebugDrawTransformIndex(false)
, bDebugDrawBoundingBox(false)
, GeometryColor(GeometryColorDefault)
, bDebugDrawProximity(false)
, bDebugDrawBreakingFace(false)
, bDebugDrawBreakingRegionData(false)
, BreakingColor(BreakingColorDefault)
, bDebugDrawFace(false)
, bDebugDrawFaceIndex(false)
, bDebugDrawFaceNormal(false)
, bDebugDrawSingleFace(false)
, SingleFaceIdx(0)
, FaceColor(FaceColorDefault)
, bDebugDrawVertex(false)
, bDebugDrawVertexIndex(false)
, bDebugDrawVertexNormal(false)
, VertexColor(VertexColorDefault)
, GeometryCollectionComponent(nullptr)
, bLevelSetTextureDirty(false)
, LevelSetTextureTransformIndex(-1)
, BaseVisibilityArray()
{
PrimaryComponentTick.bCanEverTick = true;
bTickInEditor = false;
}
void UGeometryCollectionDebugDrawComponent::BeginPlay()
{
Super::BeginPlay();
DebugDrawBeginPlay();
DebugDrawLevelSetBeginPlay();
}
void UGeometryCollectionDebugDrawComponent::EndPlay(EEndPlayReason::Type ReasonEnd)
{
Super::EndPlay(ReasonEnd);
DebugDrawLevelSetEndPlay();
}
void UGeometryCollectionDebugDrawComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
DebugDrawTick();
DebugDrawLevelSetTick();
}
#if WITH_EDITOR
void UGeometryCollectionDebugDrawComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
#if GEOMETRYCOLLECTION_DEBUG_DRAW
if (GeometryCollectionComponent)
{
const FName PropertyName = PropertyChangedEvent.Property ? PropertyChangedEvent.Property->GetFName(): NAME_None;
if (PropertyName == GET_MEMBER_NAME_CHECKED(UGeometryCollectionDebugDrawComponent, SingleFaceIdx))
{
check(GeometryCollectionComponent->DynamicCollection != nullptr);
check(GeometryCollectionComponent->DynamicCollection->GetGeometryCollection().IsValid());
FGeometryCollection* const Collection = GeometryCollectionComponent->DynamicCollection->GetGeometryCollection().Get();
if (Collection)
{
const int32 NumFaces = Collection->NumElements(FGeometryCollection::FacesGroup);
SingleFaceIdx = FMath::Clamp(SingleFaceIdx, 0, NumFaces - 1);
}
}
}
#endif // #if GEOMETRYCOLLECTION_DEBUG_DRAW
}
#endif // #if WITH_EDITOR
void UGeometryCollectionDebugDrawComponent::DebugDrawLevelSetBeginPlay()
{
#if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
// Look out for existing render level set actor, or create one when needed
if (!GeometryCollectionRenderLevelSet)
{
UWorld* const world = GetWorld();
check(world);
const TActorIterator<AGeometryCollectionRenderLevelSetActor> ActorIterator(world);
if (ActorIterator)
{
GeometryCollectionRenderLevelSet = *ActorIterator;
}
else
{
FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
GeometryCollectionRenderLevelSet = world->SpawnActor<AGeometryCollectionRenderLevelSetActor>(SpawnInfo);
if (GeometryCollectionRenderLevelSet)
{
GeometryCollectionRenderLevelSet->SetActorEnableCollision(false);
}
}
}
if (GeometryCollectionComponent && GeometryCollectionComponent->DynamicCollection && GeometryCollectionRenderLevelSet)
{
// For now it makes sense to always initialize our visibility arrays since a user might start with vis debug off, then turn it on
// This logic could change in the future
TManagedArray<bool> &VisibleArray = *GeometryCollectionComponent->DynamicCollection->GetGeometryCollection()->Visible;
BaseVisibilityArray = GeometryCollectionComponent->DynamicCollection->GetGeometryCollection()->AddAttribute<bool>("BaseVisibility", FGeometryCollection::FacesGroup);
(*BaseVisibilityArray).Init(VisibleArray);
bLevelSetTextureDirty = true;
LevelSetTextureTransformIndex = -1;
}
#endif // #if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
}
void UGeometryCollectionDebugDrawComponent::DebugDrawLevelSetEndPlay()
{
#if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
if (GeometryCollectionComponent && GeometryCollectionRenderLevelSet)
{
// @note: it might be the case that the user checks and unchecks render level set multiple times, and when they exit it is off
// but we still want to reset the visibility. One solution is to always reset visibility at run time when the check box changes
if (bDebugDrawLevelSet) {
DebugDrawLevelSetResetVisiblity();
}
// turn off rendering
GeometryCollectionRenderLevelSet->SetEnabled(false);
bLevelSetTextureDirty = true;
LevelSetTextureTransformIndex = -1;
}
#endif // #if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
}
void UGeometryCollectionDebugDrawComponent::DebugDrawLevelSetResetVisiblity()
{
#if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
check(GeometryCollectionComponent != nullptr);
check(GeometryCollectionComponent->DynamicCollection != nullptr);
check(GeometryCollectionComponent->DynamicCollection->GetGeometryCollection().IsValid());
check(BaseVisibilityArray.IsValid());
// reset visibility array
TManagedArray<bool> &VisibleArray = *GeometryCollectionComponent->DynamicCollection->GetGeometryCollection()->Visible;
VisibleArray.Init(*BaseVisibilityArray);
// if we only have one piece, and all of the faces were hidden, then ensure we set visibility true
if (!GeometryCollectionComponent->IsVisible()) {
GeometryCollectionComponent->SetVisibility(true);
}
else
{
GeometryCollectionComponent->ForceInitRenderData();
}
#endif // #if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
}
void UGeometryCollectionDebugDrawComponent::DebugDrawLevelSetTick()
{
#if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
if (!bDebugDrawLevelSet)
{
if (LevelSetTextureTransformIndex != -1 && GeometryCollectionRenderLevelSet && BaseVisibilityArray)
{
// in this case, the user switched from debug draw to not at run time, reset visibility
DebugDrawLevelSetResetVisiblity();
// turn off rendering
GeometryCollectionRenderLevelSet->SetEnabled(false);
bLevelSetTextureDirty = true;
LevelSetTextureTransformIndex = -1;
}
}
else
{
// if the level set index has changed at run time, then reload the volume
// because someone wants to visualize another piece
if (LevelSetTextureTransformIndex != -1 && LevelSetTextureTransformIndex != LevelSetIndex) {
bLevelSetTextureDirty = true;
}
// Error case if the level set renderer, physics proxy, or solver are null
if (!GeometryCollectionRenderLevelSet) {
UE_LOG(UGCCDD_LOG, Warning, TEXT("level set renderer: %s"), *GetFullName());
return;
}
if (!GeometryCollectionComponent) {
UE_LOG(UGCCDD_LOG, Warning, TEXT("no geometry component: %s"), *GetFullName());
return;
}
FGeometryCollectionPhysicsProxy *PhysicsProxy = GeometryCollectionComponent->GetPhysicsProxy();
if (!GeometryCollectionRenderLevelSet || !PhysicsProxy || !PhysicsProxy->GetSolver()) {
UE_LOG(UGCCDD_LOG, Warning, TEXT("No solver context: %s"), *GetFullName());
return;
}
// We must have at least one body to continue
const Chaos::PBDRigidsSolver::FParticlesType &Particles = PhysicsProxy->GetSolver()->GetRigidParticles();
if (Particles.Size() == 0) {
UE_LOG(UGCCDD_LOG, Warning, TEXT("No rbds in solver context: %s"), *GetFullName());
return;
}
// Map the piece index to the rbd index to extract the level set
if (GeometryCollectionComponent->RigidBodyIds.Num() == 0) {
UE_LOG(UGCCDD_LOG, Warning, TEXT("No rbd ids synced: %s"), *GetFullName());
return;
}
// Make sure we have a valid tranform index
if (LevelSetIndex < 0 || LevelSetIndex > GeometryCollectionComponent->RigidBodyIds.Num() - 1) {
UE_LOG(UGCCDD_LOG, Warning, TEXT("Invalid level set index: %s"), *GetFullName());
return;
}
// Make sure we have a valid rbd index
int32 RbdId = GeometryCollectionComponent->RigidBodyIds[LevelSetIndex];
if (RbdId < 0) {
UE_LOG(UGCCDD_LOG, Warning, TEXT("No rbd ids synced: %s"), *GetFullName());
return;
}
Chaos::TImplicitObject<float, 3>* CollisionBase = Particles.Geometry(RbdId);
// Make sure the actual implicit object isn't null
if (CollisionBase == NULL) {
UE_LOG(UGCCDD_LOG, Warning, TEXT("Collision is null for level set visualization: %s"), *GetFullName());
return;
}
// Cast to level set, make sure the type is correct
Chaos::TLevelSet<float, 3>* CollisionLevelSet = CollisionBase->GetObject< Chaos::TLevelSet<float, 3> >();
if (CollisionLevelSet == NULL) {
UE_LOG(UGCCDD_LOG, Warning, TEXT("Incorrect collision type for level set rendering. It must be a level set: %s"), *GetFullName());
return;
}
// Get the transform for the current piece
FTransform CurrTransform = FTransform::Identity;
UGeometryCollection *DynamicCollection = GeometryCollectionComponent->DynamicCollection;
// Update the transform if we are rendering the level set aligned with the simulated geometry
if (!bRenderLevelSetAtOrigin) {
//TManagedArray<FTransform> &TransformArray = DynamicCollection->GetGeometryCollection()->GetAttribute<FTransform>("Transform", FGeometryCollection::TransformGroup).Get();
//CurrTransform = TransformArray[LevelSetIndex];
// @todo: this is slow to recompute the global matrices here. Ideally we'd grab some cached ones from the geom collection component
TArray<FTransform> GlobalMatrices;
GeometryCollectionAlgo::GlobalMatrices(DynamicCollection->GetGeometryCollection().Get(), GlobalMatrices);
CurrTransform = GlobalMatrices[LevelSetIndex];
CurrTransform *= this->GetOwner()->GetTransform();
}
// If we are only updating the transform, or also loading the volume
if (!bLevelSetTextureDirty) {
GeometryCollectionRenderLevelSet->SyncLevelSetTransform(CurrTransform);
}
else {
// Build the volume texture
// @note: we only want to do this once, so we have a state variable on the component to ensure that
bool success = GeometryCollectionRenderLevelSet->SetLevelSetToRender(*CollisionLevelSet, CurrTransform);
// Error case if volume fill didn't work
if (!success) {
UE_LOG(UGCCDD_LOG, Warning, TEXT("Levelset generation failed: %s"), *GetFullName());
return;
}
// hide the original piece
TManagedArray<bool> &VisibleArray = *DynamicCollection->GetGeometryCollection()->Visible;
// reset visibility to original state
// @todo: there is some logic missing here, but also GeometryCollectionComponent doesn't like
// debug rendering flags being set in simulate mode, so we don't switch piece often right now
if (LevelSetTextureTransformIndex != -1) {
VisibleArray.Init(*BaseVisibilityArray);
}
const TManagedArray<int32> &TransformIndexArray = *DynamicCollection->GetGeometryCollection()->TransformIndex;
const TManagedArray<int32> &FaceStartArray = *DynamicCollection->GetGeometryCollection()->FaceStart;
const TManagedArray<int32> &FaceCountArray = *DynamicCollection->GetGeometryCollection()->FaceCount;
const TManagedArray<FIntVector> &Indices = *DynamicCollection->GetGeometryCollection()->Indices;
const TManagedArray<int32>& MaterialIndex = *DynamicCollection->GetGeometryCollection()->MaterialIndex;
// for each geom, check if it is the transform object
// if it is, hide all the faces
int NumHid = 0;
for (int i = 0; i < TransformIndexArray.Num(); ++i) {
const int32 currT = TransformIndexArray[i];
if (currT == LevelSetIndex) {
int32 FaceStart = FaceStartArray[i];
int32 FaceCount = FaceCountArray[i];
// set visibility on faces to false
for (int j = FaceStart; j < FaceStart + FaceCount; ++j) {
VisibleArray[j] = false;
}
NumHid = FaceCount;
}
}
// if we have no visible faces, hide the geometry without changing the collection
// #todo: right now we can't send zero vertices to force the vertex buffer to be empty,
// so we just hide the component.
if (NumHid == VisibleArray.Num())
{
GeometryCollectionComponent->SetVisibility(false);
}
else
{
// init all render data
GeometryCollectionComponent->ForceInitRenderData();
}
// Make sure we know not to refill the texture on subsequent frames
bLevelSetTextureDirty = false;
LevelSetTextureTransformIndex = LevelSetIndex;
// Turn on the volume rendering
GeometryCollectionRenderLevelSet->SetEnabled(true);
}
}
#endif // #if INCLUDE_CHAOS && GEOMETRYCOLLECTION_DEBUG_DRAW
}
void UGeometryCollectionDebugDrawComponent::DebugDrawBeginPlay()
{
#if GEOMETRYCOLLECTION_DEBUG_DRAW
if (!GeometryCollectionDebugDrawActor)
{
// Look out for existing debug draw actor, or create one when needed
UWorld* const world = GetWorld();
check(world);
const TActorIterator<AGeometryCollectionDebugDrawActor> ActorIterator(world);
if (ActorIterator)
{
GeometryCollectionDebugDrawActor = *ActorIterator;
}
else
{
FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
GeometryCollectionDebugDrawActor = world->SpawnActor<AGeometryCollectionDebugDrawActor>(SpawnInfo);
if (GeometryCollectionDebugDrawActor)
{
GeometryCollectionDebugDrawActor->SetActorEnableCollision(false);
}
}
}
// Make sure to tick the debug draw first, it is required to clear up the persistent lines before drawing a new frame
if (GeometryCollectionDebugDrawActor)
{
AActor* const actor = GetOwner();
check(actor);
GeometryCollectionDebugDrawActor->AddTickPrerequisiteActor(actor);
}
#endif // #if GEOMETRYCOLLECTION_DEBUG_DRAW
}
void UGeometryCollectionDebugDrawComponent::DebugDrawTick()
{
#if GEOMETRYCOLLECTION_DEBUG_DRAW
check(GeometryCollectionDebugDrawActor);
// Only draw when a GeometryCollectionComponent is also attached to the actor (GeometryCollectionComponent is set by AGeometryCollectionActor::AGeometryCollectionActor())
if (!GeometryCollectionComponent)
{
UE_LOG(UGCCDD_LOG, Warning, TEXT("Null geometry component pointer: %s"), *GetFullName());
return;
}
if (!GeometryCollectionComponent->DynamicCollection)
{
UE_LOG(UGCCDD_LOG, Warning, TEXT("Null geometry dynamic collection pointer: %s"), *GetFullName());
return;
}
if (!GeometryCollectionComponent->DynamicCollection->GetGeometryCollection().IsValid())
{
UE_LOG(UGCCDD_LOG, Warning, TEXT("No valid geometry collection: %s"), *GetFullName());
return;
}
// Draw collection
FGeometryCollection* const Collection = GeometryCollectionComponent->DynamicCollection->GetGeometryCollection().Get();
check(Collection);
AActor* const Actor = GetOwner();
check(Actor);
if (bDebugDrawVertex)
{
const FColor Color = VertexColor.ToFColor(false);
GeometryCollectionDebugDrawActor->DrawVertices(Collection, Actor, Color);
}
if (bDebugDrawVertexIndex)
{
const FColor Color = (VertexColor.LinearRGBToHSV() * LighterColorFactor).HSVToLinearRGB().ToFColor(false);
GeometryCollectionDebugDrawActor->DrawVertexIndices(Collection, Actor, Color);
}
if (bDebugDrawVertexNormal)
{
const FColor Color = (VertexColor.LinearRGBToHSV() * DarkerColorFactor).HSVToLinearRGB().ToFColor(false);
GeometryCollectionDebugDrawActor->DrawVertexNormals(Collection, Actor, Color);
}
if (bDebugDrawFace)
{
const FColor Color = FaceColor.ToFColor(false);
GeometryCollectionDebugDrawActor->DrawFaces(Collection, Actor, Color);
}
if (bDebugDrawFaceIndex)
{
const FColor Color = (FaceColor.LinearRGBToHSV() * LighterColorFactor).HSVToLinearRGB().ToFColor(false);
GeometryCollectionDebugDrawActor->DrawFaceIndices(Collection, Actor, Color);
}
if (bDebugDrawSingleFace)
{
const FColor Color = (FaceColor.LinearRGBToHSV() * LighterColorFactor).HSVToLinearRGB().ToFColor(false);
GeometryCollectionDebugDrawActor->DrawSingleFace(Collection, Actor, SingleFaceIdx, Color);
}
if (bDebugDrawFaceNormal)
{
const FColor Color = (FaceColor.LinearRGBToHSV() * DarkerColorFactor).HSVToLinearRGB().ToFColor(false);
GeometryCollectionDebugDrawActor->DrawFaceNormals(Collection, Actor, Color);
}
if (bDebugDrawTransform)
{
GeometryCollectionDebugDrawActor->DrawTransforms(Collection, Actor);
}
if (bDebugDrawTransformIndex)
{
const FColor Color = (GeometryColor.LinearRGBToHSV() * LighterColorFactor).HSVToLinearRGB().ToFColor(false);
GeometryCollectionDebugDrawActor->DrawTransformIndices(Collection, Actor, Color);
}
if (bDebugDrawBoundingBox)
{
const FColor Color = GeometryColor.ToFColor(false);
GeometryCollectionDebugDrawActor->DrawBoundingBoxes(Collection, Actor, Color);
}
if (bDebugDrawProximity)
{
const FColor Color = BreakingColor.ToFColor(false);
GeometryCollectionDebugDrawActor->DrawProximity(Collection, Actor, Color);
}
if (bDebugDrawBreakingFace)
{
const FColor Color = (BreakingColor.LinearRGBToHSV() * LighterColorFactor).HSVToLinearRGB().ToFColor(false);
GeometryCollectionDebugDrawActor->DrawBreakingFaces(Collection, Actor, Color);
}
if (bDebugDrawBreakingRegionData)
{
const FColor Color = (BreakingColor.LinearRGBToHSV() * DarkerColorFactor).HSVToLinearRGB().ToFColor(false);
GeometryCollectionDebugDrawActor->DrawBreakingRegionData(Collection, Actor, Color);
}
#endif // #if GEOMETRYCOLLECTION_DEBUG_DRAW
}