void FSCSEditorViewportClient::DrawCanvas( FViewport& InViewport, FSceneView& View, FCanvas& Canvas ) { AActor* PreviewActor = GetPreviewActor(); if(PreviewActor) { TGuardValue<bool> AutoRestore(GAllowActorScriptExecutionInEditor, true); const int32 HalfX = 0.5f * Viewport->GetSizeXY().X; const int32 HalfY = 0.5f * Viewport->GetSizeXY().Y; auto SelectedNodes = BlueprintEditorPtr.Pin()->GetSelectedSCSEditorTreeNodes(); if(bIsManipulating && SelectedNodes.Num() > 0) { USceneComponent* SceneComp = Cast<USceneComponent>(SelectedNodes[0]->FindComponentInstanceInActor(PreviewActor, true)); if(SceneComp) { const FVector WidgetLocation = GetWidgetLocation(); const FPlane Proj = View.Project(WidgetLocation); if(Proj.W > 0.0f) { const int32 XPos = HalfX + (HalfX * Proj.X); const int32 YPos = HalfY + (HalfY * (Proj.Y * -1)); DrawAngles(&Canvas, XPos, YPos, GetCurrentWidgetAxis(), GetWidgetMode(), GetWidgetCoordSystem().Rotator(), WidgetLocation); } } } } }
/** * Used to cycle widget modes */ void FEditorModeTools::CycleWidgetMode (void) { //make sure we're not currently tracking mouse movement. If we are, changing modes could cause a crash due to referencing an axis/plane that is incompatible with the widget for(int32 ViewportIndex = 0;ViewportIndex < GEditor->LevelViewportClients.Num();ViewportIndex++) { FEditorViewportClient* ViewportClient = GEditor->LevelViewportClients[ ViewportIndex ]; if (ViewportClient->IsTracking()) { return; } } //only cycle when the mode is requesting the drawing of a widget if( GetShowWidget() ) { const int32 CurrentWk = GetWidgetMode(); int32 Wk = CurrentWk; do { Wk++; if ((Wk == FWidget::WM_TranslateRotateZ) && (!GetDefault<ULevelEditorViewportSettings>()->bAllowTranslateRotateZWidget)) { Wk++; } // Roll back to the start if we go past FWidget::WM_Scale if( Wk >= FWidget::WM_Max) { Wk -= FWidget::WM_Max; } } while (!UsesTransformWidget((FWidget::EWidgetMode)Wk) && Wk != CurrentWk); SetWidgetMode( (FWidget::EWidgetMode)Wk ); FEditorSupportDelegates::RedrawAllViewports.Broadcast(); } }
ECoordSystem FEditorModeTools::GetCoordSystem(bool bGetRawValue) { if (!bGetRawValue && GetWidgetMode() == FWidget::WM_Scale ) { return COORD_Local; } else { return CoordSystem; } }
void FSCSEditorViewportClient::DrawCanvas( FViewport& InViewport, FSceneView& View, FCanvas& Canvas ) { AActor* PreviewActor = GetPreviewActor(); if(PreviewActor) { if (GUnrealEd != NULL) { TArray<FSCSEditorTreeNodePtrType> SelectedNodes = BlueprintEditorPtr.Pin()->GetSelectedSCSEditorTreeNodes(); for (int32 SelectionIndex = 0; SelectionIndex < SelectedNodes.Num(); ++SelectionIndex) { FSCSEditorTreeNodePtrType SelectedNode = SelectedNodes[SelectionIndex]; UActorComponent* Comp = Cast<USceneComponent>(SelectedNode->FindComponentInstanceInActor(PreviewActor)); if (Comp != NULL && Comp->IsRegistered()) { // Try and find a visualizer TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(Comp->GetClass()); if (Visualizer.IsValid()) { Visualizer->DrawVisualizationHUD(Comp, &InViewport, &View, &Canvas); } } } } TGuardValue<bool> AutoRestore(GAllowActorScriptExecutionInEditor, true); const int32 HalfX = 0.5f * Viewport->GetSizeXY().X; const int32 HalfY = 0.5f * Viewport->GetSizeXY().Y; auto SelectedNodes = BlueprintEditorPtr.Pin()->GetSelectedSCSEditorTreeNodes(); if(bIsManipulating && SelectedNodes.Num() > 0) { USceneComponent* SceneComp = Cast<USceneComponent>(SelectedNodes[0]->FindComponentInstanceInActor(PreviewActor)); if(SceneComp) { const FVector WidgetLocation = GetWidgetLocation(); const FPlane Proj = View.Project(WidgetLocation); if(Proj.W > 0.0f) { const int32 XPos = HalfX + (HalfX * Proj.X); const int32 YPos = HalfY + (HalfY * (Proj.Y * -1)); DrawAngles(&Canvas, XPos, YPos, GetCurrentWidgetAxis(), GetWidgetMode(), GetWidgetCoordSystem().Rotator(), WidgetLocation); } } } } }
ECoordSystem FPhATEdPreviewViewportClient::GetWidgetCoordSystemSpace() const { return GetWidgetMode() == FWidget::WM_Scale ? COORD_Local : ModeTools->GetCoordSystem();; }
bool FPhATEdPreviewViewportClient::InputWidgetDelta( FViewport* InViewport, EAxisList::Type CurrentAxis, FVector& Drag, FRotator& Rot, FVector& Scale ) { bool bHandled = false; TArray<FPhATSharedData::FSelection> & SelectedObjects = SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit ? SharedData->SelectedBodies : SharedData->SelectedConstraints; for(int32 i=0; i<SelectedObjects.Num(); ++i) { FPhATSharedData::FSelection & SelectedObject = SelectedObjects[i]; if( SharedData->bManipulating ) { float BoneScale = 1.f; if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) /// BODY EDITING /// { int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(SharedData->PhysicsAsset->SkeletalBodySetups[SelectedObject.Index]->BoneName); FTransform BoneTM = SharedData->EditorSkelComp->GetBoneTransform(BoneIndex); BoneScale = BoneTM.GetScale3D().GetAbsMax(); BoneTM.RemoveScaling(); SelectedObject.WidgetTM = SharedData->EditorSkelComp->GetPrimitiveTransform(BoneTM, SelectedObject.Index, SelectedObject.PrimitiveType, SelectedObject.PrimitiveIndex, BoneScale); } else /// CONSTRAINT EDITING /// { SelectedObject.WidgetTM = SharedData->GetConstraintMatrix(SelectedObject.Index, EConstraintFrame::Frame2, 1.f); } if ( GetWidgetMode() == FWidget::WM_Translate ) { FVector Dir = SelectedObject.WidgetTM.InverseTransformVector( Drag.GetSafeNormal() ); FVector DragVec = Dir * Drag.Size() / BoneScale; SelectedObject.ManipulateTM.AddToTranslation( DragVec ); } else if ( GetWidgetMode() == FWidget::WM_Rotate ) { FVector Axis; float Angle; Rot.Quaternion().ToAxisAndAngle(Axis, Angle); Axis = SelectedObject.WidgetTM.InverseTransformVectorNoScale( Axis ); const FQuat Start = SelectedObject.ManipulateTM.GetRotation(); const FQuat Delta = FQuat( Axis, Angle ); const FQuat Result = Delta * Start; SelectedObject.ManipulateTM = FTransform( Result ); } else if ( GetWidgetMode() == FWidget::WM_Scale && SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) // Scaling only valid for bodies. { ModifyPrimitiveSize(SelectedObject.Index, SelectedObject.PrimitiveType, SelectedObject.PrimitiveIndex, Scale ); } if (SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit) { UPhysicsConstraintTemplate* ConstraintSetup = SharedData->PhysicsAsset->ConstraintSetup[SelectedObject.Index]; ConstraintSetup->DefaultInstance.SetRefFrame(EConstraintFrame::Frame2, SelectedObject.ManipulateTM * StartManParentConTM); //Rotation by default only rotates one frame, but translation by default moves both bool bMultiFrame = (IsAltPressed() && GetWidgetMode() == FWidget::WM_Rotate) || (!IsAltPressed() && GetWidgetMode() == FWidget::WM_Translate); if (bMultiFrame) { SharedData->SetSelectedConstraintRelTM(StartManRelConTM); } else { ConstraintSetup->DefaultInstance.SetRefFrame(EConstraintFrame::Frame1, FTransform(StartManChildConTM)); } } bHandled = true; } } return bHandled; }
void FPhATEdPreviewViewportClient::DrawCanvas( FViewport& InViewport, FSceneView& View, FCanvas& Canvas ) { if (!PhATPtr.IsValid()) { return; } // Turn on/off the ground box SharedData->EditorFloorComp->SetVisibility(SharedData->bDrawGround); float W, H; PhATFont->GetCharSize(TEXT('L'), W, H); const float XOffset = 5.0f; const float YOffset = 32.0f; FCanvasTextItem TextItem( FVector2D::ZeroVector, FText::GetEmpty(), PhATFont, FLinearColor::White ); // Write body/constraint count at top. FString StatusString = FText::Format( NSLOCTEXT("UnrealEd", "BodiesConstraints_F", "{0} Bodies {1} Considered for bounds {2} Ratio {3} Constraints"), FText::AsNumber(SharedData->PhysicsAsset->SkeletalBodySetups.Num()), FText::AsNumber(SharedData->PhysicsAsset->BoundsBodies.Num()), FText::AsNumber(static_cast<float>(SharedData->PhysicsAsset->BoundsBodies.Num())/static_cast<float>(SharedData->PhysicsAsset->SkeletalBodySetups.Num())), FText::AsNumber(SharedData->PhysicsAsset->ConstraintSetup.Num()) ).ToString(); TextItem.Text = FText::FromString( StatusString ); Canvas.DrawItem( TextItem, XOffset, YOffset); TextItem.Text = FText::GetEmpty(); if (SharedData->bRunningSimulation) { #if PLATFORM_MAC TextItem.Text = NSLOCTEXT("UnrealEd", "Sim_Mac", "SIM: Command+RightMouse to interact with bodies"); #else TextItem.Text = NSLOCTEXT("UnrealEd", "Sim", "SIM: Ctrl+RightMouse to interact with bodies"); #endif } else if (SharedData->bSelectionLock) { TextItem.Text = NSLOCTEXT("UnrealEd", "Lock", "LOCK"); }else if(SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit) { if(GetWidgetMode() == FWidget::WM_Translate) { TextItem.Text = NSLOCTEXT("UnrealEd", "SingleMove", "hold ALT to move a single reference frame"); }else if(GetWidgetMode() == FWidget::WM_Rotate) { TextItem.Text = NSLOCTEXT("UnrealEd", "DoubleRotate", "hold ALT to rotate both reference frames"); } } Canvas.DrawItem( TextItem, XOffset, Viewport->GetSizeXY().Y - (3 + H) ); // Draw current physics weight if (SharedData->bRunningSimulation) { FString PhysWeightString = FString::Printf(TEXT("Phys Blend: %3.0f pct"), SharedData->EditorSimOptions->PhysicsBlend * 100.f); int32 PWLW, PWLH; StringSize(PhATFont, PWLW, PWLH, *PhysWeightString); TextItem.Text = FText::FromString(PhysWeightString); Canvas.DrawItem( TextItem, Viewport->GetSizeXY().X - (3 + PWLW + 2*W), Viewport->GetSizeXY().Y - (3 + H) ); } int32 HalfX = (Viewport->GetSizeXY().X-XOffset)/2; int32 HalfY = Viewport->GetSizeXY().Y/2; if ((SharedData->bShowHierarchy && SharedData->EditorSimOptions->bShowNamesInHierarchy)) { // Iterate over each graphics bone. for (int32 i = 0; i < SharedData->EditorSkelComp->GetNumComponentSpaceTransforms(); ++i) { FVector BonePos = SharedData->EditorSkelComp->ComponentToWorld.TransformPosition(SharedData->EditorSkelComp->GetComponentSpaceTransforms()[i].GetLocation()); FPlane proj = View.Project(BonePos); if (proj.W > 0.f) // This avoids drawing bone names that are behind us. { int32 XPos = HalfX + (HalfX * proj.X); int32 YPos = HalfY + (HalfY * (proj.Y * -1)); FName BoneName = SharedData->EditorSkelMesh->RefSkeleton.GetBoneName(i); FColor BoneNameColor = FColor::White; //iterate through selected bones and see if any match for(int32 j=0; j< SharedData->SelectedBodies.Num(); ++j) { int32 SelectedBodyIndex = SharedData->SelectedBodies[j].Index; FName SelectedBoneName = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedBodyIndex]->BoneName; if(SelectedBoneName == BoneName) { BoneNameColor = FColor::Green; break; } } if (Canvas.IsHitTesting()) { Canvas.SetHitProxy(new HPhATEdBoneNameProxy(i)); } TextItem.Text = FText::FromString(BoneName.ToString()); TextItem.SetColor(BoneNameColor); Canvas.DrawItem( TextItem, XPos, YPos ); if (Canvas.IsHitTesting()) { Canvas.SetHitProxy(NULL); } } } } // If showing center-of-mass, and physics is started up.. if (SharedData->bShowCOM) { // iterate over each bone for (int32 i = 0; i <SharedData->EditorSkelComp->Bodies.Num(); ++i) { FBodyInstance* BodyInst = SharedData->EditorSkelComp->Bodies[i]; check(BodyInst); FVector BodyCOMPos = BodyInst->GetCOMPosition(); float BodyMass = BodyInst->GetBodyMass(); FPlane Projection = View.Project(BodyCOMPos); if (Projection.W > 0.f) // This avoids drawing bone names that are behind us. { int32 XPos = HalfX + (HalfX * Projection.X); int32 YPos = HalfY + (HalfY * (Projection.Y * -1)); FString COMString = FString::Printf(TEXT("%3.3f"), BodyMass); TextItem.Text = FText::FromString(COMString); TextItem.SetColor(SharedData->COMRenderColor); Canvas.DrawItem( TextItem, XPos, YPos ); } } } }
int32 UAnimGraphNode_SkeletalControlBase::ChangeToNextWidgetMode(const USkeletalMeshComponent* SkelComp, int32 CurWidgetMode) { return GetWidgetMode(SkelComp); }
void FStaticMeshEditorViewportClient::DrawCanvas( FViewport& InViewport, FSceneView& View, FCanvas& Canvas ) { #if TODO_STATICMESH if ( StaticMesh->bHasBeenSimplified && SimplygonLogo && SimplygonLogo->Resource ) { const float LogoSizeX = 64.0f; const float LogoSizeY = 40.65f; const float Padding = 6.0f; const float LogoX = Viewport->GetSizeXY().X - Padding - LogoSizeX; const float LogoY = Viewport->GetSizeXY().Y - Padding - LogoSizeY; Canvas->DrawTile( LogoX, LogoY, LogoSizeX, LogoSizeY, 0.0f, 0.0f, 1.0f, 1.0f, FLinearColor::White, SimplygonLogo->Resource, SE_BLEND_Opaque ); } #endif // #if TODO_STATICMESH auto StaticMeshEditor = StaticMeshEditorPtr.Pin(); auto StaticMeshEditorViewport = StaticMeshEditorViewportPtr.Pin(); if (!StaticMeshEditor.IsValid() || !StaticMeshEditorViewport.IsValid()) { return; } const int32 HalfX = Viewport->GetSizeXY().X/2; const int32 HalfY = Viewport->GetSizeXY().Y/2; // Draw socket names if desired. if( bShowSockets ) { for(int32 i=0; i<StaticMesh->Sockets.Num(); i++) { UStaticMeshSocket* Socket = StaticMesh->Sockets[i]; if(Socket!=NULL) { FMatrix SocketTM; Socket->GetSocketMatrix(SocketTM, StaticMeshComponent); const FVector SocketPos = SocketTM.GetOrigin(); const FPlane proj = View.Project( SocketPos ); if(proj.W > 0.f) { const int32 XPos = HalfX + ( HalfX * proj.X ); const int32 YPos = HalfY + ( HalfY * (proj.Y * -1) ); FCanvasTextItem TextItem( FVector2D( XPos, YPos ), FText::FromString( Socket->SocketName.ToString() ), GEngine->GetSmallFont(), FLinearColor(FColor(255,196,196)) ); Canvas.DrawItem( TextItem ); const UStaticMeshSocket* SelectedSocket = StaticMeshEditor->GetSelectedSocket(); if (bManipulating && SelectedSocket == Socket) { //Figure out the text height FTextSizingParameters Parameters(GEngine->GetSmallFont(), 1.0f, 1.0f); UCanvas::CanvasStringSize(Parameters, *Socket->SocketName.ToString()); int32 YL = FMath::TruncToInt(Parameters.DrawYL); DrawAngles(&Canvas, XPos, YPos + YL, Widget->GetCurrentAxis(), GetWidgetMode(), Socket->RelativeRotation, Socket->RelativeLocation); } } } } } TArray<SStaticMeshEditorViewport::FOverlayTextItem> TextItems; int32 CurrentLODLevel = StaticMeshEditor->GetCurrentLODLevel(); if (CurrentLODLevel == 0) { CurrentLODLevel = ComputeStaticMeshLOD(StaticMesh->RenderData, StaticMeshComponent->Bounds.Origin, StaticMeshComponent->Bounds.SphereRadius, View); } else { CurrentLODLevel -= 1; } TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "LOD_F", "LOD: {0}"), FText::AsNumber(CurrentLODLevel)))); float CurrentScreenSize = ComputeBoundsScreenSize(StaticMeshComponent->Bounds.Origin, StaticMeshComponent->Bounds.SphereRadius, View); FNumberFormattingOptions FormatOptions; FormatOptions.MinimumFractionalDigits = 3; FormatOptions.MaximumFractionalDigits = 6; FormatOptions.MaximumIntegralDigits = 6; TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "ScreenSize_F", "Current Screen Size: {0}"), FText::AsNumber(CurrentScreenSize, &FormatOptions)))); TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "Triangles_F", "Triangles: {0}"), FText::AsNumber(StaticMeshEditorPtr.Pin()->GetNumTriangles(CurrentLODLevel))))); TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "Vertices_F", "Vertices: {0}"), FText::AsNumber(StaticMeshEditorPtr.Pin()->GetNumVertices(CurrentLODLevel))))); TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "UVChannels_F", "UV Channels: {0}"), FText::AsNumber(StaticMeshEditorPtr.Pin()->GetNumUVChannels(CurrentLODLevel))))); if( StaticMesh->RenderData->LODResources.Num() > 0 ) { if (StaticMesh->RenderData->LODResources[0].DistanceFieldData != nullptr ) { const FDistanceFieldVolumeData& VolumeData = *(StaticMesh->RenderData->LODResources[0].DistanceFieldData); if (VolumeData.Size.GetMax() > 0) { float MemoryMb = (VolumeData.Size.X * VolumeData.Size.Y * VolumeData.Size.Z * VolumeData.DistanceFieldVolume.GetTypeSize()) / (1024.0f * 1024.0f); FNumberFormattingOptions NumberOptions; NumberOptions.MinimumFractionalDigits = 2; NumberOptions.MaximumFractionalDigits = 2; if (VolumeData.bMeshWasClosed) { TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "DistanceFieldRes_F", "Distance Field: {0}x{1}x{2} = {3}Mb"), FText::AsNumber(VolumeData.Size.X), FText::AsNumber(VolumeData.Size.Y), FText::AsNumber(VolumeData.Size.Z), FText::AsNumber(MemoryMb, &NumberOptions)))); } else { TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( NSLOCTEXT("UnrealEd", "DistanceFieldClosed_F", "Distance Field: Mesh was not closed and material was one-sided"))); } } } } TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "ApproxSize_F", "Approx Size: {0}x{1}x{2}"), FText::AsNumber(int32(StaticMesh->GetBounds().BoxExtent.X * 2.0f)), // x2 as artists wanted length not radius FText::AsNumber(int32(StaticMesh->GetBounds().BoxExtent.Y * 2.0f)), FText::AsNumber(int32(StaticMesh->GetBounds().BoxExtent.Z * 2.0f))))); // Show the number of collision primitives if we are drawing collision. if(bShowCollision && StaticMesh->BodySetup) { TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "NumPrimitives_F", "Num Primitives: {0}"), FText::AsNumber(StaticMesh->BodySetup->AggGeom.GetElementCount())))); } StaticMeshEditorViewport->PopulateOverlayText(TextItems); if(bDrawUVs) { const int32 YPos = 160; DrawUVsForMesh(Viewport, &Canvas, YPos); } }
void FStaticMeshEditorViewportClient::TrackingStarted( const struct FInputEventState& InInputState, bool bIsDraggingWidget, bool bNudge ) { if( !bManipulating && bIsDraggingWidget ) { const UStaticMeshSocket* SelectedSocket = StaticMeshEditorPtr.Pin()->GetSelectedSocket(); if (SelectedSocket) { FText TransText; if( GetWidgetMode() == FWidget::WM_Rotate ) { TransText = LOCTEXT("FStaticMeshEditorViewportClient_RotateSocket", "Rotate Socket"); } else if (GetWidgetMode() == FWidget::WM_Translate) { if( InInputState.IsLeftMouseButtonPressed() && (Widget->GetCurrentAxis() & EAxisList::XYZ) ) { const bool bAltDown = InInputState.IsAltButtonPressed(); if ( bAltDown ) { // Rather than moving/rotating the selected socket, copy it and move the copy instead StaticMeshEditorPtr.Pin()->DuplicateSelectedSocket(); } } TransText = LOCTEXT("FStaticMeshEditorViewportClient_TranslateSocket", "Translate Socket"); } if (!TransText.IsEmpty()) { GEditor->BeginTransaction(TransText); } } const bool bSelectedPrim = StaticMeshEditorPtr.Pin()->HasSelectedPrims(); if (bSelectedPrim) { FText TransText; if (GetWidgetMode() == FWidget::WM_Rotate) { TransText = LOCTEXT("FStaticMeshEditorViewportClient_RotateCollision", "Rotate Collision"); } else if (GetWidgetMode() == FWidget::WM_Scale) { TransText = LOCTEXT("FStaticMeshEditorViewportClient_ScaleCollision", "Scale Collision"); } else if (GetWidgetMode() == FWidget::WM_Translate) { if (InInputState.IsLeftMouseButtonPressed() && (Widget->GetCurrentAxis() & EAxisList::XYZ)) { const bool bAltDown = InInputState.IsAltButtonPressed(); if (bAltDown) { // Rather than moving/rotating the selected primitives, copy them and move the copies instead StaticMeshEditorPtr.Pin()->DuplicateSelectedPrims(NULL); } } TransText = LOCTEXT("FStaticMeshEditorViewportClient_TranslateCollision", "Translate Collision"); } if (!TransText.IsEmpty()) { GEditor->BeginTransaction(TransText); if (StaticMesh->BodySetup) { StaticMesh->BodySetup->Modify(); } } } bManipulating = true; } }
bool FStaticMeshEditorViewportClient::InputWidgetDelta( FViewport* Viewport, EAxisList::Type CurrentAxis, FVector& Drag, FRotator& Rot, FVector& Scale ) { bool bHandled = false; if (bManipulating) { if (CurrentAxis != EAxisList::None) { UStaticMeshSocket* SelectedSocket = StaticMeshEditorPtr.Pin()->GetSelectedSocket(); if(SelectedSocket) { UProperty* ChangedProperty = NULL; const FWidget::EWidgetMode MoveMode = GetWidgetMode(); if(MoveMode == FWidget::WM_Rotate) { ChangedProperty = FindField<UProperty>( UStaticMeshSocket::StaticClass(), "RelativeRotation" ); SelectedSocket->PreEditChange(ChangedProperty); FRotator CurrentRot = SelectedSocket->RelativeRotation; FRotator SocketWinding, SocketRotRemainder; CurrentRot.GetWindingAndRemainder(SocketWinding, SocketRotRemainder); const FQuat ActorQ = SocketRotRemainder.Quaternion(); const FQuat DeltaQ = Rot.Quaternion(); const FQuat ResultQ = DeltaQ * ActorQ; const FRotator NewSocketRotRem = FRotator( ResultQ ); FRotator DeltaRot = NewSocketRotRem - SocketRotRemainder; DeltaRot.Normalize(); SelectedSocket->RelativeRotation += DeltaRot; SelectedSocket->RelativeRotation = SelectedSocket->RelativeRotation.Clamp(); } else if(MoveMode == FWidget::WM_Translate) { ChangedProperty = FindField<UProperty>( UStaticMeshSocket::StaticClass(), "RelativeLocation" ); SelectedSocket->PreEditChange(ChangedProperty); //FRotationMatrix SocketRotTM( SelectedSocket->RelativeRotation ); //FVector SocketMove = SocketRotTM.TransformVector( Drag ); SelectedSocket->RelativeLocation += Drag; } if ( ChangedProperty ) { FPropertyChangedEvent PropertyChangedEvent( ChangedProperty ); SelectedSocket->PostEditChangeProperty(PropertyChangedEvent); } StaticMeshEditorPtr.Pin()->GetStaticMesh()->MarkPackageDirty(); } else { const bool bSelectedPrim = StaticMeshEditorPtr.Pin()->HasSelectedPrims(); if (bSelectedPrim && CurrentAxis != EAxisList::None) { const FWidget::EWidgetMode MoveMode = GetWidgetMode(); if (MoveMode == FWidget::WM_Rotate) { StaticMeshEditorPtr.Pin()->RotateSelectedPrims(Rot); } else if (MoveMode == FWidget::WM_Scale) { StaticMeshEditorPtr.Pin()->ScaleSelectedPrims(Scale); } else if (MoveMode == FWidget::WM_Translate) { StaticMeshEditorPtr.Pin()->TranslateSelectedPrims(Drag); } StaticMeshEditorPtr.Pin()->GetStaticMesh()->MarkPackageDirty(); } } } Invalidate(); bHandled = true; } return bHandled; }