You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#codereview nick.penwarden, martin.mittring, andrew.brown, gareth.martin [CL 2593605 by Nick Darnell in Main branch]
2012 lines
81 KiB
C++
2012 lines
81 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "UnrealEd.h"
|
|
#include "SnappingUtils.h"
|
|
#include "DynamicMeshBuilder.h"
|
|
#include "CanvasTypes.h"
|
|
#include "Materials/MaterialInstanceDynamic.h"
|
|
|
|
IMPLEMENT_HIT_PROXY(HWidgetAxis,HHitProxy);
|
|
|
|
static const float AXIS_LENGTH = 35.0f;
|
|
static const float TRANSLATE_ROTATE_AXIS_CIRCLE_RADIUS = 20.0f;
|
|
static const float TWOD_AXIS_CIRCLE_RADIUS = 10.0f;
|
|
static const float INNER_AXIS_CIRCLE_RADIUS = 48.0f;
|
|
static const float OUTER_AXIS_CIRCLE_RADIUS = 56.0f;
|
|
static const float ROTATION_TEXT_RADIUS = 75.0f;
|
|
static const int32 AXIS_CIRCLE_SIDES = 24;
|
|
|
|
FWidget::FWidget()
|
|
{
|
|
EditorModeTools = NULL;
|
|
TotalDeltaRotation = 0;
|
|
CurrentDeltaRotation = 0;
|
|
|
|
AxisColorX = FLinearColor(0.594f,0.0197f,0.0f);
|
|
AxisColorY = FLinearColor(0.1349f,0.3959f,0.0f);
|
|
AxisColorZ = FLinearColor(0.0251f,0.207f,0.85f);
|
|
PlaneColorXY = FColor::Yellow;
|
|
ScreenSpaceColor = FColor(196, 196, 196);
|
|
CurrentColor = FColor::Yellow;
|
|
|
|
UMaterial* AxisMaterialBase = GEngine->ArrowMaterial;
|
|
|
|
AxisMaterialX = UMaterialInstanceDynamic::Create( AxisMaterialBase, NULL );
|
|
AxisMaterialX->SetVectorParameterValue( "GizmoColor", AxisColorX );
|
|
|
|
AxisMaterialY = UMaterialInstanceDynamic::Create( AxisMaterialBase, NULL );
|
|
AxisMaterialY->SetVectorParameterValue( "GizmoColor", AxisColorY );
|
|
|
|
AxisMaterialZ = UMaterialInstanceDynamic::Create( AxisMaterialBase, NULL );
|
|
AxisMaterialZ->SetVectorParameterValue( "GizmoColor", AxisColorZ );
|
|
|
|
CurrentAxisMaterial = UMaterialInstanceDynamic::Create( AxisMaterialBase, NULL );
|
|
CurrentAxisMaterial->SetVectorParameterValue("GizmoColor", CurrentColor );
|
|
|
|
OpaquePlaneMaterialXY = UMaterialInstanceDynamic::Create( AxisMaterialBase, NULL );
|
|
OpaquePlaneMaterialXY->SetVectorParameterValue( "GizmoColor", FLinearColor::White );
|
|
|
|
TransparentPlaneMaterialXY = (UMaterial*)StaticLoadObject( UMaterial::StaticClass(),NULL,TEXT("/Engine/EditorMaterials/WidgetVertexColorMaterial.WidgetVertexColorMaterial"),NULL,LOAD_None,NULL );
|
|
|
|
GridMaterial = (UMaterial*)StaticLoadObject( UMaterial::StaticClass(),NULL,TEXT("/Engine/EditorMaterials/WidgetGridVertexColorMaterial_Ma.WidgetGridVertexColorMaterial_Ma"),NULL,LOAD_None,NULL );
|
|
if (!GridMaterial)
|
|
{
|
|
GridMaterial = TransparentPlaneMaterialXY;
|
|
}
|
|
|
|
CurrentAxis = EAxisList::None;
|
|
|
|
CustomCoordSystem = FMatrix::Identity;
|
|
CustomCoordSystemSpace = COORD_World;
|
|
|
|
bAbsoluteTranslationInitialOffsetCached = false;
|
|
InitialTranslationOffset = FVector::ZeroVector;
|
|
InitialTranslationPosition = FVector(0, 0, 0);
|
|
|
|
bDragging = false;
|
|
bSnapEnabled = false;
|
|
bDefaultVisibility = true;
|
|
bIsOrthoDrawingFullRing = false;
|
|
}
|
|
|
|
extern ENGINE_API void StringSize(UFont* Font,int32& XL,int32& YL,const TCHAR* Text, FCanvas* Canvas);
|
|
|
|
void FWidget::SetUsesEditorModeTools( FEditorModeTools* InEditorModeTools )
|
|
{
|
|
EditorModeTools = InEditorModeTools;
|
|
}
|
|
|
|
/**
|
|
* Renders any widget specific HUD text
|
|
* @param Canvas - Canvas to use for 2d rendering
|
|
*/
|
|
void FWidget::DrawHUD (FCanvas* Canvas)
|
|
{
|
|
if (HUDString.Len())
|
|
{
|
|
int32 StringPosX = FMath::FloorToInt(HUDInfoPos.X);
|
|
int32 StringPosY = FMath::FloorToInt(HUDInfoPos.Y);
|
|
|
|
//measure string size
|
|
int32 StringSizeX, StringSizeY;
|
|
StringSize(GEngine->GetSmallFont(), StringSizeX, StringSizeY, *HUDString);
|
|
|
|
//add some padding to the outside
|
|
const int32 Border = 5;
|
|
int32 FillMinX = StringPosX - Border - (StringSizeX>>1);
|
|
int32 FillMinY = StringPosY - Border;// - (StringSizeY>>1);
|
|
StringSizeX += 2*Border;
|
|
StringSizeY += 2*Border;
|
|
|
|
//mostly alpha'ed black
|
|
FCanvasTileItem TileItem( FVector2D( FillMinX, FillMinY), GWhiteTexture, FVector2D( StringSizeX, StringSizeY ), FLinearColor( 0.0f, 0.0f, 0.0f, .25f ) );
|
|
TileItem.BlendMode = SE_BLEND_Translucent;
|
|
Canvas->DrawItem( TileItem );
|
|
FCanvasTextItem TextItem( FVector2D( StringPosX, StringPosY), FText::FromString( HUDString ), GEngine->GetSmallFont(), FLinearColor::White );
|
|
TextItem.bCentreX = true;
|
|
Canvas->DrawItem( TextItem );
|
|
}
|
|
}
|
|
|
|
void FWidget::Render( const FSceneView* View,FPrimitiveDrawInterface* PDI, FEditorViewportClient* ViewportClient )
|
|
{
|
|
check(ViewportClient);
|
|
|
|
TArray<FEdMode*> ActiveModes;
|
|
if( EditorModeTools )
|
|
{
|
|
EditorModeTools->GetActiveModes( ActiveModes );
|
|
}
|
|
|
|
//reset HUD text
|
|
HUDString.Empty();
|
|
|
|
bool bDrawModeSupportsWidgetDrawing = bDefaultVisibility;
|
|
|
|
if( EditorModeTools && ActiveModes.Num() > 0)
|
|
{
|
|
bDrawModeSupportsWidgetDrawing = false;
|
|
// Check to see of any active modes support widget drawing
|
|
for( int32 ModeIndex = 0; ModeIndex < ActiveModes.Num(); ++ModeIndex )
|
|
{
|
|
bDrawModeSupportsWidgetDrawing |= ActiveModes[ModeIndex]->ShouldDrawWidget();
|
|
}
|
|
}
|
|
|
|
|
|
const bool bShowFlagsSupportsWidgetDrawing = View->Family->EngineShowFlags.ModeWidgets;
|
|
const bool bEditorModeToolsSupportsWidgetDrawing = EditorModeTools ? EditorModeTools->GetShowWidget() : true;
|
|
bool bDrawWidget;
|
|
|
|
// Because the movement routines use the widget axis to determine how to transform mouse movement into
|
|
// editor object movement, we need to still run through the Render routine even though widget drawing may be
|
|
// disabled. So we keep a flag that is used to determine whether or not to actually render anything. This way
|
|
// we can still update the widget axis' based on the Context's transform matrices, even though drawing is disabled.
|
|
if(bDrawModeSupportsWidgetDrawing && bShowFlagsSupportsWidgetDrawing && bEditorModeToolsSupportsWidgetDrawing)
|
|
{
|
|
bDrawWidget = true;
|
|
|
|
// See if there is a custom coordinate system we should be using, only change it if we are drawing widgets.
|
|
CustomCoordSystem = ViewportClient->GetWidgetCoordSystem();
|
|
}
|
|
else
|
|
{
|
|
bDrawWidget = false;
|
|
}
|
|
|
|
CustomCoordSystemSpace = ViewportClient->GetWidgetCoordSystemSpace();
|
|
|
|
// If the current modes don't want to use the widget, don't draw it.
|
|
if( EditorModeTools && !EditorModeTools->UsesTransformWidget() )
|
|
{
|
|
CurrentAxis = EAxisList::None;
|
|
return;
|
|
}
|
|
|
|
FVector Loc = ViewportClient->GetWidgetLocation();
|
|
if(!View->ScreenToPixel(View->WorldToScreen(Loc),Origin))
|
|
{
|
|
Origin.X = Origin.Y = 0;
|
|
}
|
|
|
|
switch( ViewportClient->GetWidgetMode() )
|
|
{
|
|
case WM_Translate:
|
|
Render_Translate( View, PDI, ViewportClient, Loc, bDrawWidget );
|
|
break;
|
|
|
|
case WM_Rotate:
|
|
Render_Rotate( View, PDI, ViewportClient, Loc, bDrawWidget );
|
|
break;
|
|
|
|
case WM_Scale:
|
|
Render_Scale( View, PDI, ViewportClient, Loc, bDrawWidget );
|
|
break;
|
|
|
|
case WM_TranslateRotateZ:
|
|
Render_TranslateRotateZ( View, PDI, ViewportClient, Loc, bDrawWidget );
|
|
break;
|
|
|
|
case WM_2D:
|
|
Render_2D( View, PDI, ViewportClient, Loc, bDrawWidget );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Draws an arrow head line for a specific axis.
|
|
*/
|
|
void FWidget::Render_Axis( const FSceneView* View, FPrimitiveDrawInterface* PDI, EAxisList::Type InAxis, FMatrix& InMatrix, UMaterialInterface* InMaterial, const FLinearColor& InColor, FVector2D& OutAxisDir, const FVector& InScale, bool bDrawWidget, bool bCubeHead )
|
|
{
|
|
FMatrix AxisRotation = FMatrix::Identity;
|
|
if( InAxis == EAxisList::Y )
|
|
{
|
|
AxisRotation = FRotationMatrix::MakeFromXZ(FVector(0, 1, 0), FVector(0, 0, 1));
|
|
}
|
|
else if( InAxis == EAxisList::Z )
|
|
{
|
|
AxisRotation = FRotationMatrix::MakeFromXY(FVector(0, 0, 1), FVector(0, 1, 0));
|
|
}
|
|
|
|
FMatrix ArrowToWorld = AxisRotation * InMatrix;
|
|
|
|
// The scale that is passed in potentially leaves one component with a scale of 1, if that happens
|
|
// we need to extract the inform scale and use it to construct the scale that transforms the primitives
|
|
float UniformScale = InScale.GetMax() > 1.0f ? InScale.GetMax() : InScale.GetMin() < 1.0f ? InScale.GetMin() : 1.0f;
|
|
// After the primitives have been scaled and transformed, we apply this inverse scale that flattens the dimension
|
|
// that was scaled up to prevent it from intersecting with the near plane. In perspective this won't have any effect,
|
|
// but in the ortho viewports it will prevent scaling in the direction of the camera and thus intersecting the near plane.
|
|
FVector FlattenScale = FVector(InScale.Component(0) == 1.0f ? 1.0f / UniformScale : 1.0f, InScale.Component(1) == 1.0f ? 1.0f / UniformScale : 1.0f, InScale.Component(2) == 1.0f ? 1.0f / UniformScale : 1.0f);
|
|
|
|
FScaleMatrix Scale(UniformScale);
|
|
ArrowToWorld = Scale * ArrowToWorld;
|
|
|
|
if( bDrawWidget )
|
|
{
|
|
const bool bDisabled = EditorModeTools ? (EditorModeTools->IsDefaultModeActive() && GEditor->HasLockedActors() ) : false;
|
|
PDI->SetHitProxy( new HWidgetAxis( InAxis, bDisabled) );
|
|
|
|
const float AxisLength = AXIS_LENGTH + GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment;
|
|
const float HalfHeight = AxisLength/2.0f;
|
|
const float CylinderRadius = 1.2f;
|
|
const FVector Offset( 0,0,HalfHeight );
|
|
|
|
switch( InAxis )
|
|
{
|
|
case EAxisList::X:
|
|
{
|
|
DrawCylinder(PDI, ( Scale * FRotationMatrix(FRotator(-90, 0.f, 0)) * InMatrix ) * FScaleMatrix(FlattenScale), Offset, FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), CylinderRadius, HalfHeight, 16, InMaterial->GetRenderProxy(false), SDPG_Foreground);
|
|
break;
|
|
}
|
|
case EAxisList::Y:
|
|
{
|
|
DrawCylinder(PDI, (Scale * FRotationMatrix(FRotator(0, 0, 90)) * InMatrix)* FScaleMatrix(FlattenScale), Offset, FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), CylinderRadius, HalfHeight, 16, InMaterial->GetRenderProxy(false), SDPG_Foreground );
|
|
break;
|
|
}
|
|
case EAxisList::Z:
|
|
{
|
|
DrawCylinder(PDI, ( Scale * InMatrix ) * FScaleMatrix(FlattenScale), Offset, FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), CylinderRadius, HalfHeight, 16, InMaterial->GetRenderProxy(false), SDPG_Foreground);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bCubeHead )
|
|
{
|
|
const float CubeHeadOffset = 3.0f;
|
|
FVector RootPos(AxisLength + CubeHeadOffset, 0, 0);
|
|
|
|
Render_Cube(PDI, (FTranslationMatrix(RootPos) * ArrowToWorld) * FScaleMatrix(FlattenScale), InMaterial, FVector(4.0f));
|
|
}
|
|
else
|
|
{
|
|
const float ConeHeadOffset = 12.0f;
|
|
FVector RootPos(AxisLength + ConeHeadOffset, 0, 0);
|
|
|
|
float Angle = FMath::DegreesToRadians( PI * 5 );
|
|
DrawCone(PDI, ( FScaleMatrix(-13) * FTranslationMatrix(RootPos) * ArrowToWorld ) * FScaleMatrix(FlattenScale), Angle, Angle, 32, false, FColor::White, InMaterial->GetRenderProxy(false), SDPG_Foreground);
|
|
}
|
|
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
|
|
FVector2D AxisEnd;
|
|
if(!View->ScreenToPixel(View->WorldToScreen(ArrowToWorld.TransformPosition(FVector(64,0,0))),AxisEnd))
|
|
{
|
|
AxisEnd.X = AxisEnd.Y = 0;
|
|
}
|
|
|
|
OutAxisDir = (AxisEnd - Origin).GetSafeNormal();
|
|
}
|
|
|
|
void FWidget::Render_Cube( FPrimitiveDrawInterface* PDI, const FMatrix& InMatrix, const UMaterialInterface* InMaterial, const FVector& InScale )
|
|
{
|
|
const FMatrix CubeToWorld = FScaleMatrix(InScale) * InMatrix;
|
|
DrawBox( PDI, CubeToWorld, FVector(1,1,1), InMaterial->GetRenderProxy( false ), SDPG_Foreground );
|
|
}
|
|
|
|
void DrawCornerHelper( FPrimitiveDrawInterface* PDI, const FMatrix& LocalToWorld, const FVector& Length, float Thickness, const FMaterialRenderProxy* MaterialRenderProxy,uint8 DepthPriorityGroup )
|
|
{
|
|
const float TH = Thickness;
|
|
|
|
float TX = Length.X/2;
|
|
float TY = Length.Y/2;
|
|
float TZ = Length.Z/2;
|
|
|
|
FDynamicMeshBuilder MeshBuilder;
|
|
|
|
// Top
|
|
{
|
|
int32 VertexIndices[4];
|
|
VertexIndices[0] = MeshBuilder.AddVertex( FVector(-TX, -TY, +TZ), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), FColor::White );
|
|
VertexIndices[1] = MeshBuilder.AddVertex( FVector(-TX, +TY, +TZ), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), FColor::White );
|
|
VertexIndices[2] = MeshBuilder.AddVertex( FVector(+TX, +TY, +TZ), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), FColor::White );
|
|
VertexIndices[3] = MeshBuilder.AddVertex( FVector(+TX, -TY, +TZ), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), FColor::White );
|
|
|
|
MeshBuilder.AddTriangle(VertexIndices[0],VertexIndices[1],VertexIndices[2]);
|
|
MeshBuilder.AddTriangle(VertexIndices[0],VertexIndices[2],VertexIndices[3]);
|
|
}
|
|
|
|
//Left
|
|
{
|
|
int32 VertexIndices[4];
|
|
VertexIndices[0] = MeshBuilder.AddVertex( FVector(-TX, -TY, TZ-TH), FVector2D::ZeroVector, FVector(0,0,1), FVector(0,1,0), FVector(-1,0,0), FColor::White );
|
|
VertexIndices[1] = MeshBuilder.AddVertex( FVector(-TX, -TY, TZ), FVector2D::ZeroVector, FVector(0,0,1), FVector(0,1,0), FVector(-1,0,0), FColor::White );
|
|
VertexIndices[2] = MeshBuilder.AddVertex( FVector(-TX, +TY, TZ), FVector2D::ZeroVector, FVector(0,0,1), FVector(0,1,0), FVector(-1,0,0), FColor::White );
|
|
VertexIndices[3] = MeshBuilder.AddVertex( FVector(-TX, +TY, TZ-TH), FVector2D::ZeroVector, FVector(0,0,1), FVector(0,1,0), FVector(-1,0,0), FColor::White );
|
|
|
|
|
|
MeshBuilder.AddTriangle(VertexIndices[0],VertexIndices[1],VertexIndices[2]);
|
|
MeshBuilder.AddTriangle(VertexIndices[0],VertexIndices[2],VertexIndices[3]);
|
|
}
|
|
|
|
// Front
|
|
{
|
|
int32 VertexIndices[5];
|
|
VertexIndices[0] = MeshBuilder.AddVertex( FVector(-TX, +TY, TZ-TH), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,-1), FVector(0,1,0), FColor::White );
|
|
VertexIndices[1] = MeshBuilder.AddVertex( FVector(-TX, +TY, +TZ ), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,-1), FVector(0,1,0), FColor::White );
|
|
VertexIndices[2] = MeshBuilder.AddVertex( FVector(+TX-TH, +TY, +TX ), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,-1), FVector(0,1,0), FColor::White );
|
|
VertexIndices[3] = MeshBuilder.AddVertex( FVector(+TX, +TY, +TZ ), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,-1), FVector(0,1,0), FColor::White );
|
|
VertexIndices[4] = MeshBuilder.AddVertex( FVector(+TX-TH, +TY, TZ-TH), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,-1), FVector(0,1,0), FColor::White );
|
|
|
|
MeshBuilder.AddTriangle(VertexIndices[0],VertexIndices[1],VertexIndices[2]);
|
|
MeshBuilder.AddTriangle(VertexIndices[0],VertexIndices[2],VertexIndices[4]);
|
|
MeshBuilder.AddTriangle(VertexIndices[4],VertexIndices[2],VertexIndices[3]);
|
|
}
|
|
|
|
// Back
|
|
{
|
|
int32 VertexIndices[5];
|
|
VertexIndices[0] = MeshBuilder.AddVertex( FVector(-TX, -TY, TZ-TH), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,1), FVector(0,-1,0), FColor::White );
|
|
VertexIndices[1] = MeshBuilder.AddVertex( FVector(-TX, -TY, +TZ), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,1), FVector(0,-1,0), FColor::White );
|
|
VertexIndices[2] = MeshBuilder.AddVertex( FVector(+TX-TH, -TY, +TX), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,1), FVector(0,-1,0), FColor::White );
|
|
VertexIndices[3] = MeshBuilder.AddVertex( FVector(+TX, -TY, +TZ), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,1), FVector(0,-1,0), FColor::White );
|
|
VertexIndices[4] = MeshBuilder.AddVertex( FVector(+TX-TH, -TY, TZ-TH), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,1), FVector(0,-1,0), FColor::White );
|
|
|
|
MeshBuilder.AddTriangle(VertexIndices[0],VertexIndices[1],VertexIndices[2]);
|
|
MeshBuilder.AddTriangle(VertexIndices[0],VertexIndices[2],VertexIndices[4]);
|
|
MeshBuilder.AddTriangle(VertexIndices[4],VertexIndices[2],VertexIndices[3]);
|
|
}
|
|
// Bottom
|
|
{
|
|
int32 VertexIndices[4];
|
|
VertexIndices[0] = MeshBuilder.AddVertex( FVector(-TX, -TY, TZ-TH), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,-1), FVector(0,0,1), FColor::White );
|
|
VertexIndices[1] = MeshBuilder.AddVertex( FVector(-TX, +TY, TZ-TH), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,-1), FVector(0,0,1), FColor::White );
|
|
VertexIndices[2] = MeshBuilder.AddVertex( FVector(+TX-TH, +TY, TZ-TH), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,-1), FVector(0,0,1), FColor::White );
|
|
VertexIndices[3] = MeshBuilder.AddVertex( FVector(+TX-TH, -TY, TZ-TH), FVector2D::ZeroVector, FVector(1,0,0), FVector(0,0,-1), FVector(0,0,1), FColor::White );
|
|
|
|
MeshBuilder.AddTriangle(VertexIndices[0],VertexIndices[1],VertexIndices[2]);
|
|
MeshBuilder.AddTriangle(VertexIndices[0],VertexIndices[2],VertexIndices[3]);
|
|
}
|
|
MeshBuilder.Draw(PDI,LocalToWorld,MaterialRenderProxy,DepthPriorityGroup,0.f);
|
|
}
|
|
|
|
void DrawDualAxis( FPrimitiveDrawInterface* PDI, const FMatrix& BoxToWorld,const FVector& Length, float Thickness, const FMaterialRenderProxy* AxisMat,const FMaterialRenderProxy* Axis2Mat )
|
|
{
|
|
DrawCornerHelper(PDI, BoxToWorld, Length, Thickness, Axis2Mat, SDPG_Foreground);
|
|
DrawCornerHelper(PDI, FScaleMatrix(FVector(-1, 1, 1)) * FRotationMatrix(FRotator(-90, 0, 0)) * BoxToWorld, Length, Thickness, AxisMat, SDPG_Foreground);
|
|
}
|
|
/**
|
|
* Draws the translation widget.
|
|
*/
|
|
void FWidget::Render_Translate( const FSceneView* View, FPrimitiveDrawInterface* PDI, FEditorViewportClient* ViewportClient, const FVector& InLocation, bool bDrawWidget )
|
|
{
|
|
// Figure out axis colors
|
|
const FLinearColor& XColor = ( CurrentAxis&EAxisList::X ? (FLinearColor)CurrentColor : AxisColorX );
|
|
const FLinearColor& YColor = ( CurrentAxis&EAxisList::Y ? (FLinearColor)CurrentColor : AxisColorY );
|
|
const FLinearColor& ZColor = ( CurrentAxis&EAxisList::Z ? (FLinearColor)CurrentColor : AxisColorZ );
|
|
FColor CurrentScreenColor = ( CurrentAxis & EAxisList::Screen ? CurrentColor : ScreenSpaceColor );
|
|
|
|
// Figure out axis matrices
|
|
FMatrix WidgetMatrix = CustomCoordSystem * FTranslationMatrix( InLocation );
|
|
|
|
bool bIsPerspective = ( View->ViewMatrices.ProjMatrix.M[3][3] < 1.0f );
|
|
const bool bIsOrthoXY = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[2][2]) > 0.0f;
|
|
const bool bIsOrthoXZ = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[1][2]) > 0.0f;
|
|
const bool bIsOrthoYZ = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[0][2]) > 0.0f;
|
|
|
|
// For local space widgets, we always want to draw all three axis, since they may not be aligned with
|
|
// the orthographic projection anyway.
|
|
const bool bIsLocalSpace = ( ViewportClient->GetWidgetCoordSystemSpace() == COORD_Local );
|
|
|
|
const EAxisList::Type DrawAxis = GetAxisToDraw( ViewportClient->GetWidgetMode() );
|
|
|
|
const bool bDisabled = IsWidgetDisabled();
|
|
|
|
FVector Scale;
|
|
float UniformScale = View->WorldToScreen(InLocation).W * ( 4.0f / View->ViewRect.Width() / View->ViewMatrices.ProjMatrix.M[0][0] );
|
|
|
|
if ( bIsOrthoXY )
|
|
{
|
|
Scale = FVector(UniformScale, UniformScale, 1.0f);
|
|
}
|
|
else if ( bIsOrthoXZ )
|
|
{
|
|
Scale = FVector(UniformScale, 1.0f, UniformScale);
|
|
}
|
|
else if ( bIsOrthoYZ )
|
|
{
|
|
Scale = FVector(1.0f, UniformScale, UniformScale);
|
|
}
|
|
else
|
|
{
|
|
Scale = FVector(UniformScale, UniformScale, UniformScale);
|
|
}
|
|
|
|
// Draw the axis lines with arrow heads
|
|
if( DrawAxis&EAxisList::X && (bIsPerspective || bIsLocalSpace || !bIsOrthoYZ) )
|
|
{
|
|
UMaterialInstanceDynamic* XMaterial = ( CurrentAxis&EAxisList::X ? CurrentAxisMaterial : AxisMaterialX );
|
|
Render_Axis( View, PDI, EAxisList::X, WidgetMatrix, XMaterial, XColor, XAxisDir, Scale, bDrawWidget );
|
|
}
|
|
|
|
if( DrawAxis&EAxisList::Y && (bIsPerspective || bIsLocalSpace || !bIsOrthoXZ) )
|
|
{
|
|
UMaterialInstanceDynamic* YMaterial = ( CurrentAxis&EAxisList::Y ? CurrentAxisMaterial : AxisMaterialY );
|
|
Render_Axis( View, PDI, EAxisList::Y, WidgetMatrix, YMaterial, YColor, YAxisDir, Scale, bDrawWidget );
|
|
}
|
|
|
|
if( DrawAxis&EAxisList::Z && (bIsPerspective || bIsLocalSpace || !bIsOrthoXY) )
|
|
{
|
|
UMaterialInstanceDynamic* ZMaterial = ( CurrentAxis&EAxisList::Z ? CurrentAxisMaterial : AxisMaterialZ );
|
|
Render_Axis( View, PDI, EAxisList::Z, WidgetMatrix, ZMaterial, ZColor, ZAxisDir, Scale, bDrawWidget );
|
|
}
|
|
|
|
// Draw the grabbers
|
|
if( bDrawWidget )
|
|
{
|
|
FVector CornerPos = FVector(7, 0, 7) * UniformScale;
|
|
FVector AxisSize = FVector(12, 1.2, 12) * UniformScale;
|
|
float CornerLength = 1.2f * UniformScale;
|
|
|
|
// After the primitives have been scaled and transformed, we apply this inverse scale that flattens the dimension
|
|
// that was scaled up to prevent it from intersecting with the near plane. In perspective this won't have any effect,
|
|
// but in the ortho viewports it will prevent scaling in the direction of the camera and thus intersecting the near plane.
|
|
FVector FlattenScale = FVector(Scale.Component(0) == 1.0f ? 1.0f / UniformScale : 1.0f, Scale.Component(1) == 1.0f ? 1.0f / UniformScale : 1.0f, Scale.Component(2) == 1.0f ? 1.0f / UniformScale : 1.0f);
|
|
|
|
if (bIsPerspective || bIsLocalSpace || bIsOrthoXY)
|
|
{
|
|
if( (DrawAxis&EAxisList::XY) == EAxisList::XY ) // Top
|
|
{
|
|
UMaterialInstanceDynamic* XMaterial = ( (CurrentAxis&EAxisList::XY) == EAxisList::XY ? CurrentAxisMaterial : AxisMaterialX );
|
|
UMaterialInstanceDynamic* YMaterial = ( (CurrentAxis&EAxisList::XY) == EAxisList::XY? CurrentAxisMaterial : AxisMaterialY );
|
|
|
|
PDI->SetHitProxy( new HWidgetAxis(EAxisList::XY, bDisabled) );
|
|
{
|
|
DrawDualAxis(PDI, ( FTranslationMatrix(CornerPos) * FRotationMatrix(FRotator(0, 0, 90)) * WidgetMatrix ) * FScaleMatrix(FlattenScale), AxisSize, CornerLength, XMaterial->GetRenderProxy(false), YMaterial->GetRenderProxy(false));
|
|
}
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
}
|
|
|
|
if (bIsPerspective || bIsLocalSpace || bIsOrthoXZ) // Front
|
|
{
|
|
if( (DrawAxis&EAxisList::XZ) == EAxisList::XZ )
|
|
{
|
|
UMaterialInstanceDynamic* XMaterial = ( (CurrentAxis&EAxisList::XZ) == EAxisList::XZ ? CurrentAxisMaterial : AxisMaterialX );
|
|
UMaterialInstanceDynamic* ZMaterial = ( (CurrentAxis&EAxisList::XZ) == EAxisList::XZ ? CurrentAxisMaterial : AxisMaterialZ );
|
|
|
|
PDI->SetHitProxy( new HWidgetAxis(EAxisList::XZ, bDisabled) );
|
|
{
|
|
DrawDualAxis(PDI, (FTranslationMatrix(CornerPos) * WidgetMatrix) * FScaleMatrix(FlattenScale), AxisSize, CornerLength, XMaterial->GetRenderProxy(false), ZMaterial->GetRenderProxy(false) );
|
|
}
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
}
|
|
|
|
if( bIsPerspective || bIsLocalSpace || bIsOrthoYZ ) // Side
|
|
{
|
|
if( (DrawAxis&EAxisList::YZ) == EAxisList::YZ )
|
|
{
|
|
UMaterialInstanceDynamic* YMaterial = ( (CurrentAxis&EAxisList::YZ) == EAxisList::YZ ? CurrentAxisMaterial : AxisMaterialY );
|
|
UMaterialInstanceDynamic* ZMaterial = ( (CurrentAxis&EAxisList::YZ) == EAxisList::YZ ? CurrentAxisMaterial : AxisMaterialZ );
|
|
|
|
PDI->SetHitProxy( new HWidgetAxis(EAxisList::YZ, bDisabled) );
|
|
{
|
|
DrawDualAxis(PDI, (FTranslationMatrix(CornerPos) * FRotationMatrix(FRotator(0, 90, 0)) * WidgetMatrix) * FScaleMatrix(FlattenScale), AxisSize, CornerLength, YMaterial->GetRenderProxy(false), ZMaterial->GetRenderProxy(false) );
|
|
}
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw screen-space movement handle (circle)
|
|
if( bDrawWidget && ( DrawAxis & EAxisList::Screen ) && bIsPerspective )
|
|
{
|
|
PDI->SetHitProxy( new HWidgetAxis(EAxisList::Screen, bDisabled) );
|
|
const FVector CameraXAxis = View->ViewMatrices.ViewMatrix.GetColumn(0);
|
|
const FVector CameraYAxis = View->ViewMatrices.ViewMatrix.GetColumn(1);
|
|
const FVector CameraZAxis = View->ViewMatrices.ViewMatrix.GetColumn(2);
|
|
|
|
UMaterialInstanceDynamic* XYZMaterial = ( CurrentAxis&EAxisList::Screen) ? CurrentAxisMaterial : OpaquePlaneMaterialXY;
|
|
DrawSphere( PDI, InLocation, 4.0f * Scale, 10, 5, XYZMaterial->GetRenderProxy(false), SDPG_Foreground );
|
|
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws the rotation widget.
|
|
*/
|
|
void FWidget::Render_Rotate( const FSceneView* View,FPrimitiveDrawInterface* PDI, FEditorViewportClient* ViewportClient, const FVector& InLocation, bool bDrawWidget )
|
|
{
|
|
float Scale = View->WorldToScreen( InLocation ).W * ( 4.0f / View->ViewRect.Width() / View->ViewMatrices.ProjMatrix.M[0][0] );
|
|
|
|
//get the axes
|
|
FVector XAxis = CustomCoordSystem.TransformVector(FVector(1, 0, 0));
|
|
FVector YAxis = CustomCoordSystem.TransformVector(FVector(0, 1, 0));
|
|
FVector ZAxis = CustomCoordSystem.TransformVector(FVector(0, 0, 1));
|
|
|
|
EAxisList::Type DrawAxis = GetAxisToDraw( ViewportClient->GetWidgetMode() );
|
|
|
|
FVector DirectionToWidget = View->IsPerspectiveProjection() ? (InLocation - View->ViewMatrices.ViewOrigin) : -View->GetViewDirection();
|
|
DirectionToWidget.Normalize();
|
|
|
|
// Draw a circle for each axis
|
|
if (bDrawWidget || bDragging)
|
|
{
|
|
bIsOrthoDrawingFullRing = false;
|
|
|
|
//no draw the arc segments
|
|
if( DrawAxis&EAxisList::X )
|
|
{
|
|
DrawRotationArc(View, PDI, EAxisList::X, InLocation, ZAxis, YAxis, DirectionToWidget, AxisColorX.ToFColor(true), Scale, XAxisDir);
|
|
}
|
|
|
|
if( DrawAxis&EAxisList::Y )
|
|
{
|
|
DrawRotationArc(View, PDI, EAxisList::Y, InLocation, XAxis, ZAxis, DirectionToWidget, AxisColorY.ToFColor(true), Scale, YAxisDir);
|
|
}
|
|
|
|
if( DrawAxis&EAxisList::Z )
|
|
{
|
|
DrawRotationArc(View, PDI, EAxisList::Z, InLocation, XAxis, YAxis, DirectionToWidget, AxisColorZ.ToFColor(true), Scale, ZAxisDir);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws the scaling widget.
|
|
*/
|
|
void FWidget::Render_Scale( const FSceneView* View,FPrimitiveDrawInterface* PDI, FEditorViewportClient* ViewportClient, const FVector& InLocation, bool bDrawWidget )
|
|
{
|
|
// Figure out axis colors
|
|
const FLinearColor& XColor = ( CurrentAxis&EAxisList::X ? (FLinearColor)CurrentColor : AxisColorX );
|
|
const FLinearColor& YColor = ( CurrentAxis&EAxisList::Y ? (FLinearColor)CurrentColor : AxisColorY );
|
|
const FLinearColor& ZColor = ( CurrentAxis&EAxisList::Z ? (FLinearColor)CurrentColor : AxisColorZ );
|
|
FColor CurrentScreenColor = ( CurrentAxis & EAxisList::Screen ? CurrentColor : ScreenSpaceColor );
|
|
|
|
// Figure out axis materials
|
|
|
|
UMaterialInstanceDynamic* XMaterial = ( CurrentAxis&EAxisList::X ? CurrentAxisMaterial : AxisMaterialX );
|
|
UMaterialInstanceDynamic* YMaterial = ( CurrentAxis&EAxisList::Y ? CurrentAxisMaterial : AxisMaterialY );
|
|
UMaterialInstanceDynamic* ZMaterial = ( CurrentAxis&EAxisList::Z ? CurrentAxisMaterial : AxisMaterialZ );
|
|
UMaterialInstanceDynamic* XYZMaterial = ( CurrentAxis&EAxisList::XYZ ? CurrentAxisMaterial : OpaquePlaneMaterialXY );
|
|
|
|
// Figure out axis matrices
|
|
|
|
FMatrix WidgetMatrix = CustomCoordSystem * FTranslationMatrix( InLocation );
|
|
// Determine viewport
|
|
|
|
const EAxisList::Type DrawAxis = GetAxisToDraw( ViewportClient->GetWidgetMode() );
|
|
const bool bIsPerspective = ( View->ViewMatrices.ProjMatrix.M[3][3] < 1.0f );
|
|
const bool bIsOrthoXY = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[2][2]) > 0.0f;
|
|
const bool bIsOrthoXZ = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[1][2]) > 0.0f;
|
|
const bool bIsOrthoYZ = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[0][2]) > 0.0f;
|
|
|
|
FVector Scale;
|
|
const float UniformScale = View->WorldToScreen(InLocation).W * ( 4.0f / View->ViewRect.Width() / View->ViewMatrices.ProjMatrix.M[0][0] );
|
|
|
|
if ( bIsOrthoXY )
|
|
{
|
|
Scale = FVector(UniformScale, UniformScale, 1.0f);
|
|
}
|
|
else if ( bIsOrthoXZ )
|
|
{
|
|
Scale = FVector(UniformScale, 1.0f, UniformScale);
|
|
}
|
|
else if ( bIsOrthoYZ )
|
|
{
|
|
Scale = FVector(1.0f, UniformScale, UniformScale);
|
|
}
|
|
else
|
|
{
|
|
Scale = FVector(UniformScale, UniformScale, UniformScale);
|
|
}
|
|
|
|
// Draw the axis lines with cube heads
|
|
if( !bIsOrthoYZ && DrawAxis&EAxisList::X )
|
|
{
|
|
Render_Axis( View, PDI, EAxisList::X, WidgetMatrix, XMaterial, XColor, XAxisDir, Scale, bDrawWidget, true );
|
|
}
|
|
|
|
if( !bIsOrthoXZ && DrawAxis&EAxisList::Y )
|
|
{
|
|
Render_Axis( View, PDI, EAxisList::Y, WidgetMatrix, YMaterial, YColor, YAxisDir, Scale, bDrawWidget, true );
|
|
}
|
|
|
|
if( !bIsOrthoXY && DrawAxis&EAxisList::Z )
|
|
{
|
|
Render_Axis( View, PDI, EAxisList::Z, WidgetMatrix, ZMaterial, ZColor, ZAxisDir, Scale, bDrawWidget, true );
|
|
}
|
|
|
|
// Draw grabber handles and center cube
|
|
if ( bDrawWidget )
|
|
{
|
|
const bool bDisabled = IsWidgetDisabled();
|
|
|
|
// Grabber handles
|
|
if( !bIsOrthoYZ && !bIsOrthoXZ && ((DrawAxis&(EAxisList::X|EAxisList::Y)) == (EAxisList::X|EAxisList::Y)) )
|
|
{
|
|
PDI->SetHitProxy( new HWidgetAxis(EAxisList::XY, bDisabled) );
|
|
{
|
|
PDI->DrawLine( WidgetMatrix.TransformPosition(FVector(24,0,0) * Scale), WidgetMatrix.TransformPosition(FVector(12,12,0) * Scale), XColor, SDPG_Foreground );
|
|
PDI->DrawLine( WidgetMatrix.TransformPosition(FVector(12,12,0) * Scale), WidgetMatrix.TransformPosition(FVector(0,24,0) * Scale), YColor, SDPG_Foreground );
|
|
}
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
|
|
if( !bIsOrthoYZ && !bIsOrthoXY && ((DrawAxis&(EAxisList::X|EAxisList::Z)) == (EAxisList::X|EAxisList::Z)) )
|
|
{
|
|
PDI->SetHitProxy( new HWidgetAxis(EAxisList::XZ, bDisabled) );
|
|
{
|
|
PDI->DrawLine( WidgetMatrix.TransformPosition(FVector(24,0,0) * Scale), WidgetMatrix.TransformPosition(FVector(12,0,12) * Scale), XColor, SDPG_Foreground );
|
|
PDI->DrawLine( WidgetMatrix.TransformPosition(FVector(12,0,12) * Scale), WidgetMatrix.TransformPosition(FVector(0,0,24) * Scale), ZColor, SDPG_Foreground );
|
|
}
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
|
|
if( !bIsOrthoXY && !bIsOrthoXZ && ((DrawAxis&(EAxisList::Y|EAxisList::Z)) == (EAxisList::Y|EAxisList::Z)) )
|
|
{
|
|
PDI->SetHitProxy( new HWidgetAxis(EAxisList::YZ, bDisabled) );
|
|
{
|
|
PDI->DrawLine( WidgetMatrix.TransformPosition(FVector(0,24,0) * Scale), WidgetMatrix.TransformPosition(FVector(0,12,12) * Scale), YColor, SDPG_Foreground );
|
|
PDI->DrawLine( WidgetMatrix.TransformPosition(FVector(0,12,12) * Scale), WidgetMatrix.TransformPosition(FVector(0,0,24) * Scale), ZColor, SDPG_Foreground );
|
|
}
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
|
|
// Center cube
|
|
if( (DrawAxis&(EAxisList::XYZ)) == EAxisList::XYZ )
|
|
{
|
|
PDI->SetHitProxy( new HWidgetAxis(EAxisList::XYZ, bDisabled) );
|
|
|
|
Render_Cube(PDI, WidgetMatrix, XYZMaterial, Scale * 4 );
|
|
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws the Translate & Rotate Z widget.
|
|
*/
|
|
|
|
void FWidget::Render_TranslateRotateZ( const FSceneView* View, FPrimitiveDrawInterface* PDI, FEditorViewportClient* ViewportClient, const FVector& InLocation, bool bDrawWidget )
|
|
{
|
|
// Figure out axis colors
|
|
|
|
FColor XYPlaneColor = ( (CurrentAxis&EAxisList::XY) == EAxisList::XY) ? CurrentColor : PlaneColorXY;
|
|
FColor ZRotateColor = ( ( CurrentAxis&EAxisList::ZRotation ) == EAxisList::ZRotation ) ? CurrentColor : AxisColorZ.ToFColor(true);
|
|
FColor XColor = ( ( CurrentAxis&EAxisList::X ) == EAxisList::X ) ? CurrentColor : AxisColorX.ToFColor(true);
|
|
FColor YColor = ( ( CurrentAxis&EAxisList::Y ) == EAxisList::Y && CurrentAxis != EAxisList::ZRotation ) ? CurrentColor : AxisColorY.ToFColor(true);
|
|
FColor ZColor = ( ( CurrentAxis&EAxisList::Z ) == EAxisList::Z ) ? CurrentColor : AxisColorZ.ToFColor(true);
|
|
|
|
// Figure out axis materials
|
|
UMaterialInterface* ZRotateMaterial = (CurrentAxis&EAxisList::ZRotation) == EAxisList::ZRotation? CurrentAxisMaterial : AxisMaterialZ;
|
|
UMaterialInterface* XMaterial = CurrentAxis&EAxisList::X? CurrentAxisMaterial : AxisMaterialX;
|
|
UMaterialInterface* YMaterial = ( CurrentAxis&EAxisList::Y && CurrentAxis != EAxisList::ZRotation ) ? CurrentAxisMaterial : AxisMaterialY;
|
|
UMaterialInterface* ZMaterial = CurrentAxis&EAxisList::Z ? CurrentAxisMaterial : AxisMaterialZ;
|
|
|
|
// Figure out axis matrices
|
|
FMatrix AxisMatrix = CustomCoordSystem * FTranslationMatrix( InLocation );
|
|
|
|
bool bIsPerspective = ( View->ViewMatrices.ProjMatrix.M[3][3] < 1.0f );
|
|
const bool bIsOrthoXY = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[2][2]) > 0.0f;
|
|
const bool bIsOrthoXZ = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[1][2]) > 0.0f;
|
|
const bool bIsOrthoYZ = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[0][2]) > 0.0f;
|
|
|
|
// For local space widgets, we always want to draw all three axis, since they may not be aligned with
|
|
// the orthographic projection anyway.
|
|
bool bIsLocalSpace = ( ViewportClient->GetWidgetCoordSystemSpace() == COORD_Local );
|
|
|
|
EAxisList::Type DrawAxis = GetAxisToDraw( ViewportClient->GetWidgetMode() );
|
|
|
|
FVector Scale;
|
|
float UniformScale = View->WorldToScreen(InLocation).W * ( 4.0f / View->ViewRect.Width() / View->ViewMatrices.ProjMatrix.M[0][0] );
|
|
|
|
if ( bIsOrthoXY )
|
|
{
|
|
Scale = FVector(UniformScale, UniformScale, 1.0f);
|
|
}
|
|
else if ( bIsOrthoXZ )
|
|
{
|
|
Scale = FVector(UniformScale, 1.0f, UniformScale);
|
|
}
|
|
else if ( bIsOrthoYZ )
|
|
{
|
|
Scale = FVector(1.0f, UniformScale, UniformScale);
|
|
}
|
|
else
|
|
{
|
|
Scale = FVector(UniformScale, UniformScale, UniformScale);
|
|
}
|
|
|
|
// Draw the grabbers
|
|
if( bDrawWidget )
|
|
{
|
|
// Draw the axis lines with arrow heads
|
|
if( DrawAxis&EAxisList::X && (bIsPerspective || bIsLocalSpace || View->ViewMatrices.ViewMatrix.M[0][2] != -1.f) )
|
|
{
|
|
Render_Axis( View, PDI, EAxisList::X, AxisMatrix, XMaterial, XColor, XAxisDir, Scale, bDrawWidget );
|
|
}
|
|
|
|
if( DrawAxis&EAxisList::Y && (bIsPerspective || bIsLocalSpace || View->ViewMatrices.ViewMatrix.M[1][2] != -1.f) )
|
|
{
|
|
Render_Axis( View, PDI, EAxisList::Y, AxisMatrix, YMaterial, YColor, YAxisDir, Scale, bDrawWidget );
|
|
}
|
|
|
|
if( DrawAxis&EAxisList::Z && (bIsPerspective || bIsLocalSpace || View->ViewMatrices.ViewMatrix.M[0][1] != 1.f) )
|
|
{
|
|
Render_Axis( View, PDI, EAxisList::Z, AxisMatrix, ZMaterial, ZColor, ZAxisDir, Scale, bDrawWidget );
|
|
}
|
|
|
|
const bool bDisabled = IsWidgetDisabled();
|
|
|
|
const float ScaledRadius = (TRANSLATE_ROTATE_AXIS_CIRCLE_RADIUS * UniformScale) + GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment;
|
|
|
|
//ZRotation
|
|
if( DrawAxis&EAxisList::ZRotation && (bIsPerspective || bIsLocalSpace || View->ViewMatrices.ViewMatrix.M[0][2] != -1.f) )
|
|
{
|
|
PDI->SetHitProxy( new HWidgetAxis(EAxisList::ZRotation, bDisabled) );
|
|
{
|
|
FVector XAxis = CustomCoordSystem.TransformPosition( FVector(1,0,0).RotateAngleAxis( (EditorModeTools ? EditorModeTools->TranslateRotateXAxisAngle : 0 ), FVector(0,0,1)) );
|
|
FVector YAxis = CustomCoordSystem.TransformPosition( FVector(0,1,0).RotateAngleAxis( (EditorModeTools ? EditorModeTools->TranslateRotateXAxisAngle : 0 ), FVector(0,0,1)) );
|
|
FVector BaseArrowPoint = InLocation + XAxis * ScaledRadius;
|
|
DrawFlatArrow(PDI, BaseArrowPoint, XAxis, YAxis, ZRotateColor, ScaledRadius, ScaledRadius*.5f, ZRotateMaterial->GetRenderProxy(false), SDPG_Foreground);
|
|
}
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
|
|
//XY Plane
|
|
if( bIsPerspective || bIsLocalSpace || View->ViewMatrices.ViewMatrix.M[0][1] != 1.f )
|
|
{
|
|
if( (DrawAxis & EAxisList::XY) == EAxisList::XY )
|
|
{
|
|
// Add more sides to the circle if we've been scaled up to keep the circle looking circular
|
|
// An extra side for every 5 extra unreal units seems to produce a nice result
|
|
const int32 CircleSides = (GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment > 0)
|
|
? AXIS_CIRCLE_SIDES + (GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment / 5)
|
|
: AXIS_CIRCLE_SIDES;
|
|
|
|
PDI->SetHitProxy( new HWidgetAxis(EAxisList::XY, bDisabled) );
|
|
{
|
|
DrawCircle( PDI, InLocation, CustomCoordSystem.TransformPosition( FVector(1,0,0) ), CustomCoordSystem.TransformPosition( FVector(0,1,0) ), XYPlaneColor, ScaledRadius, CircleSides, SDPG_Foreground );
|
|
XYPlaneColor.A = ((CurrentAxis&EAxisList::XY) == EAxisList::XY) ? 0x3f : 0x0f; //make the disc transparent
|
|
DrawDisc ( PDI, InLocation, CustomCoordSystem.TransformPosition( FVector(1,0,0) ), CustomCoordSystem.TransformPosition( FVector(0,1,0) ), XYPlaneColor, ScaledRadius, CircleSides, TransparentPlaneMaterialXY->GetRenderProxy(false), SDPG_Foreground );
|
|
}
|
|
PDI->SetHitProxy( NULL );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws the 2D widget.
|
|
*/
|
|
|
|
void FWidget::Render_2D(const FSceneView* View, FPrimitiveDrawInterface* PDI, FEditorViewportClient* ViewportClient, const FVector& InLocation, bool bDrawWidget)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Translation subwidget
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Figure out axis colors
|
|
const FLinearColor& XColor = (CurrentAxis&EAxisList::X ? (FLinearColor)CurrentColor : AxisColorX);
|
|
const FLinearColor& YColor = (CurrentAxis&EAxisList::Y ? (FLinearColor)CurrentColor : AxisColorY);
|
|
const FLinearColor& ZColor = (CurrentAxis&EAxisList::Z ? (FLinearColor)CurrentColor : AxisColorZ);
|
|
FColor CurrentScreenColor = (CurrentAxis & EAxisList::Screen ? CurrentColor : ScreenSpaceColor);
|
|
|
|
// Figure out axis matrices
|
|
FMatrix WidgetMatrix = CustomCoordSystem * FTranslationMatrix(InLocation);
|
|
|
|
bool bIsPerspective = (View->ViewMatrices.ProjMatrix.M[3][3] < 1.0f);
|
|
const bool bIsOrthoXY = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[2][2]) > 0.0f;
|
|
const bool bIsOrthoXZ = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[1][2]) > 0.0f;
|
|
const bool bIsOrthoYZ = !bIsPerspective && FMath::Abs(View->ViewMatrices.ViewMatrix.M[0][2]) > 0.0f;
|
|
|
|
// For local space widgets, we always want to draw all three axis, since they may not be aligned with
|
|
// the orthographic projection anyway.
|
|
const bool bIsLocalSpace = (ViewportClient->GetWidgetCoordSystemSpace() == COORD_Local);
|
|
|
|
EAxisList::Type DrawAxis = EAxisList::None;
|
|
if (bIsOrthoXY)
|
|
{
|
|
DrawAxis = EAxisList::X;
|
|
}
|
|
else if (bIsOrthoXZ)
|
|
{
|
|
DrawAxis = EAxisList::XZ;
|
|
}
|
|
else if (bIsOrthoYZ)
|
|
{
|
|
DrawAxis = EAxisList::Z;
|
|
}
|
|
else if (bIsPerspective)
|
|
{
|
|
// Find the best plane to move on
|
|
const FVector CameraZAxis = View->ViewMatrices.ViewMatrix.GetColumn(2);
|
|
const FVector LargestAxis = CameraZAxis.GetAbs();
|
|
if (LargestAxis.X > LargestAxis.Y)
|
|
{
|
|
if (LargestAxis.Z > LargestAxis.X)
|
|
{
|
|
DrawAxis = EAxisList::X;
|
|
}
|
|
else
|
|
{
|
|
DrawAxis = EAxisList::Z;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (LargestAxis.Y > LargestAxis.Z)
|
|
{
|
|
DrawAxis = EAxisList::XZ;
|
|
}
|
|
else
|
|
{
|
|
DrawAxis = EAxisList::X;
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool bDisabled = IsWidgetDisabled();
|
|
|
|
FVector Scale;
|
|
float UniformScale = View->WorldToScreen(InLocation).W * (4.0f / View->ViewRect.Width() / View->ViewMatrices.ProjMatrix.M[0][0]);
|
|
|
|
if (bIsOrthoXY)
|
|
{
|
|
Scale = FVector(UniformScale, UniformScale, 1.0f);
|
|
}
|
|
else if (bIsOrthoXZ)
|
|
{
|
|
Scale = FVector(UniformScale, 1.0f, UniformScale);
|
|
}
|
|
else if (bIsOrthoYZ)
|
|
{
|
|
Scale = FVector(1.0f, UniformScale, UniformScale);
|
|
}
|
|
else
|
|
{
|
|
Scale = FVector(UniformScale, UniformScale, UniformScale);
|
|
}
|
|
|
|
// Radius
|
|
const float ScaledRadius = (TWOD_AXIS_CIRCLE_RADIUS * UniformScale) + GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment;
|
|
const int32 CircleSides = (GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment > 0)
|
|
? AXIS_CIRCLE_SIDES + (GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment / 5)
|
|
: AXIS_CIRCLE_SIDES;
|
|
|
|
// Draw the grabbers
|
|
if (bDrawWidget)
|
|
{
|
|
FVector CornerPos = FVector(7, 0, 7) * UniformScale;
|
|
FVector AxisSize = FVector(12, 1.2, 12) * UniformScale;
|
|
float CornerLength = 1.2f * UniformScale;
|
|
|
|
// Draw the axis lines with arrow heads
|
|
if (DrawAxis&EAxisList::X && (bIsPerspective || bIsLocalSpace || !bIsOrthoYZ))
|
|
{
|
|
UMaterialInstanceDynamic* XMaterial = (CurrentAxis&EAxisList::X ? CurrentAxisMaterial : AxisMaterialX);
|
|
Render_Axis(View, PDI, EAxisList::X, WidgetMatrix, XMaterial, XColor, XAxisDir, Scale, bDrawWidget);
|
|
}
|
|
|
|
if (DrawAxis&EAxisList::Y && (bIsPerspective || bIsLocalSpace || !bIsOrthoXZ))
|
|
{
|
|
UMaterialInstanceDynamic* YMaterial = (CurrentAxis&EAxisList::Y ? CurrentAxisMaterial : AxisMaterialY);
|
|
Render_Axis(View, PDI, EAxisList::Y, WidgetMatrix, YMaterial, YColor, YAxisDir, Scale, bDrawWidget);
|
|
}
|
|
|
|
if (DrawAxis&EAxisList::Z && (bIsPerspective || bIsLocalSpace || !bIsOrthoXY))
|
|
{
|
|
UMaterialInstanceDynamic* ZMaterial = (CurrentAxis&EAxisList::Z ? CurrentAxisMaterial : AxisMaterialZ);
|
|
Render_Axis(View, PDI, EAxisList::Z, WidgetMatrix, ZMaterial, ZColor, ZAxisDir, Scale, bDrawWidget);
|
|
}
|
|
|
|
// After the primitives have been scaled and transformed, we apply this inverse scale that flattens the dimension
|
|
// that was scaled up to prevent it from intersecting with the near plane. In perspective this won't have any effect,
|
|
// but in the ortho viewports it will prevent scaling in the direction of the camera and thus intersecting the near plane.
|
|
FVector FlattenScale = FVector(Scale.Component(0) == 1.0f ? 1.0f / UniformScale : 1.0f, Scale.Component(1) == 1.0f ? 1.0f / UniformScale : 1.0f, Scale.Component(2) == 1.0f ? 1.0f / UniformScale : 1.0f);
|
|
float ArrowRadius = ScaledRadius * 2.0f;
|
|
float ArrowStartRadius = ScaledRadius * 1.3f;
|
|
|
|
uint8 HoverAlpha = 0xff;
|
|
uint8 NormalAlpha = 0x2f;
|
|
|
|
if (((DrawAxis&EAxisList::XZ) == EAxisList::XZ) && (bIsPerspective || bIsLocalSpace || bIsOrthoXZ)) // Front
|
|
{
|
|
uint8 Alpha = ((CurrentAxis&EAxisList::XZ) == EAxisList::XZ ? HoverAlpha : NormalAlpha);
|
|
PDI->SetHitProxy(new HWidgetAxis(EAxisList::XZ, bDisabled));
|
|
{
|
|
FColor Color = YColor.ToFColor(true);
|
|
DrawCircle(PDI, InLocation, CustomCoordSystem.TransformPosition(FVector(1, 0, 0)), CustomCoordSystem.TransformPosition(FVector(0, 0, 1)), Color, ScaledRadius, CircleSides, SDPG_Foreground);
|
|
Color.A = Alpha;
|
|
DrawDisc(PDI, InLocation, CustomCoordSystem.TransformPosition(FVector(1, 0, 0)), CustomCoordSystem.TransformPosition(FVector(0, 0, 1)), Color, ScaledRadius, CircleSides, TransparentPlaneMaterialXY->GetRenderProxy(false), SDPG_Foreground);
|
|
}
|
|
PDI->SetHitProxy(NULL);
|
|
|
|
PDI->SetHitProxy(new HWidgetAxis(EAxisList::Rotate2D, bDisabled));
|
|
{
|
|
FColor Color = YColor.ToFColor(true);
|
|
Color.A = ((CurrentAxis&EAxisList::Rotate2D) == EAxisList::Rotate2D ? HoverAlpha : NormalAlpha);
|
|
|
|
FVector XAxis = CustomCoordSystem.TransformPosition(FVector(1, 0, 0).RotateAngleAxis((EditorModeTools ? EditorModeTools->TranslateRotate2DAngle : 0), FVector(0, -1, 0)));
|
|
FVector YAxis = CustomCoordSystem.TransformPosition(FVector(0, 0, 1).RotateAngleAxis((EditorModeTools ? EditorModeTools->TranslateRotate2DAngle : 0), FVector(0, -1, 0)));
|
|
FVector BaseArrowPoint = InLocation + XAxis * ArrowStartRadius;
|
|
DrawFlatArrow(PDI, BaseArrowPoint, XAxis, YAxis, Color, ArrowRadius, ArrowRadius*.5f, TransparentPlaneMaterialXY->GetRenderProxy(false), SDPG_Foreground);
|
|
}
|
|
PDI->SetHitProxy(NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts mouse movement on the screen to widget axis movement/rotation.
|
|
*/
|
|
void FWidget::ConvertMouseMovementToAxisMovement( FEditorViewportClient* InViewportClient, bool bInUsedDragModifier, FVector& InOutDelta, FVector& OutDrag, FRotator& OutRotation, FVector& OutScale )
|
|
{
|
|
OutDrag = FVector::ZeroVector;
|
|
OutRotation = FRotator::ZeroRotator;
|
|
OutScale = FVector::ZeroVector;
|
|
|
|
const int32 WidgetMode = InViewportClient->GetWidgetMode();
|
|
|
|
// Get input delta as 2D vector, adjusted for inverted screen space Y axis
|
|
const FVector2D DragDir = FVector2D(InOutDelta.X, -InOutDelta.Y);
|
|
|
|
// Get offset of the drag start position from the widget origin
|
|
const FVector2D DirectionToMousePos = FVector2D(DragStartPos - Origin).GetSafeNormal();
|
|
|
|
// For rotations which display as a full ring, calculate the tangent direction representing a clockwise movement
|
|
FVector2D TangentDir;
|
|
if (bInUsedDragModifier)
|
|
{
|
|
// If a drag modifier has been used, this implies we are not actually touching the widget, so don't attempt to
|
|
// calculate the tangent dir based on the relative offset of the cursor from the widget location.
|
|
TangentDir = FVector2D(1, 1).GetSafeNormal();
|
|
}
|
|
else
|
|
{
|
|
// Treat the tangent dir as perpendicular to the relative offset of the cursor from the widget location.
|
|
TangentDir = FVector2D(-DirectionToMousePos.Y, DirectionToMousePos.X);
|
|
}
|
|
|
|
switch (WidgetMode)
|
|
{
|
|
case WM_Translate:
|
|
{
|
|
// Get drag delta in widget axis space
|
|
OutDrag = FVector(
|
|
(CurrentAxis & EAxisList::X) ? FVector2D::DotProduct(XAxisDir, DragDir) : 0.0f,
|
|
(CurrentAxis & EAxisList::Y) ? FVector2D::DotProduct(YAxisDir, DragDir) : 0.0f,
|
|
(CurrentAxis & EAxisList::Z) ? FVector2D::DotProduct(ZAxisDir, DragDir) : 0.0f
|
|
);
|
|
|
|
// Snap to grid in widget axis space
|
|
const FVector GridSize = FVector(GEditor->GetGridSize());
|
|
FSnappingUtils::SnapPointToGrid(OutDrag, GridSize);
|
|
|
|
// Convert to effective screen space delta, and replace input delta, adjusted for inverted screen space Y axis
|
|
const FVector2D EffectiveDelta = OutDrag.X * XAxisDir + OutDrag.Y * YAxisDir + OutDrag.Z * ZAxisDir;
|
|
InOutDelta = FVector(EffectiveDelta.X, -EffectiveDelta.Y, 0.0f);
|
|
|
|
// Transform drag delta into world space
|
|
OutDrag = CustomCoordSystem.TransformPosition(OutDrag);
|
|
}
|
|
break;
|
|
|
|
case WM_Rotate:
|
|
{
|
|
FRotator Rotation;
|
|
FVector2D EffectiveDelta;
|
|
|
|
if (CurrentAxis == EAxisList::X)
|
|
{
|
|
// Get screen direction representing positive rotation
|
|
const FVector2D AxisDir = bIsOrthoDrawingFullRing ? TangentDir : XAxisDir;
|
|
|
|
// Get rotation in widget local space
|
|
Rotation = FRotator(0, 0, FVector2D::DotProduct(AxisDir, DragDir));
|
|
FSnappingUtils::SnapRotatorToGrid(Rotation);
|
|
|
|
// Record delta rotation (used by the widget to render the accumulated delta)
|
|
CurrentDeltaRotation = Rotation.Roll;
|
|
|
|
// Use to calculate the new input delta
|
|
EffectiveDelta = AxisDir * Rotation.Roll;
|
|
}
|
|
else if (CurrentAxis == EAxisList::Y)
|
|
{
|
|
// TODO: Determine why -TangentDir is necessary here, and fix whatever is causing it
|
|
const FVector2D AxisDir = bIsOrthoDrawingFullRing ? -TangentDir : YAxisDir;
|
|
|
|
Rotation = FRotator(FVector2D::DotProduct(AxisDir, DragDir), 0, 0);
|
|
FSnappingUtils::SnapRotatorToGrid(Rotation);
|
|
|
|
CurrentDeltaRotation = Rotation.Pitch;
|
|
EffectiveDelta = AxisDir * Rotation.Pitch;
|
|
}
|
|
else if (CurrentAxis == EAxisList::Z)
|
|
{
|
|
const FVector2D AxisDir = bIsOrthoDrawingFullRing ? TangentDir : ZAxisDir;
|
|
|
|
Rotation = FRotator(0, FVector2D::DotProduct(AxisDir, DragDir), 0);
|
|
FSnappingUtils::SnapRotatorToGrid(Rotation);
|
|
|
|
CurrentDeltaRotation = Rotation.Yaw;
|
|
EffectiveDelta = AxisDir * Rotation.Yaw;
|
|
}
|
|
|
|
// Adjust the input delta according to how much rotation was actually applied
|
|
InOutDelta = FVector(EffectiveDelta.X, -EffectiveDelta.Y, 0.0f);
|
|
|
|
// Need to get the delta rotation in the current coordinate space of the widget
|
|
OutRotation = (CustomCoordSystem.Inverse() * FRotationMatrix(Rotation) * CustomCoordSystem).Rotator();
|
|
}
|
|
break;
|
|
|
|
case WM_Scale:
|
|
{
|
|
FVector2D AxisDir = FVector2D::ZeroVector;
|
|
|
|
if (CurrentAxis & EAxisList::X)
|
|
{
|
|
AxisDir += XAxisDir;
|
|
}
|
|
|
|
if (CurrentAxis & EAxisList::Y)
|
|
{
|
|
AxisDir += YAxisDir;
|
|
}
|
|
|
|
if (CurrentAxis & EAxisList::Z)
|
|
{
|
|
AxisDir += ZAxisDir;
|
|
}
|
|
|
|
AxisDir.Normalize();
|
|
const float ScaleDelta = FVector2D::DotProduct(AxisDir, DragDir);
|
|
|
|
OutScale = FVector(
|
|
(CurrentAxis & EAxisList::X) ? ScaleDelta : 0.0f,
|
|
(CurrentAxis & EAxisList::Y) ? ScaleDelta : 0.0f,
|
|
(CurrentAxis & EAxisList::Z) ? ScaleDelta : 0.0f
|
|
);
|
|
|
|
// Snap to grid in widget axis space
|
|
const FVector GridSize = FVector(GEditor->GetGridSize());
|
|
FSnappingUtils::SnapScale(OutScale, GridSize);
|
|
|
|
// Convert to effective screen space delta, and replace input delta, adjusted for inverted screen space Y axis
|
|
const float ScaleMax = OutScale.GetMax();
|
|
const float ScaleMin = OutScale.GetMin();
|
|
const float ScaleApplied = (ScaleMax > -ScaleMin) ? ScaleMax : ScaleMin;
|
|
const FVector2D EffectiveDelta = AxisDir * ScaleApplied;
|
|
InOutDelta = FVector(EffectiveDelta.X, -EffectiveDelta.Y, 0.0f);
|
|
}
|
|
break;
|
|
|
|
case WM_TranslateRotateZ:
|
|
{
|
|
if( CurrentAxis == EAxisList::ZRotation )
|
|
{
|
|
|
|
const FVector2D AxisDir = bIsOrthoDrawingFullRing ? TangentDir : ZAxisDir;
|
|
FRotator Rotation = FRotator(0, FVector2D::DotProduct(AxisDir, DragDir), 0);
|
|
FSnappingUtils::SnapRotatorToGrid(Rotation);
|
|
CurrentDeltaRotation = Rotation.Yaw;
|
|
|
|
const FVector2D EffectiveDelta = AxisDir * Rotation.Yaw;
|
|
InOutDelta = FVector(EffectiveDelta.X, -EffectiveDelta.Y, 0.0f);
|
|
|
|
OutRotation = (CustomCoordSystem.Inverse() * FRotationMatrix(Rotation) * CustomCoordSystem).Rotator();
|
|
}
|
|
else
|
|
{
|
|
// Get drag delta in widget axis space
|
|
OutDrag = FVector(
|
|
(CurrentAxis & EAxisList::X) ? FVector2D::DotProduct(XAxisDir, DragDir) : 0.0f,
|
|
(CurrentAxis & EAxisList::Y) ? FVector2D::DotProduct(YAxisDir, DragDir) : 0.0f,
|
|
(CurrentAxis & EAxisList::Z) ? FVector2D::DotProduct(ZAxisDir, DragDir) : 0.0f
|
|
);
|
|
|
|
// Snap to grid in widget axis space
|
|
const FVector GridSize = FVector(GEditor->GetGridSize());
|
|
FSnappingUtils::SnapPointToGrid(OutDrag, GridSize);
|
|
|
|
// Convert to effective screen space delta, and replace input delta, adjusted for inverted screen space Y axis
|
|
const FVector2D EffectiveDelta = OutDrag.X * XAxisDir + OutDrag.Y * YAxisDir + OutDrag.Z * ZAxisDir;
|
|
InOutDelta = FVector(EffectiveDelta.X, -EffectiveDelta.Y, 0.0f);
|
|
|
|
// Transform drag delta into world space
|
|
OutDrag = CustomCoordSystem.TransformPosition(OutDrag);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_2D:
|
|
{
|
|
if (CurrentAxis == EAxisList::Rotate2D)
|
|
{
|
|
// TODO: Determine why -TangentDir is necessary here, and fix whatever is causing it
|
|
const FVector2D AxisDir = bIsOrthoDrawingFullRing ? -TangentDir : YAxisDir;
|
|
|
|
FRotator Rotation = FRotator(FVector2D::DotProduct(AxisDir, DragDir), 0, 0);
|
|
FSnappingUtils::SnapRotatorToGrid(Rotation);
|
|
|
|
CurrentDeltaRotation = Rotation.Pitch;
|
|
FVector2D EffectiveDelta = AxisDir * Rotation.Pitch;
|
|
|
|
|
|
// Adjust the input delta according to how much rotation was actually applied
|
|
InOutDelta = FVector(EffectiveDelta.X, -EffectiveDelta.Y, 0.0f);
|
|
|
|
// Need to get the delta rotation in the current coordinate space of the widget
|
|
OutRotation = (CustomCoordSystem.Inverse() * FRotationMatrix(Rotation) * CustomCoordSystem).Rotator();
|
|
}
|
|
else
|
|
{
|
|
// Get drag delta in widget axis space
|
|
OutDrag = FVector(
|
|
(CurrentAxis & EAxisList::X) ? FVector2D::DotProduct(XAxisDir, DragDir) : 0.0f,
|
|
(CurrentAxis & EAxisList::Y) ? FVector2D::DotProduct(YAxisDir, DragDir) : 0.0f,
|
|
(CurrentAxis & EAxisList::Z) ? FVector2D::DotProduct(ZAxisDir, DragDir) : 0.0f
|
|
);
|
|
|
|
// Snap to grid in widget axis space
|
|
const FVector GridSize = FVector(GEditor->GetGridSize());
|
|
FSnappingUtils::SnapPointToGrid(OutDrag, GridSize);
|
|
|
|
// Convert to effective screen space delta, and replace input delta, adjusted for inverted screen space Y axis
|
|
const FVector2D EffectiveDelta = OutDrag.X * XAxisDir + OutDrag.Y * YAxisDir + OutDrag.Z * ZAxisDir;
|
|
InOutDelta = FVector(EffectiveDelta.X, -EffectiveDelta.Y, 0.0f);
|
|
|
|
// Transform drag delta into world space
|
|
OutDrag = CustomCoordSystem.TransformPosition(OutDrag);
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For axis movement, get the "best" planar normal and axis mask
|
|
* @param InAxis - Axis of movement
|
|
* @param InDirToPixel -
|
|
* @param OutPlaneNormal - Normal of the plane to project the mouse onto
|
|
* @param OutMask - Used to mask out the component of the planar movement we want
|
|
*/
|
|
void GetAxisPlaneNormalAndMask(const FMatrix& InCoordSystem, const FVector& InAxis, const FVector& InDirToPixel, FVector& OutPlaneNormal, FVector& NormalToRemove)
|
|
{
|
|
FVector XAxis = InCoordSystem.TransformVector(FVector(1, 0, 0));
|
|
FVector YAxis = InCoordSystem.TransformVector(FVector(0, 1, 0));
|
|
FVector ZAxis = InCoordSystem.TransformVector(FVector(0, 0, 1));
|
|
|
|
float XDot = FMath::Abs(InDirToPixel | XAxis);
|
|
float YDot = FMath::Abs(InDirToPixel | YAxis);
|
|
float ZDot = FMath::Abs(InDirToPixel | ZAxis);
|
|
|
|
if ((InAxis|XAxis) > .1f)
|
|
{
|
|
OutPlaneNormal = (YDot > ZDot) ? YAxis : ZAxis;
|
|
NormalToRemove = (YDot > ZDot) ? ZAxis : YAxis;
|
|
}
|
|
else if ((InAxis|YAxis) > .1f)
|
|
{
|
|
OutPlaneNormal = (XDot > ZDot) ? XAxis : ZAxis;
|
|
NormalToRemove = (XDot > ZDot) ? ZAxis : XAxis;
|
|
}
|
|
else
|
|
{
|
|
OutPlaneNormal = (XDot > YDot) ? XAxis : YAxis;
|
|
NormalToRemove = (XDot > YDot) ? YAxis : XAxis;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For planar movement, get the "best" planar normal and axis mask
|
|
* @param InAxis - Axis of movement
|
|
* @param OutPlaneNormal - Normal of the plane to project the mouse onto
|
|
* @param OutMask - Used to mask out the component of the planar movement we want
|
|
*/
|
|
void GetPlaneNormalAndMask(const FVector& InAxis, FVector& OutPlaneNormal, FVector& NormalToRemove)
|
|
{
|
|
OutPlaneNormal = InAxis;
|
|
NormalToRemove = InAxis;
|
|
}
|
|
|
|
/**
|
|
* Absolute Translation conversion from mouse movement on the screen to widget axis movement/rotation.
|
|
*/
|
|
void FWidget::AbsoluteTranslationConvertMouseMovementToAxisMovement(FSceneView* InView, FEditorViewportClient* InViewportClient, const FVector& InLocation, const FVector2D& InMousePosition, FVector& OutDrag, FRotator& OutRotation, FVector& OutScale )
|
|
{
|
|
// Compute a world space ray from the screen space mouse coordinates
|
|
FViewportCursorLocation MouseViewportRay( InView, InViewportClient, InMousePosition.X, InMousePosition.Y );
|
|
|
|
FAbsoluteMovementParams Params;
|
|
Params.EyePos = MouseViewportRay.GetOrigin();
|
|
Params.PixelDir = MouseViewportRay.GetDirection();
|
|
Params.CameraDir = InView->GetViewDirection();
|
|
Params.Position = InLocation;
|
|
//dampen by
|
|
Params.bMovementLockedToCamera = InViewportClient->IsShiftPressed();
|
|
Params.bPositionSnapping = true;
|
|
|
|
FMatrix InputCoordSystem = InViewportClient->GetWidgetCoordSystem();
|
|
|
|
Params.XAxis = InputCoordSystem.TransformVector(FVector(1, 0, 0));
|
|
Params.YAxis = InputCoordSystem.TransformVector(FVector(0, 1, 0));
|
|
Params.ZAxis = InputCoordSystem.TransformVector(FVector(0, 0, 1));
|
|
|
|
switch( InViewportClient->GetWidgetMode() )
|
|
{
|
|
case WM_Translate:
|
|
{
|
|
switch( CurrentAxis )
|
|
{
|
|
case EAxisList::X: GetAxisPlaneNormalAndMask(InputCoordSystem, Params.XAxis, Params.CameraDir, Params.PlaneNormal, Params.NormalToRemove); break;
|
|
case EAxisList::Y: GetAxisPlaneNormalAndMask(InputCoordSystem, Params.YAxis, Params.CameraDir, Params.PlaneNormal, Params.NormalToRemove); break;
|
|
case EAxisList::Z: GetAxisPlaneNormalAndMask(InputCoordSystem, Params.ZAxis, Params.CameraDir, Params.PlaneNormal, Params.NormalToRemove); break;
|
|
case EAxisList::XY: GetPlaneNormalAndMask(Params.ZAxis, Params.PlaneNormal, Params.NormalToRemove); break;
|
|
case EAxisList::XZ: GetPlaneNormalAndMask(Params.YAxis, Params.PlaneNormal, Params.NormalToRemove); break;
|
|
case EAxisList::YZ: GetPlaneNormalAndMask(Params.XAxis, Params.PlaneNormal, Params.NormalToRemove); break;
|
|
case EAxisList::Screen:
|
|
Params.XAxis = InView->ViewMatrices.ViewMatrix.GetColumn(0);
|
|
Params.YAxis = InView->ViewMatrices.ViewMatrix.GetColumn(1);
|
|
Params.ZAxis = InView->ViewMatrices.ViewMatrix.GetColumn(2);
|
|
GetPlaneNormalAndMask(Params.ZAxis, Params.PlaneNormal, Params.NormalToRemove); break;
|
|
break;
|
|
}
|
|
|
|
OutDrag = GetAbsoluteTranslationDelta(Params);
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_2D:
|
|
{
|
|
switch (CurrentAxis)
|
|
{
|
|
case EAxisList::X:
|
|
{
|
|
GetAxisPlaneNormalAndMask(InputCoordSystem, Params.XAxis, Params.CameraDir, Params.PlaneNormal, Params.NormalToRemove);
|
|
OutDrag = GetAbsoluteTranslationDelta(Params);
|
|
break;
|
|
}
|
|
case EAxisList::Z:
|
|
{
|
|
GetAxisPlaneNormalAndMask(InputCoordSystem, Params.ZAxis, Params.CameraDir, Params.PlaneNormal, Params.NormalToRemove);
|
|
OutDrag = GetAbsoluteTranslationDelta(Params);
|
|
break;
|
|
}
|
|
case EAxisList::XZ:
|
|
{
|
|
GetPlaneNormalAndMask(Params.YAxis, Params.PlaneNormal, Params.NormalToRemove);
|
|
OutDrag = GetAbsoluteTranslationDelta(Params);
|
|
break;
|
|
}
|
|
|
|
//Rotate about the y-axis
|
|
case EAxisList::Rotate2D:
|
|
{
|
|
//no position snapping, we'll handle the rotation snapping elsewhere
|
|
Params.bPositionSnapping = false;
|
|
|
|
GetPlaneNormalAndMask(Params.YAxis, Params.PlaneNormal, Params.NormalToRemove);
|
|
//No DAMPING
|
|
Params.bMovementLockedToCamera = false;
|
|
//this is the one movement type where we want to always use the widget origin and
|
|
//NOT the "first click" origin
|
|
FVector XZPlaneProjectedPosition = GetAbsoluteTranslationDelta(Params) + InitialTranslationOffset;
|
|
|
|
//remove the component along the normal we want to mute
|
|
float MovementAlongMutedAxis = XZPlaneProjectedPosition | Params.NormalToRemove;
|
|
XZPlaneProjectedPosition = XZPlaneProjectedPosition - (Params.NormalToRemove*MovementAlongMutedAxis);
|
|
|
|
if (!XZPlaneProjectedPosition.Normalize())
|
|
{
|
|
XZPlaneProjectedPosition = Params.YAxis;
|
|
}
|
|
|
|
//NOW, find the rotation around the PlaneNormal to make the xaxis point at InDrag
|
|
OutRotation = FRotator::ZeroRotator;
|
|
|
|
float PitchDegrees = -FMath::Atan2(-XZPlaneProjectedPosition.Z, XZPlaneProjectedPosition.X) * 180.f / PI;
|
|
OutRotation.Pitch = PitchDegrees - (EditorModeTools ? EditorModeTools->TranslateRotate2DAngle : 0);
|
|
|
|
if (bSnapEnabled)
|
|
{
|
|
FSnappingUtils::SnapRotatorToGrid(OutRotation);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_TranslateRotateZ:
|
|
{
|
|
FVector LineToUse;
|
|
switch( CurrentAxis )
|
|
{
|
|
case EAxisList::X:
|
|
{
|
|
GetAxisPlaneNormalAndMask(InputCoordSystem, Params.XAxis, Params.CameraDir, Params.PlaneNormal, Params.NormalToRemove);
|
|
OutDrag = GetAbsoluteTranslationDelta (Params);
|
|
break;
|
|
}
|
|
case EAxisList::Y:
|
|
{
|
|
GetAxisPlaneNormalAndMask(InputCoordSystem, Params.YAxis, Params.CameraDir, Params.PlaneNormal, Params.NormalToRemove);
|
|
OutDrag = GetAbsoluteTranslationDelta (Params);
|
|
break;
|
|
}
|
|
case EAxisList::Z:
|
|
{
|
|
GetAxisPlaneNormalAndMask(InputCoordSystem, Params.ZAxis, Params.CameraDir, Params.PlaneNormal, Params.NormalToRemove);
|
|
OutDrag = GetAbsoluteTranslationDelta (Params);
|
|
break;
|
|
}
|
|
case EAxisList::XY:
|
|
{
|
|
GetPlaneNormalAndMask(Params.ZAxis, Params.PlaneNormal, Params.NormalToRemove);
|
|
OutDrag = GetAbsoluteTranslationDelta (Params);
|
|
break;
|
|
}
|
|
//Rotate about the z-axis
|
|
case EAxisList::ZRotation:
|
|
{
|
|
//no position snapping, we'll handle the rotation snapping elsewhere
|
|
Params.bPositionSnapping = false;
|
|
|
|
//find new point on the
|
|
GetPlaneNormalAndMask(Params.ZAxis, Params.PlaneNormal, Params.NormalToRemove);
|
|
//No DAMPING
|
|
Params.bMovementLockedToCamera = false;
|
|
//this is the one movement type where we want to always use the widget origin and
|
|
//NOT the "first click" origin
|
|
FVector XYPlaneProjectedPosition = GetAbsoluteTranslationDelta (Params) + InitialTranslationOffset;
|
|
|
|
//remove the component along the normal we want to mute
|
|
float MovementAlongMutedAxis = XYPlaneProjectedPosition|Params.NormalToRemove;
|
|
XYPlaneProjectedPosition = XYPlaneProjectedPosition - (Params.NormalToRemove*MovementAlongMutedAxis);
|
|
|
|
if (!XYPlaneProjectedPosition.Normalize())
|
|
{
|
|
XYPlaneProjectedPosition = Params.XAxis;
|
|
}
|
|
|
|
//NOW, find the rotation around the PlaneNormal to make the xaxis point at InDrag
|
|
OutRotation = FRotator::ZeroRotator;
|
|
|
|
OutRotation.Yaw = XYPlaneProjectedPosition.Rotation().Yaw - (EditorModeTools ? EditorModeTools->TranslateRotateXAxisAngle : 0 );
|
|
|
|
if (bSnapEnabled)
|
|
{
|
|
FSnappingUtils::SnapRotatorToGrid( OutRotation );
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
case WM_Rotate:
|
|
case WM_Scale:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/** Only some modes support Absolute Translation Movement */
|
|
bool FWidget::AllowsAbsoluteTranslationMovement(EWidgetMode WidgetMode)
|
|
{
|
|
if ((WidgetMode == WM_Translate) || (WidgetMode == WM_TranslateRotateZ) || (WidgetMode == WM_2D))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Serializes the widget references so they don't get garbage collected.
|
|
*
|
|
* @param Ar FArchive to serialize with
|
|
*/
|
|
void FWidget::AddReferencedObjects( FReferenceCollector& Collector )
|
|
{
|
|
Collector.AddReferencedObject( AxisMaterialX );
|
|
Collector.AddReferencedObject( AxisMaterialY );
|
|
Collector.AddReferencedObject( AxisMaterialZ );
|
|
Collector.AddReferencedObject( OpaquePlaneMaterialXY );
|
|
Collector.AddReferencedObject( TransparentPlaneMaterialXY );
|
|
Collector.AddReferencedObject( GridMaterial );
|
|
Collector.AddReferencedObject( CurrentAxisMaterial );
|
|
}
|
|
|
|
#define CAMERA_LOCK_DAMPING_FACTOR .1f
|
|
#define MAX_CAMERA_MOVEMENT_SPEED 512.0f
|
|
/**
|
|
* Returns the Delta from the current position that the absolute movement system wants the object to be at
|
|
* @param InParams - Structure containing all the information needed for absolute movement
|
|
* @return - The requested delta from the current position
|
|
*/
|
|
FVector FWidget::GetAbsoluteTranslationDelta(const FAbsoluteMovementParams& InParams)
|
|
{
|
|
FPlane MovementPlane(InParams.Position, InParams.PlaneNormal);
|
|
FVector ProposedEndofEyeVector = InParams.EyePos + (InParams.PixelDir * (InParams.Position - InParams.EyePos).Size());
|
|
|
|
//default to not moving
|
|
FVector RequestedPosition = InParams.Position;
|
|
|
|
float DotProductWithPlaneNormal = InParams.PixelDir|InParams.PlaneNormal;
|
|
//check to make sure we're not co-planar
|
|
if (FMath::Abs(DotProductWithPlaneNormal) > DELTA)
|
|
{
|
|
//Get closest point on plane
|
|
RequestedPosition = FMath::LinePlaneIntersection(InParams.EyePos, ProposedEndofEyeVector, MovementPlane);
|
|
}
|
|
|
|
//drag is a delta position, so just update the different between the previous position and the new position
|
|
FVector DeltaPosition = RequestedPosition - InParams.Position;
|
|
|
|
//Retrieve the initial offset, passing in the current requested position and the current position
|
|
FVector InitialOffset = GetAbsoluteTranslationInitialOffset(RequestedPosition, InParams.Position);
|
|
|
|
//subtract off the initial offset (where the widget was clicked) to prevent popping
|
|
DeltaPosition -= InitialOffset;
|
|
|
|
//remove the component along the normal we want to mute
|
|
float MovementAlongMutedAxis = DeltaPosition|InParams.NormalToRemove;
|
|
FVector OutDrag = DeltaPosition - (InParams.NormalToRemove*MovementAlongMutedAxis);
|
|
|
|
if (InParams.bMovementLockedToCamera)
|
|
{
|
|
//DAMPEN ABSOLUTE MOVEMENT when the camera is locked to the object
|
|
OutDrag *= CAMERA_LOCK_DAMPING_FACTOR;
|
|
OutDrag.X = FMath::Clamp(OutDrag.X, -MAX_CAMERA_MOVEMENT_SPEED, MAX_CAMERA_MOVEMENT_SPEED);
|
|
OutDrag.Y = FMath::Clamp(OutDrag.Y, -MAX_CAMERA_MOVEMENT_SPEED, MAX_CAMERA_MOVEMENT_SPEED);
|
|
OutDrag.Z = FMath::Clamp(OutDrag.Z, -MAX_CAMERA_MOVEMENT_SPEED, MAX_CAMERA_MOVEMENT_SPEED);
|
|
}
|
|
|
|
//the they requested position snapping and we're not moving with the camera
|
|
if (InParams.bPositionSnapping && !InParams.bMovementLockedToCamera && bSnapEnabled)
|
|
{
|
|
FVector MovementAlongAxis = FVector(OutDrag|InParams.XAxis, OutDrag|InParams.YAxis, OutDrag|InParams.ZAxis);
|
|
//translation (either xy plane or z)
|
|
FSnappingUtils::SnapPointToGrid( MovementAlongAxis, FVector(GEditor->GetGridSize(),GEditor->GetGridSize(),GEditor->GetGridSize()) );
|
|
OutDrag = MovementAlongAxis.X*InParams.XAxis + MovementAlongAxis.Y*InParams.YAxis + MovementAlongAxis.Z*InParams.ZAxis;
|
|
}
|
|
|
|
//get the distance from the original position to the new proposed position
|
|
FVector DeltaFromStart = InParams.Position + OutDrag - InitialTranslationPosition;
|
|
|
|
//Get the vector from the eye to the proposed new position (to make sure it's not behind the camera
|
|
FVector EyeToNewPosition = (InParams.Position + OutDrag) - InParams.EyePos;
|
|
float BehindTheCameraDotProduct = EyeToNewPosition|InParams.CameraDir;
|
|
|
|
//Don't let the requested position go behind the camera
|
|
if ( BehindTheCameraDotProduct <= 0 )
|
|
{
|
|
OutDrag = OutDrag.ZeroVector;
|
|
}
|
|
return OutDrag;
|
|
}
|
|
|
|
/**
|
|
* Returns the offset from the initial selection point
|
|
*/
|
|
FVector FWidget::GetAbsoluteTranslationInitialOffset(const FVector& InNewPosition, const FVector& InCurrentPosition)
|
|
{
|
|
if (!bAbsoluteTranslationInitialOffsetCached)
|
|
{
|
|
bAbsoluteTranslationInitialOffsetCached = true;
|
|
InitialTranslationOffset = InNewPosition - InCurrentPosition;
|
|
InitialTranslationPosition = InCurrentPosition;
|
|
}
|
|
return InitialTranslationOffset;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Returns true if we're in Local Space editing mode
|
|
*/
|
|
bool FWidget::IsRotationLocalSpace() const
|
|
{
|
|
return ( CustomCoordSystemSpace == COORD_Local );
|
|
}
|
|
|
|
void FWidget::UpdateDeltaRotation()
|
|
{
|
|
TotalDeltaRotation += CurrentDeltaRotation;
|
|
if ( (TotalDeltaRotation <= -360.f) || (TotalDeltaRotation >= 360.f) )
|
|
{
|
|
TotalDeltaRotation = FRotator::ClampAxis(TotalDeltaRotation);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the angle in degrees representation of how far we have just rotated
|
|
*/
|
|
float FWidget::GetDeltaRotation() const
|
|
{
|
|
return TotalDeltaRotation;
|
|
}
|
|
|
|
|
|
uint8 LargeInnerAlpha = 0x3f;
|
|
uint8 SmallInnerAlpha = 0x0f;
|
|
uint8 LargeOuterAlpha = 0x7f;
|
|
uint8 SmallOuterAlpha = 0x0f;
|
|
|
|
/**
|
|
* If actively dragging, draws a ring representing the potential rotation of the selected objects, snap ticks, and "delta" markers
|
|
* If not actively dragging, draws a quarter ring representing the closest quadrant to the camera
|
|
* @param View - Information about the scene/camera/etc
|
|
* @param PDI - Drawing interface
|
|
* @param InAxis - Enumeration of axis to rotate about
|
|
* @param InLocation - The Origin of the widget
|
|
* @param Axis0 - The Axis that describes a 0 degree rotation
|
|
* @param Axis1 - The Axis that describes a 90 degree rotation
|
|
* @param InDirectionToWidget - Direction from camera to the widget
|
|
* @param InColor - The color associated with the axis of rotation
|
|
* @param InScale - Multiplier to maintain a constant screen size for rendering the widget
|
|
*/
|
|
void FWidget::DrawRotationArc(const FSceneView* View, FPrimitiveDrawInterface* PDI, EAxisList::Type InAxis, const FVector& InLocation, const FVector& Axis0, const FVector& Axis1, const FVector& InDirectionToWidget, const FColor& InColor, const float InScale, FVector2D& OutAxisDir)
|
|
{
|
|
bool bIsPerspective = ( View->ViewMatrices.ProjMatrix.M[3][3] < 1.0f );
|
|
bool bIsOrtho = !bIsPerspective;
|
|
|
|
//if we're in an ortho viewport and the ring is perpendicular to the camera (both Axis0 & Axis1 are perpendicular)
|
|
bIsOrthoDrawingFullRing |= bIsOrtho && (FMath::Abs(Axis0|InDirectionToWidget) < KINDA_SMALL_NUMBER) && (FMath::Abs(Axis1|InDirectionToWidget) < KINDA_SMALL_NUMBER);
|
|
|
|
FColor ArcColor = InColor;
|
|
ArcColor.A = LargeOuterAlpha;
|
|
|
|
if (bDragging || (bIsOrthoDrawingFullRing))
|
|
{
|
|
if ((CurrentAxis&InAxis) || (bIsOrthoDrawingFullRing))
|
|
{
|
|
float DeltaRotation = GetDeltaRotation();
|
|
float AdjustedDeltaRotation = IsRotationLocalSpace() ? -DeltaRotation : DeltaRotation;
|
|
float AbsRotation = FRotator::ClampAxis(FMath::Abs(DeltaRotation));
|
|
float AngleOfChangeRadians (AbsRotation * PI / 180.f);
|
|
|
|
//always draw clockwise, so if we're negative we need to flip the angle
|
|
float StartAngle = AdjustedDeltaRotation < 0.0f ? -AngleOfChangeRadians : 0.0f;
|
|
float FilledAngle = AngleOfChangeRadians;
|
|
|
|
//the axis of rotation
|
|
FVector ZAxis = Axis0 ^ Axis1;
|
|
|
|
ArcColor.A = LargeOuterAlpha;
|
|
DrawPartialRotationArc(View, PDI, InAxis, InLocation, Axis0, Axis1, StartAngle, StartAngle + FilledAngle, ArcColor, InScale, InDirectionToWidget);
|
|
ArcColor.A = SmallOuterAlpha;
|
|
DrawPartialRotationArc(View, PDI, InAxis, InLocation, Axis0, Axis1, StartAngle + FilledAngle, StartAngle + 2*PI, ArcColor, InScale, InDirectionToWidget);
|
|
|
|
ArcColor = (CurrentAxis&InAxis) ? CurrentColor : ArcColor;
|
|
//Hallow Arrow
|
|
ArcColor.A = 0;
|
|
DrawStartStopMarker(PDI, InLocation, Axis0, Axis1, 0, ArcColor, InScale);
|
|
//Filled Arrow
|
|
ArcColor.A = LargeOuterAlpha;
|
|
DrawStartStopMarker(PDI, InLocation, Axis0, Axis1, AdjustedDeltaRotation, ArcColor, InScale);
|
|
|
|
ArcColor.A = 255;
|
|
|
|
FVector SnapLocation = InLocation;
|
|
|
|
if (GetDefault<ULevelEditorViewportSettings>()->RotGridEnabled)
|
|
{
|
|
float DeltaAngle = GEditor->GetRotGridSize().Yaw;
|
|
//every 22.5 degrees
|
|
float TickMarker = 22.5f;
|
|
for (float Angle = 0; Angle < 360.f; Angle+=DeltaAngle)
|
|
{
|
|
FVector GridAxis = Axis0.RotateAngleAxis(Angle, ZAxis);
|
|
float PercentSize = (FMath::Fmod(Angle, TickMarker)==0) ? .75f : .25f;
|
|
if (FMath::Fmod(Angle, 90.f) != 0)
|
|
{
|
|
DrawSnapMarker(PDI, SnapLocation, GridAxis, FVector::ZeroVector, ArcColor, InScale, 0.0f, PercentSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
//draw axis tick marks
|
|
FColor AxisColor = InColor;
|
|
//Rotate Colors to match Axis 0
|
|
Swap(AxisColor.R, AxisColor.G);
|
|
Swap(AxisColor.B, AxisColor.R);
|
|
AxisColor.A = (AdjustedDeltaRotation == 0) ? MAX_uint8 : LargeOuterAlpha;
|
|
DrawSnapMarker(PDI, SnapLocation, Axis0, Axis1, AxisColor, InScale, .25f);
|
|
AxisColor.A = (AdjustedDeltaRotation == 180.f) ? MAX_uint8 : LargeOuterAlpha;
|
|
DrawSnapMarker(PDI, SnapLocation, -Axis0, -Axis1, AxisColor, InScale, .25f);
|
|
|
|
//Rotate Colors to match Axis 1
|
|
Swap(AxisColor.R, AxisColor.G);
|
|
Swap(AxisColor.B, AxisColor.R);
|
|
AxisColor.A = (AdjustedDeltaRotation == 90.f) ? MAX_uint8 : LargeOuterAlpha;
|
|
DrawSnapMarker(PDI, SnapLocation, Axis1, -Axis0, AxisColor, InScale, .25f);
|
|
AxisColor.A = (AdjustedDeltaRotation == 270.f) ? MAX_uint8 : LargeOuterAlpha;
|
|
DrawSnapMarker(PDI, SnapLocation, -Axis1, Axis0, AxisColor, InScale, .25f);
|
|
|
|
if (bDragging)
|
|
{
|
|
float OffsetAngle = IsRotationLocalSpace() ? 0 : AdjustedDeltaRotation;
|
|
|
|
CacheRotationHUDText(View, PDI, InLocation, Axis0.RotateAngleAxis(OffsetAngle, ZAxis), Axis1.RotateAngleAxis(OffsetAngle, ZAxis), DeltaRotation, InScale);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Reverse the axes based on camera view
|
|
bool bMirrorAxis0 = ((Axis0 | InDirectionToWidget) <= 0.0f);
|
|
bool bMirrorAxis1 = ((Axis1 | InDirectionToWidget) <= 0.0f);
|
|
|
|
FVector RenderAxis0 = bMirrorAxis0 ? Axis0 : -Axis0;
|
|
FVector RenderAxis1 = bMirrorAxis1 ? Axis1 : -Axis1;
|
|
float Direction = (bMirrorAxis0 ^ bMirrorAxis1) ? -1.0f : 1.0f;
|
|
|
|
DrawPartialRotationArc(View, PDI, InAxis, InLocation, RenderAxis0, RenderAxis1, 0, PI/2, ArcColor, InScale, InDirectionToWidget);
|
|
|
|
FVector2D Axis0ScreenLocation;
|
|
if (!View->ScreenToPixel(View->WorldToScreen(InLocation + RenderAxis0 * 64.0f), Axis0ScreenLocation))
|
|
{
|
|
Axis0ScreenLocation.X = Axis0ScreenLocation.Y = 0;
|
|
}
|
|
|
|
FVector2D Axis1ScreenLocation;
|
|
if (!View->ScreenToPixel(View->WorldToScreen(InLocation + RenderAxis1 * 64.0f), Axis1ScreenLocation))
|
|
{
|
|
Axis1ScreenLocation.X = Axis1ScreenLocation.Y = 0;
|
|
}
|
|
|
|
OutAxisDir = ((Axis1ScreenLocation - Axis0ScreenLocation) * Direction).GetSafeNormal();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If actively dragging, draws a ring representing the potential rotation of the selected objects, snap ticks, and "delta" markers
|
|
* If not actively dragging, draws a quarter ring representing the closest quadrant to the camera
|
|
* @param View - Information about the scene/camera/etc
|
|
* @param PDI - Drawing interface
|
|
* @param InAxis - Enumeration of axis to rotate about
|
|
* @param InLocation - The Origin of the widget
|
|
* @param Axis0 - The Axis that describes a 0 degree rotation
|
|
* @param Axis1 - The Axis that describes a 90 degree rotation
|
|
* @param InStartAngle - The starting angle about (Axis0^Axis1) to render the arc, in radians
|
|
* @param InEndAngle - The ending angle about (Axis0^Axis1) to render the arc, in radians
|
|
* @param InColor - The color associated with the axis of rotation
|
|
* @param InScale - Multiplier to maintain a constant screen size for rendering the widget
|
|
*/
|
|
void FWidget::DrawPartialRotationArc(const FSceneView* View, FPrimitiveDrawInterface* PDI, EAxisList::Type InAxis, const FVector& InLocation, const FVector& Axis0, const FVector& Axis1, const float InStartAngle, const float InEndAngle, const FColor& InColor, const float InScale, const FVector& InDirectionToWidget )
|
|
{
|
|
const float InnerRadius = (INNER_AXIS_CIRCLE_RADIUS * InScale) + GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment;
|
|
const float OuterRadius = (OUTER_AXIS_CIRCLE_RADIUS * InScale) + GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment;
|
|
|
|
bool bIsPerspective = ( View->ViewMatrices.ProjMatrix.M[3][3] < 1.0f );
|
|
PDI->SetHitProxy( new HWidgetAxis( InAxis ) );
|
|
{
|
|
FThickArcParams OuterArcParams(PDI, InLocation, TransparentPlaneMaterialXY, InnerRadius, OuterRadius);
|
|
FColor OuterColor = ( CurrentAxis&InAxis ? CurrentColor : InColor );
|
|
//Pass through alpha
|
|
OuterColor.A = InColor.A;
|
|
DrawThickArc(OuterArcParams, Axis0, Axis1, InStartAngle, InEndAngle, OuterColor, InDirectionToWidget, !bIsPerspective );
|
|
}
|
|
PDI->SetHitProxy( NULL );
|
|
|
|
const bool bIsHitProxyView = View->Family->EngineShowFlags.HitProxies;
|
|
if (bIsPerspective && !bIsHitProxyView && !PDI->IsHitTesting())
|
|
{
|
|
FThickArcParams InnerArcParams(PDI, InLocation, GridMaterial, 0.0f, InnerRadius);
|
|
FColor InnerColor = InColor;
|
|
//if something is selected and it's not this
|
|
InnerColor.A = ((CurrentAxis & InAxis) && !bDragging) ? LargeInnerAlpha : SmallInnerAlpha;
|
|
DrawThickArc(InnerArcParams, Axis0, Axis1, InStartAngle, InEndAngle, InnerColor, InDirectionToWidget, false );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renders a portion of an arc for the rotation widget
|
|
* @param InParams - Material, Radii, etc
|
|
* @param InStartAxis - Start of the arc, in radians
|
|
* @param InEndAxis - End of the arc, in radians
|
|
* @param InColor - Color to use for the arc
|
|
*/
|
|
void FWidget::DrawThickArc (const FThickArcParams& InParams, const FVector& Axis0, const FVector& Axis1, const float InStartAngle, const float InEndAngle, const FColor& InColor, const FVector& InDirectionToWidget, bool bIsOrtho )
|
|
{
|
|
if (InColor.A == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Add more sides to the circle if we've been scaled up to keep the circle looking circular
|
|
// An extra side for every 5 extra unreal units seems to produce a nice result
|
|
const int32 CircleSides = (GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment > 0)
|
|
? AXIS_CIRCLE_SIDES + (GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment / 5)
|
|
: AXIS_CIRCLE_SIDES;
|
|
const int32 NumPoints = FMath::TruncToInt(CircleSides * (InEndAngle-InStartAngle)/(PI/2)) + 1;
|
|
|
|
FColor TriangleColor = InColor;
|
|
FColor RingColor = InColor;
|
|
RingColor.A = MAX_uint8;
|
|
|
|
FVector ZAxis = Axis0 ^ Axis1;
|
|
FVector LastVertex;
|
|
|
|
FDynamicMeshBuilder MeshBuilder;
|
|
|
|
for (int32 RadiusIndex = 0; RadiusIndex < 2; ++RadiusIndex)
|
|
{
|
|
float Radius = (RadiusIndex == 0) ? InParams.OuterRadius : InParams.InnerRadius;
|
|
float TCRadius = Radius / (float) InParams.OuterRadius;
|
|
//Compute vertices for base circle.
|
|
for(int32 VertexIndex = 0;VertexIndex <= NumPoints;VertexIndex++)
|
|
{
|
|
float Percent = VertexIndex/(float)NumPoints;
|
|
float Angle = FMath::Lerp(InStartAngle, InEndAngle, Percent);
|
|
float AngleDeg = FRotator::ClampAxis(Angle * 180.f / PI);
|
|
|
|
FVector VertexDir = Axis0.RotateAngleAxis(AngleDeg, ZAxis);
|
|
VertexDir.Normalize();
|
|
|
|
float TCAngle = Percent*(PI/2);
|
|
FVector2D TC(TCRadius*FMath::Cos(Angle), TCRadius*FMath::Sin(Angle));
|
|
|
|
const FVector VertexPosition = InParams.Position + VertexDir*Radius;
|
|
FVector Normal = VertexPosition - InParams.Position;
|
|
Normal.Normalize();
|
|
|
|
FDynamicMeshVertex MeshVertex;
|
|
MeshVertex.Position = VertexPosition;
|
|
MeshVertex.Color = TriangleColor;
|
|
MeshVertex.TextureCoordinate = TC;
|
|
|
|
MeshVertex.SetTangents(
|
|
-ZAxis,
|
|
(-ZAxis) ^ Normal,
|
|
Normal
|
|
);
|
|
|
|
MeshBuilder.AddVertex(MeshVertex); //Add bottom vertex
|
|
|
|
// Push out the arc line borders so they dont z-fight with the mesh arcs
|
|
FVector StartLinePos = LastVertex;
|
|
FVector EndLinePos = VertexPosition;
|
|
if (VertexIndex != 0)
|
|
{
|
|
InParams.PDI->DrawLine(StartLinePos,EndLinePos,RingColor,SDPG_Foreground);
|
|
}
|
|
LastVertex = VertexPosition;
|
|
}
|
|
}
|
|
|
|
//Add top/bottom triangles, in the style of a fan.
|
|
int32 InnerVertexStartIndex = NumPoints + 1;
|
|
for(int32 VertexIndex = 0; VertexIndex < NumPoints; VertexIndex++)
|
|
{
|
|
MeshBuilder.AddTriangle(VertexIndex, VertexIndex+1, InnerVertexStartIndex+VertexIndex);
|
|
MeshBuilder.AddTriangle(VertexIndex+1, InnerVertexStartIndex+VertexIndex+1, InnerVertexStartIndex+VertexIndex);
|
|
}
|
|
|
|
MeshBuilder.Draw(InParams.PDI, FMatrix::Identity, InParams.Material->GetRenderProxy(false),SDPG_Foreground,0.f);
|
|
}
|
|
|
|
/**
|
|
* Draws protractor like ticks where the rotation widget would snap too.
|
|
* Also, used to draw the wider axis tick marks
|
|
* @param PDI - Drawing interface
|
|
* @param InLocation - The Origin of the widget
|
|
* @param Axis0 - The Axis that describes a 0 degree rotation
|
|
* @param Axis1 - The Axis that describes a 90 degree rotation
|
|
* @param InAngle - The Angle to rotate about the axis of rotation, the vector (Axis0 ^ Axis1)
|
|
* @param InColor - The color to use for line/poly drawing
|
|
* @param InScale - Multiplier to maintain a constant screen size for rendering the widget
|
|
* @param InWidthPercent - The percent of the distance between the outer ring and inner ring to use for tangential thickness
|
|
* @param InPercentSize - The percent of the distance between the outer ring and inner ring to use for radial distance
|
|
*/
|
|
void FWidget::DrawSnapMarker(FPrimitiveDrawInterface* PDI, const FVector& InLocation, const FVector& Axis0, const FVector& Axis1, const FColor& InColor, const float InScale, const float InWidthPercent, const float InPercentSize)
|
|
{
|
|
const float InnerDistance = (INNER_AXIS_CIRCLE_RADIUS * InScale) + GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment;
|
|
const float OuterDistance = (OUTER_AXIS_CIRCLE_RADIUS * InScale) + GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment;
|
|
const float MaxMarkerHeight = OuterDistance - InnerDistance;
|
|
const float MarkerWidth = MaxMarkerHeight*InWidthPercent;
|
|
const float MarkerHeight = MaxMarkerHeight*InPercentSize;
|
|
|
|
FVector Vertices[4];
|
|
Vertices[0] = InLocation + (OuterDistance)*Axis0 - (MarkerWidth*.5)*Axis1;
|
|
Vertices[1] = Vertices[0] + (MarkerWidth)*Axis1;
|
|
Vertices[2] = InLocation + (OuterDistance-MarkerHeight)*Axis0 - (MarkerWidth*.5)*Axis1;
|
|
Vertices[3] = Vertices[2] + (MarkerWidth)*Axis1;
|
|
|
|
//draw at least one line
|
|
PDI->DrawLine(Vertices[0], Vertices[2], InColor, SDPG_Foreground);
|
|
|
|
//if there should be thickness, draw the other lines
|
|
if (InWidthPercent > 0.0f)
|
|
{
|
|
PDI->DrawLine(Vertices[0], Vertices[1], InColor, SDPG_Foreground);
|
|
PDI->DrawLine(Vertices[1], Vertices[3], InColor, SDPG_Foreground);
|
|
PDI->DrawLine(Vertices[2], Vertices[3], InColor, SDPG_Foreground);
|
|
|
|
//fill in the box
|
|
FDynamicMeshBuilder MeshBuilder;
|
|
|
|
for(int32 VertexIndex = 0;VertexIndex < 4; VertexIndex++)
|
|
{
|
|
FDynamicMeshVertex MeshVertex;
|
|
MeshVertex.Position = Vertices[VertexIndex];
|
|
MeshVertex.Color = InColor;
|
|
MeshVertex.TextureCoordinate = FVector2D(0.0f, 0.0f);
|
|
MeshVertex.SetTangents(
|
|
Axis0,
|
|
Axis1,
|
|
(Axis0) ^ Axis1
|
|
);
|
|
MeshBuilder.AddVertex(MeshVertex); //Add bottom vertex
|
|
}
|
|
|
|
MeshBuilder.AddTriangle(0, 1, 2);
|
|
MeshBuilder.AddTriangle(1, 3, 2);
|
|
MeshBuilder.Draw(PDI, FMatrix::Identity, TransparentPlaneMaterialXY->GetRenderProxy(false),SDPG_Foreground,0.f);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draw Start/Stop Marker to show delta rotations along the arc of rotation
|
|
* @param PDI - Drawing interface
|
|
* @param InLocation - The Origin of the widget
|
|
* @param Axis0 - The Axis that describes a 0 degree rotation
|
|
* @param Axis1 - The Axis that describes a 90 degree rotation
|
|
* @param InAngle - The Angle to rotate about the axis of rotation, the vector (Axis0 ^ Axis1), units are degrees
|
|
* @param InColor - The color to use for line/poly drawing
|
|
* @param InScale - Multiplier to maintain a constant screen size for rendering the widget
|
|
*/
|
|
void FWidget::DrawStartStopMarker(FPrimitiveDrawInterface* PDI, const FVector& InLocation, const FVector& Axis0, const FVector& Axis1, const float InAngle, const FColor& InColor, const float InScale)
|
|
{
|
|
const float ArrowHeightPercent = .8f;
|
|
const float InnerDistance = (INNER_AXIS_CIRCLE_RADIUS * InScale) + GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment;
|
|
const float OuterDistance = (OUTER_AXIS_CIRCLE_RADIUS * InScale) + GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment;
|
|
const float RingHeight = OuterDistance - InnerDistance;
|
|
const float ArrowHeight = RingHeight*ArrowHeightPercent;
|
|
const float ThirtyDegrees = PI / 6.0f;
|
|
const float HalfArrowidth = ArrowHeight*FMath::Tan(ThirtyDegrees);
|
|
|
|
FVector ZAxis = Axis0 ^ Axis1;
|
|
FVector RotatedAxis0 = Axis0.RotateAngleAxis(InAngle, ZAxis);
|
|
FVector RotatedAxis1 = Axis1.RotateAngleAxis(InAngle, ZAxis);
|
|
|
|
FVector Vertices[3];
|
|
Vertices[0] = InLocation + (OuterDistance)*RotatedAxis0;
|
|
Vertices[1] = Vertices[0] + (ArrowHeight)*RotatedAxis0 - HalfArrowidth*RotatedAxis1;
|
|
Vertices[2] = Vertices[1] + (2*HalfArrowidth)*RotatedAxis1;
|
|
|
|
PDI->DrawLine(Vertices[0], Vertices[1], InColor, SDPG_Foreground);
|
|
PDI->DrawLine(Vertices[1], Vertices[2], InColor, SDPG_Foreground);
|
|
PDI->DrawLine(Vertices[0], Vertices[2], InColor, SDPG_Foreground);
|
|
|
|
if (InColor.A > 0)
|
|
{
|
|
//fill in the box
|
|
FDynamicMeshBuilder MeshBuilder;
|
|
|
|
for(int32 VertexIndex = 0;VertexIndex < 3; VertexIndex++)
|
|
{
|
|
FDynamicMeshVertex MeshVertex;
|
|
MeshVertex.Position = Vertices[VertexIndex];
|
|
MeshVertex.Color = InColor;
|
|
MeshVertex.TextureCoordinate = FVector2D(0.0f, 0.0f);
|
|
MeshVertex.SetTangents(
|
|
RotatedAxis0,
|
|
RotatedAxis1,
|
|
(RotatedAxis0) ^ RotatedAxis1
|
|
);
|
|
MeshBuilder.AddVertex(MeshVertex); //Add bottom vertex
|
|
}
|
|
|
|
MeshBuilder.AddTriangle(0, 1, 2);
|
|
MeshBuilder.Draw(PDI, FMatrix::Identity, TransparentPlaneMaterialXY->GetRenderProxy(false),SDPG_Foreground,0.f);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Caches off HUD text to display after 3d rendering is complete
|
|
* @param View - Information about the scene/camera/etc
|
|
* @param PDI - Drawing interface
|
|
* @param InLocation - The Origin of the widget
|
|
* @param Axis0 - The Axis that describes a 0 degree rotation
|
|
* @param Axis1 - The Axis that describes a 90 degree rotation
|
|
* @param AngleOfAngle - angle we've rotated so far (in degrees)
|
|
*/
|
|
void FWidget::CacheRotationHUDText(const FSceneView* View, FPrimitiveDrawInterface* PDI, const FVector& InLocation, const FVector& Axis0, const FVector& Axis1, const float AngleOfChange, const float InScale)
|
|
{
|
|
const float TextDistance = (ROTATION_TEXT_RADIUS * InScale) + GetDefault<ULevelEditorViewportSettings>()->TransformWidgetSizeAdjustment;
|
|
|
|
FVector AxisVectors[4] = { Axis0, Axis1, -Axis0, -Axis1};
|
|
|
|
for (int i = 0 ; i < 4; ++i)
|
|
{
|
|
FVector PotentialTextPosition = InLocation + (TextDistance)*AxisVectors[i];
|
|
if(View->ScreenToPixel(View->WorldToScreen(PotentialTextPosition), HUDInfoPos))
|
|
{
|
|
if (FMath::IsWithin<float>(HUDInfoPos.X, 0, View->ViewRect.Width()) && FMath::IsWithin<float>(HUDInfoPos.Y, 0, View->ViewRect.Height()))
|
|
{
|
|
//only valid screen locations get a valid string
|
|
HUDString = FString::Printf(TEXT("%3.2f"), AngleOfChange);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32 FWidget::GetDominantAxisIndex( const FVector& InDiff, FEditorViewportClient* ViewportClient ) const
|
|
{
|
|
uint32 DominantIndex = 0;
|
|
if( FMath::Abs(InDiff.X) < FMath::Abs(InDiff.Y) )
|
|
{
|
|
DominantIndex = 1;
|
|
}
|
|
|
|
const int32 WidgetMode = ViewportClient->GetWidgetMode();
|
|
|
|
switch( WidgetMode )
|
|
{
|
|
case WM_Translate:
|
|
switch( ViewportClient->ViewportType )
|
|
{
|
|
case LVT_OrthoXY:
|
|
if( CurrentAxis == EAxisList::X )
|
|
{
|
|
DominantIndex = 0;
|
|
}
|
|
else if( CurrentAxis == EAxisList::Y )
|
|
{
|
|
DominantIndex = 1;
|
|
}
|
|
break;
|
|
case LVT_OrthoXZ:
|
|
if( CurrentAxis == EAxisList::X )
|
|
{
|
|
DominantIndex = 0;
|
|
}
|
|
else if( CurrentAxis == EAxisList::Z )
|
|
{
|
|
DominantIndex = 1;
|
|
}
|
|
break;
|
|
case LVT_OrthoYZ:
|
|
if( CurrentAxis == EAxisList::Y )
|
|
{
|
|
DominantIndex = 0;
|
|
}
|
|
else if( CurrentAxis == EAxisList::Z )
|
|
{
|
|
DominantIndex = 1;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return DominantIndex;
|
|
}
|
|
|
|
|
|
EAxisList::Type FWidget::GetAxisToDraw( EWidgetMode WidgetMode ) const
|
|
{
|
|
return EditorModeTools ? EditorModeTools->GetWidgetAxisToDraw( WidgetMode ) : EAxisList::All;
|
|
}
|
|
|
|
bool FWidget::IsWidgetDisabled() const
|
|
{
|
|
return EditorModeTools ? (EditorModeTools->IsDefaultModeActive() && GEditor->HasLockedActors()) : false;
|
|
}
|
|
|