// Copyright 1998-2019 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 ActorIterator(world); if (ActorIterator) { GeometryCollectionRenderLevelSet = *ActorIterator; } else { FActorSpawnParameters SpawnInfo; SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; GeometryCollectionRenderLevelSet = world->SpawnActor(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 &VisibleArray = *GeometryCollectionComponent->DynamicCollection->GetGeometryCollection()->Visible; BaseVisibilityArray = GeometryCollectionComponent->DynamicCollection->GetGeometryCollection()->AddAttribute("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 &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* 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* CollisionLevelSet = CollisionBase->GetObject< Chaos::TLevelSet >(); 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 &TransformArray = DynamicCollection->GetGeometryCollection()->GetAttribute("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 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 &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 &TransformIndexArray = *DynamicCollection->GetGeometryCollection()->TransformIndex; const TManagedArray &FaceStartArray = *DynamicCollection->GetGeometryCollection()->FaceStart; const TManagedArray &FaceCountArray = *DynamicCollection->GetGeometryCollection()->FaceCount; const TManagedArray &Indices = *DynamicCollection->GetGeometryCollection()->Indices; const TManagedArray& 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 ActorIterator(world); if (ActorIterator) { GeometryCollectionDebugDrawActor = *ActorIterator; } else { FActorSpawnParameters SpawnInfo; SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; GeometryCollectionDebugDrawActor = world->SpawnActor(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 }