// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "PhATModule.h" #include "PhATEdSkeletalMeshComponent.h" #include "PhAT.h" #include "MouseDeltaTracker.h" #include "ScopedTransaction.h" #include "PhATHitProxies.h" #include "PhATActions.h" #include "PhATSharedData.h" #include "PhATPreviewViewportClient.h" #include "SPhATPreviewViewport.h" #include "GameFramework/WorldSettings.h" #include "CanvasTypes.h" #include "PhysicsEngine/BodySetup.h" #include "Engine/Font.h" #include "PhysicsEngine/PhysicsConstraintTemplate.h" #include "PhysicsEngine/PhysicsSettings.h" #include "PhysicsEngine/PhysicsHandleComponent.h" #include "DrawDebugHelpers.h" FPhATEdPreviewViewportClient::FPhATEdPreviewViewportClient(TWeakPtr InPhAT, TSharedPtr Data, const TSharedRef& InPhATPreviewViewport) : FEditorViewportClient(nullptr, &Data->PreviewScene, StaticCastSharedRef(InPhATPreviewViewport)) , PhATPtr(InPhAT) , SharedData(Data) , MinPrimSize(0.5f) , PhAT_TranslateSpeed(0.25f) , PhAT_RotateSpeed(1.0 * (PI / 180.0)) , PhAT_LightRotSpeed(0.22f) , SimGrabCheckDistance(5000.0f) , SimHoldDistanceChangeDelta(20.0f) , SimMinHoldDistance(10.0f) , SimGrabMoveSpeed(1.0f) { check(PhATPtr.IsValid()); ModeTools->SetWidgetMode(FWidget::EWidgetMode::WM_Translate); ModeTools->SetCoordSystem(COORD_Local); bAllowedToMoveCamera = true; // Setup defaults for the common draw helper. DrawHelper.bDrawPivot = false; DrawHelper.bDrawWorldBox = false; DrawHelper.bDrawKillZ = false; DrawHelper.GridColorAxis = FColor(80,80,80); DrawHelper.GridColorMajor = FColor(72,72,72); DrawHelper.GridColorMinor = FColor(64,64,64); DrawHelper.PerspectiveGridSize = 32767; PhATFont = GEngine->GetSmallFont(); check(PhATFont); EngineShowFlags.DisableAdvancedFeatures(); EngineShowFlags.CompositeEditorPrimitives = true; // Get actors asset collision bounding box, and move actor so its not intersection the floor plane at Z = 0. FBox CollBox = SharedData->PhysicsAsset->CalcAABB(SharedData->EditorSkelComp, SharedData->EditorSkelComp->ComponentToWorld); FVector SkelCompLocation = FVector(0, 0, -CollBox.Min.Z + SharedData->EditorSimOptions->FloorGap); SharedData->EditorSkelComp->SetAbsolute(true, true, true); SharedData->EditorSkelComp->SetRelativeLocation(SkelCompLocation); SharedData->ResetTM = SharedData->EditorSkelComp->GetComponentToWorld(); // Get new bounding box and set view based on that. CollBox = SharedData->PhysicsAsset->CalcAABB(SharedData->EditorSkelComp, SharedData->EditorSkelComp->ComponentToWorld); FVector CollBoxExtent = CollBox.GetExtent(); // Take into account internal mesh translation/rotation/scaling etc. FTransform LocalToWorld = SharedData->EditorSkelComp->ComponentToWorld; FSphere WorldSphere = SharedData->EditorSkelMesh->Bounds.GetSphere().TransformBy(LocalToWorld); CollBoxExtent = CollBox.GetExtent(); if (CollBoxExtent.X > CollBoxExtent.Y) { SetViewLocation( FVector(WorldSphere.Center.X, WorldSphere.Center.Y - 1.5*WorldSphere.W, WorldSphere.Center.Z) ); SetViewRotation( EditorViewportDefs::DefaultPerspectiveViewRotation ); } else { SetViewLocation( FVector(WorldSphere.Center.X - 1.5*WorldSphere.W, WorldSphere.Center.Y, WorldSphere.Center.Z) ); SetViewRotation( FRotator::ZeroRotator ); } SetViewLocationForOrbiting(FVector::ZeroVector); SetViewModes(VMI_Lit, VMI_Lit); bUsingOrbitCamera = true; if (!FPhAT::IsPIERunning()) { SetRealtime(true); } } FPhATEdPreviewViewportClient::~FPhATEdPreviewViewportClient() { } 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 = 200.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->BodySetup.Num()), FText::AsNumber(SharedData->PhysicsAsset->BoundsBodies.Num()), FText::AsNumber(static_cast(SharedData->PhysicsAsset->BoundsBodies.Num())/static_cast(SharedData->PhysicsAsset->BodySetup.Num())), FText::AsNumber(SharedData->PhysicsAsset->ConstraintSetup.Num()) ).ToString(); TextItem.Text = FText::FromString( StatusString ); Canvas.DrawItem( TextItem, XOffset, 3 ); 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 EditorSkelComp->GetNumSpaceBases(); ++i) { FVector BonePos = SharedData->EditorSkelComp->ComponentToWorld.TransformPosition(SharedData->EditorSkelComp->GetSpaceBases()[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->BodySetup[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 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 ); } } } } void FPhATEdPreviewViewportClient::Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI) { FEditorViewportClient::Draw(View,PDI); FPhATSharedData::EPhATRenderMode MeshViewMode = SharedData->GetCurrentMeshViewMode(); if (MeshViewMode != FPhATSharedData::PRM_None) { SharedData->EditorSkelComp->SetVisibility(true); if (MeshViewMode == FPhATSharedData::PRM_Wireframe) { SharedData->EditorSkelComp->SetForceWireframe(true); } else { SharedData->EditorSkelComp->SetForceWireframe(false); } } else { SharedData->EditorSkelComp->SetVisibility(false); } // Draw phat skeletal component. if (PDI->IsHitTesting()) { SharedData->EditorSkelComp->RenderHitTest(View, PDI); } else { SharedData->EditorSkelComp->Render(View, PDI); } } bool FPhATEdPreviewViewportClient::InputKey(FViewport* Viewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed, bool Gamepad) { int32 HitX = Viewport->GetMouseX(); int32 HitY = Viewport->GetMouseY(); bool bCtrlDown = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl); bool bShiftDown = Viewport->KeyState(EKeys::LeftShift) || Viewport->KeyState(EKeys::RightShift); bool bHandled = false; if (SharedData->bRunningSimulation) { if (Key == EKeys::RightMouseButton || Key == EKeys::LeftMouseButton) { if (Event == IE_Pressed) { if (bShiftDown) { bHandled = true; SimMousePress(Viewport, false, Key); bAllowedToMoveCamera = false; } else if (bCtrlDown) { bHandled = true; SimMousePress(Viewport, true, Key); bAllowedToMoveCamera = false; } } else if (Event == IE_Released) { bHandled = true; SimMouseRelease(); bAllowedToMoveCamera = true; } UpdateAndApplyCursorVisibility(); } else if (Key == EKeys::MouseScrollUp) { bHandled = true; SimMouseWheelUp(); } else if (Key == EKeys::MouseScrollDown) { bHandled = true; SimMouseWheelDown(); } else if(IsFlightCameraActive()) { // If the flight camera is active (user is looking or moving around the scene) // consume the event so hotkeys don't fire. bHandled = true; } } if( !bHandled ) { bHandled = FEditorViewportClient::InputKey( Viewport, ControllerId, Key, Event, AmountDepressed, Gamepad ); } if( bHandled ) { Invalidate(); } return bHandled; } bool FPhATEdPreviewViewportClient::InputAxis(FViewport* Viewport, int32 ControllerId, FKey Key, float Delta, float DeltaTime, int32 NumSamples, bool bGamepad) { bool bHandled = false; // If we are 'manipulating' don't move the camera but do something else with mouse input. if (SharedData->bManipulating) { bool bCtrlDown = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl); if (SharedData->bRunningSimulation) { if (Key == EKeys::MouseX) { SimMouseMove(Delta, 0.0f); } else if (Key == EKeys::MouseY) { SimMouseMove(0.0f, Delta); } bHandled = true; } } if( !bHandled ) { bHandled = FEditorViewportClient::InputAxis(Viewport,ControllerId,Key,Delta,DeltaTime,NumSamples,bGamepad); } Viewport->Invalidate(); return bHandled; } void FPhATEdPreviewViewportClient::ProcessClick(class FSceneView& View, class HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY) { if( Key == EKeys::LeftMouseButton ) { if (HitProxy && HitProxy->IsA(HPhATEdBoneProxy::StaticGetType())) { HPhATEdBoneProxy* BoneProxy = (HPhATEdBoneProxy*)HitProxy; SharedData->HitBone(BoneProxy->BodyIndex, BoneProxy->PrimType, BoneProxy->PrimIndex, IsCtrlPressed() || IsShiftPressed()); } else if (HitProxy && HitProxy->IsA(HPhATEdConstraintProxy::StaticGetType())) { HPhATEdConstraintProxy* ConstraintProxy = (HPhATEdConstraintProxy*)HitProxy; SharedData->HitConstraint(ConstraintProxy->ConstraintIndex, IsCtrlPressed() || IsShiftPressed()); } else { HitNothing(); } } else if( Key == EKeys::RightMouseButton ) { if (HitProxy) { if (HitProxy->IsA(HPhATEdBoneProxy::StaticGetType()) && SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) { HPhATEdBoneProxy* BoneProxy = (HPhATEdBoneProxy*)HitProxy; bool bAlreadySelected = false; //find out if body is already selected for(int32 i=0; iSelectedBodies.Num(); ++i) { if(SharedData->SelectedBodies[i].Index == BoneProxy->BodyIndex && SharedData->SelectedBodies[i].PrimitiveType == BoneProxy->PrimType && SharedData->SelectedBodies[i].PrimitiveIndex == BoneProxy->PrimIndex) { bAlreadySelected = true; break; } } // Select body under cursor if not already selected (if ctrl is held down we only add, not remove) if (!bAlreadySelected) { FPhATSharedData::FSelection Selection(BoneProxy->BodyIndex, BoneProxy->PrimType, BoneProxy->PrimIndex); SharedData->SetSelectedBody(&Selection, IsCtrlPressed()); } // Pop up menu, if we have a body selected. if (SharedData->GetSelectedBody()) { OpenBodyMenu(); } } else if (HitProxy->IsA(HPhATEdConstraintProxy::StaticGetType()) && SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit) { HPhATEdConstraintProxy* ConstraintProxy = (HPhATEdConstraintProxy*)HitProxy; bool bAlreadySelected = false; //find out if constraint is already selected for(int32 i=0; iSelectedConstraints.Num(); ++i) { if(SharedData->SelectedConstraints[i].Index == ConstraintProxy->ConstraintIndex) { bAlreadySelected = true; break; } } // Select constraint under cursor if not already selected (if ctrl is held down we only add, not remove) if (!bAlreadySelected) { SharedData->SetSelectedConstraint(ConstraintProxy->ConstraintIndex, IsCtrlPressed()); } // Pop up menu, if we have a constraint selected. if (SharedData->GetSelectedConstraint()) { OpenConstraintMenu(); } } } } } bool FPhATEdPreviewViewportClient::InputWidgetDelta( FViewport* Viewport, EAxisList::Type CurrentAxis, FVector& Drag, FRotator& Rot, FVector& Scale ) { bool bHandled = false; TArray & SelectedObjects = SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit ? SharedData->SelectedBodies : SharedData->SelectedConstraints; for(int32 i=0; ibManipulating ) { float BoneScale = 1.f; if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) /// BODY EDITING /// { int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(SharedData->PhysicsAsset->BodySetup[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::TrackingStarted( const struct FInputEventState& InInputState, bool bIsDraggingWidget, bool bNudge ) { // If releasing the mouse button, check we are done manipulating if ( bIsDraggingWidget ) { FSceneViewFamilyContext ViewFamily( FSceneViewFamily::ConstructionValues( Viewport, GetScene(), EngineShowFlags )); FSceneView* View = CalcSceneView(&ViewFamily); const int32 HitX = InInputState.GetViewport()->GetMouseX(); const int32 HitY = InInputState.GetViewport()->GetMouseY(); StartManipulating(Widget->GetCurrentAxis(), FViewportClick(View, this, InInputState.GetKey(), InInputState.GetInputEvent(), HitX, HitY), View->ViewMatrices.ViewMatrix); // If we are manipulating, don't move the camera as we drag now. if (SharedData->bManipulating) { bAllowedToMoveCamera = false; } } } void FPhATEdPreviewViewportClient::TrackingStopped() { if (SharedData->bManipulating) { EndManipulating(); bAllowedToMoveCamera = true; } } FWidget::EWidgetMode FPhATEdPreviewViewportClient::GetWidgetMode() const { FWidget::EWidgetMode ReturnWidgetMode = FWidget::WM_None; FWidget::EWidgetMode WidgetMode = FEditorViewportClient::GetWidgetMode(); if( SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit && SharedData->GetSelectedBody()) { ReturnWidgetMode = WidgetMode; } else if( SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit && SharedData->GetSelectedConstraint() && WidgetMode != FWidget::WM_Scale ) { ReturnWidgetMode = WidgetMode; } return ReturnWidgetMode; } FVector FPhATEdPreviewViewportClient::GetWidgetLocation() const { if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) /// BODY EDITING /// { // Don't draw widget if nothing selected. if (!SharedData->GetSelectedBody()) { return FVector::ZeroVector; } int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(SharedData->PhysicsAsset->BodySetup[SharedData->GetSelectedBody()->Index]->BoneName); FTransform BoneTM = SharedData->EditorSkelComp->GetBoneTransform(BoneIndex); const float Scale = BoneTM.GetScale3D().GetAbsMax(); BoneTM.RemoveScaling(); return SharedData->EditorSkelComp->GetPrimitiveTransform(BoneTM, SharedData->GetSelectedBody()->Index, SharedData->GetSelectedBody()->PrimitiveType, SharedData->GetSelectedBody()->PrimitiveIndex, Scale).GetTranslation(); } else /// CONSTRAINT EDITING /// { if (!SharedData->GetSelectedConstraint()) { return FVector::ZeroVector; } return SharedData->GetConstraintMatrix(SharedData->GetSelectedConstraint()->Index, EConstraintFrame::Frame2, 1.f).GetTranslation(); } } FMatrix FPhATEdPreviewViewportClient::GetWidgetCoordSystem() const { if( GetWidgetCoordSystemSpace() == COORD_Local ) { if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) /// BODY EDITING /// { // Don't draw widget if nothing selected. if (!SharedData->GetSelectedBody()) { return FMatrix::Identity; } int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(SharedData->PhysicsAsset->BodySetup[SharedData->GetSelectedBody()->Index]->BoneName); FTransform BoneTM = SharedData->EditorSkelComp->GetBoneTransform(BoneIndex); BoneTM.RemoveScaling(); return SharedData->EditorSkelComp->GetPrimitiveTransform(BoneTM, SharedData->GetSelectedBody()->Index, SharedData->GetSelectedBody()->PrimitiveType, SharedData->GetSelectedBody()->PrimitiveIndex, 1.f).ToMatrixNoScale().RemoveTranslation(); } else /// CONSTRAINT EDITING /// { if (!SharedData->GetSelectedConstraint()) { return FMatrix::Identity; } return SharedData->GetConstraintMatrix(SharedData->GetSelectedConstraint()->Index, EConstraintFrame::Frame2, 1.f).ToMatrixNoScale().RemoveTranslation(); } } else { return FMatrix::Identity; } } ECoordSystem FPhATEdPreviewViewportClient::GetWidgetCoordSystemSpace() const { return GetWidgetMode() == FWidget::WM_Scale ? COORD_Local : ModeTools->GetCoordSystem();; } void FPhATEdPreviewViewportClient::Tick(float DeltaSeconds) { FEditorViewportClient::Tick(DeltaSeconds); UWorld* World = SharedData->PreviewScene.GetWorld(); if (SharedData->bRunningSimulation) { // check if PIE disabled the realtime viewport and quit sim if so if (!bIsRealtime) { SharedData->ToggleSimulation(); Invalidate(); } AWorldSettings* Setting = World->GetWorldSettings(); Setting->WorldGravityZ = SharedData->bNoGravitySimulation ? 0.0f : UPhysicsSettings::Get()->DefaultGravityZ*SharedData->EditorSimOptions->GravScale; Setting->bWorldGravitySet = true; // We back up the transforms array now SharedData->EditorSkelComp->AnimationSpaceBases = SharedData->EditorSkelComp->GetSpaceBases(); SharedData->EditorSkelComp->SetPhysicsBlendWeight(SharedData->EditorSimOptions->PhysicsBlend); } World->Tick(LEVELTICK_All, DeltaSeconds); if(SharedData->Recorder.InRecording()) { // make sure you don't allow switch SharedData->EditorSkelComp SharedData->Recorder.UpdateRecord(SharedData->EditorSkelComp, DeltaSeconds); } } FSceneInterface* FPhATEdPreviewViewportClient::GetScene() const { return SharedData->PreviewScene.GetScene(); } FLinearColor FPhATEdPreviewViewportClient::GetBackgroundColor() const { return FColor(64,64,64); } void FPhATEdPreviewViewportClient::OpenBodyMenu() { TSharedPtr MenuWidget = PhATPtr.Pin()->BuildMenuWidgetBody(); TSharedPtr< SPhATPreviewViewport > ParentWidget = PhATPtr.Pin()->GetPreviewViewportWidget(); if ( MenuWidget.IsValid() && ParentWidget.IsValid() ) { const FVector2D MouseCursorLocation = FSlateApplication::Get().GetCursorPos(); FSlateApplication::Get().PushMenu( ParentWidget.ToSharedRef(), MenuWidget.ToSharedRef(), MouseCursorLocation, FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu) ); } } void FPhATEdPreviewViewportClient::OpenConstraintMenu() { TSharedPtr MenuWidget = PhATPtr.Pin()->BuildMenuWidgetConstraint(); TSharedPtr< SPhATPreviewViewport > ParentWidget = PhATPtr.Pin()->GetPreviewViewportWidget(); if ( MenuWidget.IsValid() && ParentWidget.IsValid() ) { const FVector2D MouseCursorLocation = FSlateApplication::Get().GetCursorPos(); FSlateApplication::Get().PushMenu( ParentWidget.ToSharedRef(), MenuWidget.ToSharedRef(), MouseCursorLocation, FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu) ); } } void FPhATEdPreviewViewportClient::StartManipulating(EAxisList::Type Axis, const FViewportClick& Click, const FMatrix& WorldToCamera) { check(!SharedData->bManipulating); if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit && SharedData->SelectedBodies.Num()) { GEditor->BeginTransaction( NSLOCTEXT("UnrealEd", "MoveElement", "Move Element") ); for(int32 i=0; iSelectedBodies.Num(); ++i) { SharedData->PhysicsAsset->BodySetup[SharedData->SelectedBodies[i].Index]->Modify(); SharedData->SelectedBodies[i].ManipulateTM = FTransform::Identity; } SharedData->bManipulating = true; } else if( SharedData->GetSelectedConstraint()) { GEditor->BeginTransaction( NSLOCTEXT("UnrealEd", "MoveConstraint", "Move Constraint") ); for(int32 i=0; iSelectedConstraints.Num(); ++i) { SharedData->PhysicsAsset->ConstraintSetup[SharedData->SelectedConstraints[i].Index]->Modify(); SharedData->SelectedConstraints[i].ManipulateTM = FTransform::Identity; } const FTransform WParentFrame = SharedData->GetConstraintWorldTM(SharedData->GetSelectedConstraint(), EConstraintFrame::Frame2); const FTransform WChildFrame = SharedData->GetConstraintWorldTM(SharedData->GetSelectedConstraint(), EConstraintFrame::Frame1); StartManRelConTM = WChildFrame * WParentFrame.Inverse(); UPhysicsConstraintTemplate* Setup = SharedData->PhysicsAsset->ConstraintSetup[SharedData->GetSelectedConstraint()->Index]; StartManParentConTM = Setup->DefaultInstance.GetRefFrame(EConstraintFrame::Frame2); StartManChildConTM = Setup->DefaultInstance.GetRefFrame(EConstraintFrame::Frame1); SharedData->bManipulating = true; } } void FPhATEdPreviewViewportClient::EndManipulating() { if (SharedData->bManipulating) { SharedData->bManipulating = false; if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) { for(int32 i=0; iSelectedBodies.Num(); ++i) { FPhATSharedData::FSelection & SelectedObject = SharedData->SelectedBodies[i]; UBodySetup* BodySetup = SharedData->PhysicsAsset->BodySetup[SelectedObject.Index]; FKAggregateGeom* AggGeom = &BodySetup->AggGeom; if (SelectedObject.PrimitiveType == KPT_Sphere) { AggGeom->SphereElems[SelectedObject.PrimitiveIndex].Center = (SelectedObject.ManipulateTM * AggGeom->SphereElems[SelectedObject.PrimitiveIndex].GetTransform() ).GetLocation(); } else if (SelectedObject.PrimitiveType == KPT_Box) { AggGeom->BoxElems[SelectedObject.PrimitiveIndex].SetTransform(SelectedObject.ManipulateTM * AggGeom->BoxElems[SelectedObject.PrimitiveIndex].GetTransform() ); } else if (SelectedObject.PrimitiveType == KPT_Sphyl) { AggGeom->SphylElems[SelectedObject.PrimitiveIndex].SetTransform(SelectedObject.ManipulateTM * AggGeom->SphylElems[SelectedObject.PrimitiveIndex].GetTransform() ); } else if (SelectedObject.PrimitiveType == KPT_Convex) { FKConvexElem& Convex = AggGeom->ConvexElems[SelectedObject.PrimitiveIndex]; Convex.SetTransform(SelectedObject.ManipulateTM * Convex.GetTransform() ); } } } GEditor->EndTransaction(); Viewport->Invalidate(); } } void FPhATEdPreviewViewportClient::SimMousePress(FViewport* Viewport, bool bConstrainRotation, FKey Key) { bool bCtrlDown = Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl); bool bShiftDown = Viewport->KeyState(EKeys::LeftShift) || Viewport->KeyState(EKeys::RightShift); FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues( Viewport, GetScene(), EngineShowFlags )); FSceneView* View = CalcSceneView(&ViewFamily); const FViewportClick Click(View, this, EKeys::Invalid, IE_Released, Viewport->GetMouseX(), Viewport->GetMouseY()); #if DEBUG_CLICK_VIEWPORT SharedData->LastClickOrigin = Click.GetOrigin(); SharedData->LastClickDirection = Click.GetDirection(); #endif SharedData->LastClickPos = Click.GetClickPos(); FHitResult Result(1.f); bool bHit = SharedData->EditorSkelComp->LineTraceComponent(Result, Click.GetOrigin() - Click.GetDirection() * SimGrabCheckDistance, Click.GetOrigin() + Click.GetDirection() * SimGrabCheckDistance, FCollisionQueryParams(true)); if (bHit) { check(Result.Item != INDEX_NONE); FName BoneName = SharedData->PhysicsAsset->BodySetup[Result.Item]->BoneName; //UE_LOG(LogPhysics, Warning, TEXT("Hit Bone Name (%s)"), *BoneName.ToString()); // Right mouse is for dragging things around if (Key == EKeys::RightMouseButton) { SharedData->bManipulating = true; DragX = 0.0f; DragY = 0.0f; SimGrabPush = 0.0f; // Update mouse force properties from sim options. SharedData->MouseHandle->LinearDamping = SharedData->EditorSimOptions->HandleLinearDamping; SharedData->MouseHandle->LinearStiffness = SharedData->EditorSimOptions->HandleLinearStiffness; SharedData->MouseHandle->AngularDamping = SharedData->EditorSimOptions->HandleAngularDamping; SharedData->MouseHandle->AngularStiffness = SharedData->EditorSimOptions->HandleAngularStiffness; SharedData->MouseHandle->InterpolationSpeed = SharedData->EditorSimOptions->InterpolationSpeed; // Create handle to object. SharedData->MouseHandle->GrabComponent(SharedData->EditorSkelComp, BoneName, Result.Location, bConstrainRotation); FMatrix InvViewMatrix = View->ViewMatrices.ViewMatrix.InverseFast(); SimGrabMinPush = SimMinHoldDistance - (Result.Time * SimGrabCheckDistance); SimGrabLocation = Result.Location; SimGrabX = InvViewMatrix.GetUnitAxis( EAxis::X ); SimGrabY = InvViewMatrix.GetUnitAxis( EAxis::Y ); SimGrabZ = InvViewMatrix.GetUnitAxis( EAxis::Z ); } // Left mouse is for poking things else if (Key == EKeys::LeftMouseButton) { SharedData->EditorSkelComp->AddImpulseAtLocation(Click.GetDirection() * SharedData->EditorSimOptions->PokeStrength, Result.Location, BoneName); } } } void FPhATEdPreviewViewportClient::SimMouseMove(float DeltaX, float DeltaY) { DragX = Viewport->GetMouseX() - SharedData->LastClickPos.X; DragY = Viewport->GetMouseY() - SharedData->LastClickPos.Y; if (!SharedData->MouseHandle->GrabbedComponent) { return; } //We need to convert Pixel Delta into Screen position (deal with different viewport sizes) FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues( Viewport, GetScene(), EngineShowFlags )); FSceneView* View = CalcSceneView(&ViewFamily); FVector4 ScreenOldPos = View->PixelToScreen(SharedData->LastClickPos.X, SharedData->LastClickPos.Y, 1.f); FVector4 ScreenNewPos = View->PixelToScreen(DragX + SharedData->LastClickPos.X, DragY + SharedData->LastClickPos.Y, 1.f); FVector4 ScreenDelta = ScreenNewPos - ScreenOldPos; FVector4 ProjectedDelta = View->ScreenToWorld(ScreenDelta); FVector4 WorldDelta; //Now we project new ScreenPos to xy-plane of SimGrabLocation FVector LocalOffset = View->ViewMatrices.ViewMatrix.TransformPosition(SimGrabLocation + SimGrabZ * SimGrabPush); float ZDistance = GetViewportType() == ELevelViewportType::LVT_Perspective ? fabs(LocalOffset.Z) : 1.f; //in the ortho case we don't need to do any fixup because there is no perspective WorldDelta = ProjectedDelta * ZDistance; //Now we convert back into WorldPos FVector WorldPos = SimGrabLocation + WorldDelta + SimGrabZ * SimGrabPush; FVector NewLocation = WorldPos; float QuickRadius = 5 - SimGrabPush / SimHoldDistanceChangeDelta; QuickRadius = QuickRadius < 2 ? 2 : QuickRadius; DrawDebugPoint(GetWorld(), NewLocation, QuickRadius, FColorList::Red, false, 0.3); SharedData->MouseHandle->SetTargetLocation(NewLocation); SharedData->MouseHandle->GrabbedComponent->WakeRigidBody(SharedData->MouseHandle->GrabbedBoneName); } void FPhATEdPreviewViewportClient::SimMouseRelease() { SharedData->bManipulating = false; if (!SharedData->MouseHandle->GrabbedComponent) { return; } SharedData->MouseHandle->GrabbedComponent->WakeRigidBody(SharedData->MouseHandle->GrabbedBoneName); SharedData->MouseHandle->ReleaseComponent(); } void FPhATEdPreviewViewportClient::SimMouseWheelUp() { if (!SharedData->MouseHandle->GrabbedComponent) { return; } SimGrabPush += SimHoldDistanceChangeDelta; SimMouseMove(0.0f, 0.0f); } void FPhATEdPreviewViewportClient::SimMouseWheelDown() { if (!SharedData->MouseHandle->GrabbedComponent) { return; } SimGrabPush -= SimHoldDistanceChangeDelta; SimGrabPush = FMath::Max(SimGrabMinPush, SimGrabPush); SimMouseMove(0.0f, 0.0f); } void FPhATEdPreviewViewportClient::ModifyPrimitiveSize(int32 BodyIndex, EKCollisionPrimitiveType PrimType, int32 PrimIndex, FVector DeltaSize) { check(SharedData->GetSelectedBody()); FKAggregateGeom* AggGeom = &SharedData->PhysicsAsset->BodySetup[BodyIndex]->AggGeom; if (PrimType == KPT_Sphere) { check(AggGeom->SphereElems.IsValidIndex(PrimIndex)); AggGeom->SphereElems[PrimIndex].ScaleElem(DeltaSize, MinPrimSize); } else if (PrimType == KPT_Box) { check(AggGeom->BoxElems.IsValidIndex(PrimIndex)); AggGeom->BoxElems[PrimIndex].ScaleElem(DeltaSize, MinPrimSize); } else if (PrimType == KPT_Sphyl) { check(AggGeom->SphylElems.IsValidIndex(PrimIndex)); AggGeom->SphylElems[PrimIndex].ScaleElem(DeltaSize, MinPrimSize); } else if (PrimType == KPT_Convex) { check(AggGeom->ConvexElems.IsValidIndex(PrimIndex)); FVector ModifiedSize = DeltaSize; if (GEditor->UsePercentageBasedScaling()) { ModifiedSize = DeltaSize * ((GEditor->GetScaleGridSize() / 100.0f) / GEditor->GetGridSize()); } AggGeom->ConvexElems[PrimIndex].ScaleElem(ModifiedSize, MinPrimSize); } } void FPhATEdPreviewViewportClient::HitNothing() { if (!SharedData->bSelectionLock) { if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) { if(IsCtrlPressed() == false) //we only want to deselect if Ctrl is not used { SharedData->SetSelectedBody(0); } } else { if(IsCtrlPressed() == false) //we only want to deselect if Ctrl is not used { SharedData->SetSelectedConstraint(INDEX_NONE); } } } if (!SharedData->bSelectionLock) { Viewport->Invalidate(); PhATPtr.Pin()->RefreshHierachyTree(); } }