2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "CollisionAnalyzerPCH.h"
# define LOCTEXT_NAMESPACE "SCAQueryDetails"
/** Util to give written explanation for why we missed something */
2015-01-07 09:52:40 -05:00
FText GetReasonForMiss ( const UPrimitiveComponent * MissedComp , const FCAQuery * Query )
2014-03-14 14:13:41 -04:00
{
if ( MissedComp ! = NULL & & Query ! = NULL )
{
if ( MissedComp - > GetOwner ( ) & & ! MissedComp - > GetOwner ( ) - > GetActorEnableCollision ( ) )
{
2015-01-07 09:52:40 -05:00
return FText : : Format ( LOCTEXT ( " MissReasonActorCollisionDisabledFmt " , " Owning Actor '{0}' has all collision disabled (SetActorEnableCollision) " ) , FText : : FromString ( MissedComp - > GetOwner ( ) - > GetName ( ) ) ) ;
2014-03-14 14:13:41 -04:00
}
if ( ! MissedComp - > IsCollisionEnabled ( ) )
{
2015-01-07 09:52:40 -05:00
return FText : : Format ( LOCTEXT ( " MissReasonComponentCollisionDisabledFmt " , " Component '{0}' has CollisionEnabled == NoCollision " ) , FText : : FromString ( MissedComp - > GetName ( ) ) ) ;
2014-03-14 14:13:41 -04:00
}
if ( MissedComp - > GetCollisionResponseToChannel ( Query - > Channel ) = = ECR_Ignore )
{
2015-01-07 09:52:40 -05:00
return FText : : Format ( LOCTEXT ( " MissReasonComponentIgnoresChannelFmt " , " Component '{0}' ignores this channel. " ) , FText : : FromString ( MissedComp - > GetName ( ) ) ) ;
2014-03-14 14:13:41 -04:00
}
if ( Query - > ResponseParams . CollisionResponse . GetResponse ( MissedComp - > GetCollisionObjectType ( ) ) = = ECR_Ignore )
{
2015-01-07 09:52:40 -05:00
return FText : : Format ( LOCTEXT ( " MissReasonQueryIgnoresComponentFmt " , " Query ignores Component '{0}' movement channel. " ) , FText : : FromString ( MissedComp - > GetName ( ) ) ) ;
2014-03-14 14:13:41 -04:00
}
}
2015-01-07 09:52:40 -05:00
return LOCTEXT ( " MissReasonUnknown " , " Unknown " ) ;
2014-03-14 14:13:41 -04:00
}
/** Implements a row widget for result list. */
class SHitResultRow : public SMultiColumnTableRow < TSharedPtr < FCAHitInfo > >
{
public :
SLATE_BEGIN_ARGS ( SHitResultRow ) { }
SLATE_ARGUMENT ( TSharedPtr < FCAHitInfo > , Info )
SLATE_ARGUMENT ( TSharedPtr < SCAQueryDetails > , OwnerDetailsPtr )
SLATE_END_ARGS ( )
public :
void Construct ( const FArguments & InArgs , const TSharedRef < STableViewBase > & InOwnerTableView )
{
Info = InArgs . _Info ;
OwnerDetailsPtr = InArgs . _OwnerDetailsPtr ;
SMultiColumnTableRow < TSharedPtr < FCAHitInfo > > : : Construct ( FSuperRowType : : FArguments ( ) , InOwnerTableView ) ;
}
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
2014-06-13 06:14:46 -04:00
virtual TSharedRef < SWidget > GenerateWidgetForColumn ( const FName & ColumnName ) override
2014-03-14 14:13:41 -04:00
{
// Get info to apply to all columns (color and tooltip)
FSlateColor ResultColor = FSlateColor : : UseForeground ( ) ;
2015-01-07 09:52:40 -05:00
FText TooltipText = FText : : GetEmpty ( ) ;
2014-03-14 14:13:41 -04:00
if ( Info - > bMiss )
{
ResultColor = FLinearColor ( 0.4f , 0.4f , 0.65f ) ;
2015-01-07 09:52:40 -05:00
TooltipText = FText : : Format ( LOCTEXT ( " MissToolTipFmt " , " Miss: {0} " ) , GetReasonForMiss ( Info - > Result . Component . Get ( ) , OwnerDetailsPtr . Pin ( ) - > GetCurrentQuery ( ) ) ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Info - > Result . bBlockingHit & & Info - > Result . bStartPenetrating )
{
ResultColor = FLinearColor ( 1.f , 0.25f , 0.25f ) ;
}
// Generate widget for column
if ( ColumnName = = TEXT ( " Time " ) )
{
2015-01-07 09:52:40 -05:00
static const FNumberFormattingOptions TimeNumberFormat = FNumberFormattingOptions ( )
. SetMinimumFractionalDigits ( 3 )
. SetMaximumFractionalDigits ( 3 ) ;
2014-03-14 14:13:41 -04:00
return SNew ( STextBlock )
. ColorAndOpacity ( ResultColor )
2015-01-07 09:52:40 -05:00
. ToolTipText ( TooltipText )
. Text ( FText : : AsNumber ( Info - > Result . Time , & TimeNumberFormat ) ) ;
2014-03-14 14:13:41 -04:00
}
else if ( ColumnName = = TEXT ( " Type " ) )
{
2015-01-07 09:52:40 -05:00
FText TypeText = FText : : GetEmpty ( ) ;
2014-03-14 14:13:41 -04:00
if ( Info - > bMiss )
{
2015-01-07 09:52:40 -05:00
TypeText = LOCTEXT ( " MissLabel " , " Miss " ) ;
2014-03-14 14:13:41 -04:00
}
else if ( Info - > Result . bBlockingHit )
{
2015-01-07 09:52:40 -05:00
TypeText = LOCTEXT ( " BlockLabel " , " Block " ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2015-01-07 09:52:40 -05:00
TypeText = LOCTEXT ( " TouchLabel " , " Touch " ) ;
2014-03-14 14:13:41 -04:00
}
return SNew ( STextBlock )
. ColorAndOpacity ( ResultColor )
2015-01-07 09:52:40 -05:00
. ToolTipText ( TooltipText )
. Text ( TypeText ) ;
2014-03-14 14:13:41 -04:00
}
else if ( ColumnName = = TEXT ( " Component " ) )
{
2015-01-07 09:52:40 -05:00
FText LongName = LOCTEXT ( " InvalidLabel " , " Invalid " ) ;
2014-03-14 14:13:41 -04:00
if ( Info - > Result . Component . IsValid ( ) )
{
2015-01-07 09:52:40 -05:00
LongName = FText : : FromString ( Info - > Result . Component . Get ( ) - > GetReadableName ( ) ) ;
2014-03-14 14:13:41 -04:00
}
return SNew ( STextBlock )
. ColorAndOpacity ( ResultColor )
2015-01-07 09:52:40 -05:00
. ToolTipText ( TooltipText )
2014-03-14 14:13:41 -04:00
. Text ( LongName ) ;
}
else if ( ColumnName = = TEXT ( " Normal " ) )
{
return SNew ( STextBlock )
. ColorAndOpacity ( ResultColor )
2015-01-07 09:52:40 -05:00
. ToolTipText ( TooltipText )
. Text ( FText : : FromString ( Info - > Result . Normal . ToString ( ) ) ) ;
2014-03-14 14:13:41 -04:00
}
return SNullWidget : : NullWidget ;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
private :
/** Result to display */
TSharedPtr < FCAHitInfo > Info ;
/** Show details of */
TWeakPtr < SCAQueryDetails > OwnerDetailsPtr ;
} ;
//////////////////////////////////////////////////////////////////////////
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SCAQueryDetails : : Construct ( const FArguments & InArgs , TSharedPtr < SCollisionAnalyzer > OwningAnalyzerWidget )
{
bDisplayQuery = false ;
bShowMisses = false ;
OwningAnalyzerWidgetPtr = OwningAnalyzerWidget ;
ChildSlot
[
SNew ( SVerticalBox )
// Top area is info on the trace
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
[
SNew ( SBorder )
. BorderImage ( FEditorStyle : : GetBrush ( " ToolBar.Background " ) )
[
SNew ( SHorizontalBox )
// Left is start/end locations
+ SHorizontalBox : : Slot ( )
. FillWidth ( 1 )
[
SNew ( SGridPanel )
+ SGridPanel : : Slot ( 0 , 0 )
. Padding ( 2 )
[
SNew ( STextBlock )
2014-04-23 18:06:41 -04:00
. Text ( LOCTEXT ( " QueryStart " , " Start: " ) )
2014-03-14 14:13:41 -04:00
]
+ SGridPanel : : Slot ( 1 , 0 )
. Padding ( 2 )
[
SNew ( STextBlock )
2014-04-23 18:06:41 -04:00
. Text ( this , & SCAQueryDetails : : GetStartText )
2014-03-14 14:13:41 -04:00
]
+ SGridPanel : : Slot ( 0 , 1 )
. Padding ( 2 )
[
SNew ( STextBlock )
2014-04-23 18:06:41 -04:00
. Text ( LOCTEXT ( " QueryEnd " , " End: " ) )
2014-03-14 14:13:41 -04:00
]
+ SGridPanel : : Slot ( 1 , 1 )
. Padding ( 2 )
[
SNew ( STextBlock )
2014-04-23 18:06:41 -04:00
. Text ( this , & SCAQueryDetails : : GetEndText )
2014-03-14 14:13:41 -04:00
]
]
// Right has controls
+ SHorizontalBox : : Slot ( )
. FillWidth ( 1 )
. VAlign ( VAlign_Top )
. Padding ( 4 , 0 )
[
SNew ( SCheckBox )
. OnCheckStateChanged ( this , & SCAQueryDetails : : OnToggleShowMisses )
. Content ( )
[
SNew ( STextBlock )
2014-04-23 18:06:41 -04:00
. Text ( LOCTEXT ( " ShowMisses " , " Show Misses " ) )
2014-03-14 14:13:41 -04:00
]
]
]
]
// Bottom area is list of hits
+ SVerticalBox : : Slot ( )
. FillHeight ( 1 )
[
SNew ( SBorder )
. BorderImage ( FEditorStyle : : GetBrush ( " Menu.Background " ) )
. Padding ( 1.0 )
[
SAssignNew ( ResultListWidget , SListView < TSharedPtr < FCAHitInfo > > )
. ItemHeight ( 20 )
. ListItemsSource ( & ResultList )
. SelectionMode ( ESelectionMode : : Single )
. OnSelectionChanged ( this , & SCAQueryDetails : : ResultListSelectionChanged )
. OnGenerateRow ( this , & SCAQueryDetails : : ResultListGenerateRow )
. HeaderRow (
SNew ( SHeaderRow )
2014-04-23 18:06:41 -04:00
+ SHeaderRow : : Column ( " Time " ) . DefaultLabel ( LOCTEXT ( " ResultListTimeHeader " , " Time " ) ) . FillWidth ( 0.7 )
+ SHeaderRow : : Column ( " Type " ) . DefaultLabel ( LOCTEXT ( " ResultListTypeHeader " , " Type " ) ) . FillWidth ( 0.7 )
+ SHeaderRow : : Column ( " Component " ) . DefaultLabel ( LOCTEXT ( " ResultListComponentHeader " , " Component " ) ) . FillWidth ( 3 )
+ SHeaderRow : : Column ( " Normal " ) . DefaultLabel ( LOCTEXT ( " ResultListNormalHeader " , " Normal " ) ) . FillWidth ( 1.8 )
2014-03-14 14:13:41 -04:00
)
]
]
] ;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
2014-04-23 18:06:41 -04:00
FText SCAQueryDetails : : GetStartText ( ) const
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:06:41 -04:00
return bDisplayQuery ? CurrentQuery . Start . ToText ( ) : FText : : GetEmpty ( ) ;
2014-03-14 14:13:41 -04:00
}
2014-04-23 18:06:41 -04:00
FText SCAQueryDetails : : GetEndText ( ) const
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:06:41 -04:00
return bDisplayQuery ? CurrentQuery . End . ToText ( ) : FText : : GetEmpty ( ) ;
2014-03-14 14:13:41 -04:00
}
TSharedRef < ITableRow > SCAQueryDetails : : ResultListGenerateRow ( TSharedPtr < FCAHitInfo > Info , const TSharedRef < STableViewBase > & OwnerTable )
{
return SNew ( SHitResultRow , OwnerTable )
. Info ( Info )
. OwnerDetailsPtr ( SharedThis ( this ) ) ;
}
void SCAQueryDetails : : UpdateDisplayedBox ( )
{
FCollisionAnalyzer * Analyzer = OwningAnalyzerWidgetPtr . Pin ( ) - > Analyzer ;
Analyzer - > DrawBox = FBox ( 0 ) ;
if ( bDisplayQuery )
{
TArray < TSharedPtr < FCAHitInfo > > SelectedInfos = ResultListWidget - > GetSelectedItems ( ) ;
if ( SelectedInfos . Num ( ) > 0 )
{
UPrimitiveComponent * HitComp = SelectedInfos [ 0 ] - > Result . Component . Get ( ) ;
if ( HitComp ! = NULL )
{
Analyzer - > DrawBox = HitComp - > Bounds . GetBox ( ) ;
}
}
}
}
void SCAQueryDetails : : ResultListSelectionChanged ( TSharedPtr < FCAHitInfo > SelectedInfos , ESelectInfo : : Type SelectInfo )
{
UpdateDisplayedBox ( ) ;
}
2014-12-10 14:24:09 -05:00
void SCAQueryDetails : : OnToggleShowMisses ( ECheckBoxState InCheckboxState )
2014-03-14 14:13:41 -04:00
{
2014-12-10 14:24:09 -05:00
bShowMisses = ( InCheckboxState = = ECheckBoxState : : Checked ) ;
2014-03-14 14:13:41 -04:00
UpdateResultList ( ) ;
}
2014-12-10 14:24:09 -05:00
ECheckBoxState SCAQueryDetails : : GetShowMissesState ( ) const
2014-03-14 14:13:41 -04:00
{
2014-12-10 14:24:09 -05:00
return bShowMisses ? ECheckBoxState : : Checked : ECheckBoxState : : Unchecked ;
2014-03-14 14:13:41 -04:00
}
/** See if an array of results contains a particular component */
static bool ResultsContainComponent ( const TArray < FHitResult > Results , UPrimitiveComponent * Component )
{
for ( int32 i = 0 ; i < Results . Num ( ) ; i + + )
{
if ( Results [ i ] . Component . Get ( ) = = Component )
{
return true ;
}
}
return false ;
}
void SCAQueryDetails : : UpdateResultList ( )
{
ResultList . Empty ( ) ;
UpdateDisplayedBox ( ) ;
if ( bDisplayQuery )
{
// First add actual results
for ( int32 i = 0 ; i < CurrentQuery . Results . Num ( ) ; i + + )
{
ResultList . Add ( FCAHitInfo : : Make ( CurrentQuery . Results [ i ] , false ) ) ;
}
// If desired, look for results from our touching query that were not in the real results, and add them
if ( bShowMisses )
{
for ( int32 i = 0 ; i < CurrentQuery . TouchAllResults . Num ( ) ; i + + )
{
FHitResult & MissResult = CurrentQuery . TouchAllResults [ i ] ;
if ( MissResult . Component . IsValid ( ) & & ! ResultsContainComponent ( CurrentQuery . Results , MissResult . Component . Get ( ) ) )
{
ResultList . Add ( FCAHitInfo : : Make ( MissResult , true ) ) ;
}
}
}
// Then sort
struct FCompareFCAHitInfo
{
FORCEINLINE bool operator ( ) ( const TSharedPtr < FCAHitInfo > A , const TSharedPtr < FCAHitInfo > B ) const
{
check ( A . IsValid ( ) ) ;
check ( B . IsValid ( ) ) ;
return A - > Result . Time < B - > Result . Time ;
}
} ;
ResultList . Sort ( FCompareFCAHitInfo ( ) ) ;
}
// Finally refresh display widget
ResultListWidget - > RequestListRefresh ( ) ;
}
void SCAQueryDetails : : SetCurrentQuery ( const FCAQuery & NewQuery )
{
bDisplayQuery = true ;
CurrentQuery = NewQuery ;
UpdateResultList ( ) ;
}
void SCAQueryDetails : : ClearCurrentQuery ( )
{
bDisplayQuery = false ;
ResultList . Empty ( ) ;
UpdateDisplayedBox ( ) ;
}
FCAQuery * SCAQueryDetails : : GetCurrentQuery ( )
{
return bDisplayQuery ? & CurrentQuery : NULL ;
}
# undef LOCTEXT_NAMESPACE