!XT (BREAK-16) (Sandbox) Remove double-newlines at the end of files.
[CRYENGINE.git] / Code / Sandbox / Plugins / EditorCommon / LevelEditor / Tools / ObjectMode.cpp
blobe6c57c32ab80ed07b745395a0869bd4b25d8dedd
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "ObjectMode.h"
5 #include "Viewport.h"
6 #include <Preferences/ViewportPreferences.h>
7 #include "ViewManager.h"
8 #include "Terrain/Heightmap.h"
9 #include "GameEngine.h"
10 #include "Objects/EntityObject.h"
11 #include "Objects/CameraObject.h"
12 #include "Controls/DynamicPopupMenu.h"
13 #include "Objects/BrushObject.h"
14 #include "DeepSelection.h"
15 #include "SubObjectSelectionReferenceFrameCalculator.h"
16 #include "Gizmos/IGizmoManager.h"
17 #include "Objects/ParticleEffectObject.h"
18 #include "Objects/PrefabObject.h"
19 #include "Grid.h"
20 #include "IUndoManager.h"
21 #include "Objects/ISelectionGroup.h"
23 /////////////////////////////
24 // CObjectManipulatorOwner
25 /////////////////////////////
26 CObjectManipulatorOwner::CObjectManipulatorOwner(CObjectMode* objectModeTool)
27 : m_bIsVisible(true)
28 , m_visibilityDirty(true)
30 m_manipulator = GetIEditor()->GetGizmoManager()->AddManipulator(this);
31 m_manipulator->signalBeginDrag.Connect(objectModeTool, &CObjectMode::OnManipulatorBeginDrag);
32 m_manipulator->signalDragging.Connect(objectModeTool, &CObjectMode::OnManipulatorDrag);
33 m_manipulator->signalEndDrag.Connect(objectModeTool, &CObjectMode::OnManipulatorEndDrag);
35 GetIEditor()->GetObjectManager()->signalObjectsChanged.Connect(this, &CObjectManipulatorOwner::OnObjectsChanged);
36 GetIEditor()->GetObjectManager()->signalSelectionChanged.Connect(this, &CObjectManipulatorOwner::OnSelectionChanged);
38 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
39 int totCount = pSelection->GetCount();
41 // register to all selected objects on initialization
42 for (int i = 0; i < totCount; ++i)
44 CBaseObject* pObj = pSelection->GetObject(i);
45 pObj->signalChanged.Connect(this, &CObjectManipulatorOwner::OnObjectChanged);
49 CObjectManipulatorOwner::~CObjectManipulatorOwner()
51 GetIEditor()->GetObjectManager()->signalObjectsChanged.DisconnectObject(this);
52 GetIEditor()->GetObjectManager()->signalSelectionChanged.DisconnectObject(this);
54 m_manipulator->signalBeginDrag.DisconnectAll();
55 m_manipulator->signalDragging.DisconnectAll();
56 m_manipulator->signalEndDrag.DisconnectAll();
57 GetIEditor()->GetGizmoManager()->RemoveManipulator(m_manipulator);
59 m_manipulator = nullptr;
61 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
62 int totCount = pSelection->GetCount();
64 // unregister from all selected objects
65 for (int i = 0; i < totCount; ++i)
67 CBaseObject* pObj = pSelection->GetObject(i);
68 pObj->signalChanged.DisconnectObject(this);
72 bool CObjectManipulatorOwner::GetManipulatorMatrix(Matrix34& tm)
74 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
75 return pSelection->GetManipulatorMatrix(tm);
78 void CObjectManipulatorOwner::GetManipulatorPosition(Vec3& position)
80 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
82 position.Set(0.0f, 0.0f, 0.0f);
84 int totCount = pSelection->GetCount();
85 for (int i = 0; i < totCount; ++i)
87 CBaseObject* pObj = pSelection->GetObject(i);
88 if (pObj->IsVisible() && !pObj->IsFrozen())
90 position += pObj->GetWorldPos();
94 position *= (1.0f / totCount);
97 void CObjectManipulatorOwner::OnObjectChanged(const CBaseObject* pObject, const CObjectEvent& event)
99 if (event.m_type == OBJECT_ON_TRANSFORM)
101 // tag for update next cycle
102 m_manipulator->Invalidate();
104 else if (event.m_type == OBJECT_ON_VISIBILITY)
106 m_manipulator->Invalidate();
107 m_visibilityDirty = true;
111 void CObjectManipulatorOwner::OnObjectsChanged(const std::vector<CBaseObject*>& objects, const CObjectEvent& event)
113 if (event.m_type == OBJECT_ON_TRANSFORM)
115 // tag for update next cycle
116 m_manipulator->Invalidate();
120 void CObjectManipulatorOwner::OnSelectionChanged(const std::vector<CBaseObject*>& selected, const std::vector<CBaseObject*>& deselected)
122 CRY_PROFILE_FUNCTION(PROFILE_EDITOR);
123 // unregister from all manipulated objects
124 for (auto& pObject : deselected)
126 pObject->signalChanged.DisconnectObject(this);
129 for (auto& pObject : selected)
131 pObject->signalChanged.Connect(this, &CObjectManipulatorOwner::OnObjectChanged);
134 m_manipulator->Invalidate();
135 m_visibilityDirty = true;
138 bool CObjectManipulatorOwner::IsManipulatorVisible()
140 if (m_visibilityDirty)
142 UpdateVisibilityState();
143 m_manipulator->Invalidate();
144 m_visibilityDirty = false;
146 return m_bIsVisible;
149 void CObjectManipulatorOwner::UpdateVisibilityState()
151 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
152 m_bIsVisible = false;
153 int totCount = pSelection->GetCount();
154 for (int i = 0; i < totCount; ++i)
156 CBaseObject* pObj = pSelection->GetObject(i);
158 if (pObj->IsVisible() && !pObj->IsFrozen())
160 m_bIsVisible = true;
161 break;
166 //////////////////////////////////////////////////////////////////////////
167 IMPLEMENT_DYNCREATE(CObjectMode, CEditTool)
169 SDeepSelectionPreferences gDeepSelectionPreferences;
170 REGISTER_PREFERENCES_PAGE_PTR(SDeepSelectionPreferences, &gDeepSelectionPreferences)
172 //////////////////////////////////////////////////////////////////////////
173 CObjectMode::CObjectMode()
174 : m_objectManipulatorOwner(this)
175 , m_bDragThresholdExceeded(false)
177 m_openContext = false;
178 m_commandMode = NothingMode;
179 m_MouseOverObject = CryGUID::Null();
181 m_pDeepSelection = new CDeepSelection();
182 m_bMoveByFaceNormManipShown = false;
183 m_pHitObject = NULL;
185 m_bTransformChanged = false;
187 m_suspendHighlightChange = false;
188 m_normalMoveGizmo = nullptr;
190 m_bGizmoDrag = false;
193 //////////////////////////////////////////////////////////////////////////
194 CObjectMode::~CObjectMode()
196 GetIEditor()->UnRegisterAllObjectModeSubTools();
197 CAutoRegisterObjectModeSubToolHelper::UnregisterAll();
199 CBaseObject* pMouseOverObject = nullptr;
200 if (m_MouseOverObject != CryGUID::Null())
202 pMouseOverObject = GetIEditor()->GetObjectManager()->FindObject(m_MouseOverObject);
205 if (pMouseOverObject)
207 pMouseOverObject->SetHighlight(false);
210 if (m_normalMoveGizmo)
212 m_normalMoveGizmo->signalBeginDrag.DisconnectAll();
213 m_normalMoveGizmo->signalDragging.DisconnectAll();
214 m_normalMoveGizmo->signalEndDrag.DisconnectAll();
215 GetIEditor()->GetGizmoManager()->RemoveManipulator(m_normalMoveGizmo);
216 m_normalMoveGizmo = nullptr;
220 void CObjectMode::Activate()
222 CAutoRegisterObjectModeSubToolHelper::RegisterAll();
223 GetIEditor()->RegisterAllObjectModeSubTools();
226 void CObjectMode::RegisterSubTool(ISubTool* pSubTool)
228 m_subTools.insert(pSubTool);
231 void CObjectMode::UnRegisterSubTool(ISubTool* pSubTool)
233 m_subTools.erase(pSubTool);
236 void CObjectMode::DisplaySelectionPreview(SDisplayContext& dc)
238 IDisplayViewport* pDisplayViewport = dc.view;
239 if (!pDisplayViewport)
240 return;
242 CViewport* pViewport = static_cast<CViewport*>(pDisplayViewport);
243 if (!pViewport)
244 return;
246 IObjectManager* objMan = GetIEditor()->GetObjectManager();
248 CRect rc = pViewport->GetSelectionRectangle();
250 if (GetCommandMode() == SelectMode)
252 if (rc.Width() > 1 && rc.Height() > 1)
254 GetIEditor()->GetObjectManager()->FindObjectsInRect(pViewport, rc, m_PreviewGUIDs);
256 // Do not include child objects in the count of object candidates
257 int childNo = 0;
258 for (int objNo = 0; objNo < m_PreviewGUIDs.size(); ++objNo)
260 auto foundObj = objMan->FindObject(m_PreviewGUIDs[objNo]);
261 if (foundObj && foundObj->GetParent())
262 ++childNo;
265 // Draw Preview for objects
266 for (size_t i = 0; i < m_PreviewGUIDs.size(); ++i)
268 CBaseObject* pObject = GetIEditor()->GetObjectManager()->FindObject(m_PreviewGUIDs[i]);
270 if (!pObject)
271 continue;
273 if (pObject->GetType() & ~gViewportSelectionPreferences.objectSelectMask)
274 continue;
276 pObject->DrawSelectionPreviewHighlight(dc);
282 //////////////////////////////////////////////////////////////////////////
283 void CObjectMode::Display(SDisplayContext& dc)
285 // Selection Candidates Preview
286 DisplaySelectionPreview(dc);
289 //////////////////////////////////////////////////////////////////////////
290 bool CObjectMode::MouseCallback(CViewport* view, EMouseEvent event, CPoint& point, int flags)
292 // Sub tools get to handle the event first, if it goes unhandled, object mode will then try to handle it
293 for (ISubTool* pSubTool : m_subTools)
295 if (pSubTool->HandleMouseEvent(view, event, point, flags))
297 return true;
301 switch (event)
303 case eMouseLDown:
304 return OnLButtonDown(view, flags, point);
305 break;
306 case eMouseLUp:
307 return OnLButtonUp(view, flags, point);
308 break;
309 case eMouseLDblClick:
310 return OnLButtonDblClk(view, flags, point);
311 break;
312 case eMouseRDown:
313 return OnRButtonDown(view, flags, point);
314 break;
315 case eMouseRUp:
316 return OnRButtonUp(view, flags, point);
317 break;
318 case eMouseMove:
319 case eMouseEnter:
320 return OnMouseMove(view, flags, point);
321 break;
322 case eMouseMDown:
323 return OnMButtonDown(view, flags, point);
324 break;
325 case eMouseFocusLeave:
326 case eMouseLeave:
327 SetObjectCursor(view, 0, IObjectManager::ESelectOp::eNone);
328 return true;
329 break;
331 return false;
334 //////////////////////////////////////////////////////////////////////////
335 bool CObjectMode::OnKeyDown(CViewport* view, uint32 nChar, uint32 nRepCnt, uint32 nFlags)
337 if (nChar == Qt::Key_Escape)
339 GetIEditor()->ClearSelection();
340 CLevelEditorSharedState* pLevelEditor = GetIEditor()->GetLevelEditorSharedState();
342 if (pLevelEditor->GetEditMode() == CLevelEditorSharedState::EditMode::SelectArea)
343 pLevelEditor->SetEditMode(CLevelEditorSharedState::EditMode::Select);
345 return false;
348 //////////////////////////////////////////////////////////////////////////
349 bool CObjectMode::OnKeyUp(CViewport* view, uint32 nChar, uint32 nRepCnt, uint32 nFlags)
351 return false;
354 //////////////////////////////////////////////////////////////////////////
355 bool CObjectMode::OnLButtonDown(CViewport* view, int nFlags, CPoint point)
357 if (m_bMoveByFaceNormManipShown)
359 HideMoveByFaceNormGizmo();
362 // CPointF ptMarker;
363 CPoint ptCoord;
364 int iCurSel = -1;
366 if (GetIEditor()->IsInGameMode())
368 // Ignore clicks while in game.
369 return false;
372 // Save the mouse down position
373 m_cMouseDownPos = point;
375 m_bDragThresholdExceeded = false;
377 view->ResetSelectionRegion();
379 Vec3 pos = view->SnapToGrid(view->ViewToWorld(point));
380 CLevelEditorSharedState::EditMode editMode = GetIEditor()->GetLevelEditorSharedState()->GetEditMode();
382 // Show marker position in the status bar
383 //cry_sprintf(szNewStatusText, "X:%g Y:%g Z:%g",pos.x,pos.y,pos.z );
385 // Swap X/Y
386 float unitSize = 1;
387 CHeightmap* pHeightmap = GetIEditor()->GetHeightmap();
388 if (pHeightmap)
389 unitSize = pHeightmap->GetUnitSize();
390 float hx = pos.y / unitSize;
391 float hy = pos.x / unitSize;
392 float hz = GetIEditor()->GetTerrainElevation(pos.x, pos.y);
394 // Get control key status.
395 const bool bAltClick = (nFlags & MK_ALT);
396 const bool bCtrlClick = (nFlags & MK_CONTROL);
397 const bool bShiftClick = (nFlags & MK_SHIFT);
399 const bool bAddSelect = bShiftClick;
400 const bool bToggle = bCtrlClick;
401 // Alt click might be beginning of marque deselection - so we must not remove selection
402 const bool bNoRemoveSelection = bAddSelect || bToggle || bAltClick;
404 // Check deep selection mode activated
405 // The Deep selection has two mode.
406 // The normal mode pops the context menu, another is the cyclic selection on clinking.
407 bool bTabPressed = CheckVirtualKey(VK_TAB);
408 bool bZKeyPressed = CheckVirtualKey('Z');
410 CDeepSelection::EDeepSelectionMode dsMode =
411 (bTabPressed ? (bZKeyPressed ? CDeepSelection::DSM_POP : CDeepSelection::DSM_CYCLE) : CDeepSelection::DSM_NONE);
413 bool bLockSelection = GetIEditor()->IsSelectionLocked();
415 int numUnselected = 0;
416 int numSelected = 0;
418 // m_activeAxis = 0;
420 HitContext hitInfo;
421 hitInfo.view = view;
423 if (dsMode == CDeepSelection::DSM_POP)
425 m_pDeepSelection->Reset(true);
426 m_pDeepSelection->SetMode(dsMode);
427 hitInfo.pDeepSelection = m_pDeepSelection;
429 else if (dsMode == CDeepSelection::DSM_CYCLE)
431 if (!m_pDeepSelection->OnCycling(point))
433 // Start of the deep selection cycling mode.
434 m_pDeepSelection->Reset(false);
435 m_pDeepSelection->SetMode(dsMode);
436 hitInfo.pDeepSelection = m_pDeepSelection;
439 else
441 if (m_pDeepSelection->GetPreviousMode() == CDeepSelection::DSM_NONE)
442 m_pDeepSelection->Reset(true);
444 m_pDeepSelection->SetMode(CDeepSelection::DSM_NONE);
445 hitInfo.pDeepSelection = 0;
449 if (view->HitTest(point, hitInfo))
451 if (hitInfo.axis != CLevelEditorSharedState::Axis::None)
453 GetIEditor()->GetLevelEditorSharedState()->SetAxisConstraint(hitInfo.axis);
454 // if edit mode is set to selection, then we treat gizmo as a selection component and we should not lock the selection
455 if (editMode != CLevelEditorSharedState::EditMode::Select)
457 bLockSelection = true;
461 //////////////////////////////////////////////////////////////////////////
462 // Deep Selection
463 CheckDeepSelection(hitInfo, view);
466 CBaseObject* hitObj = hitInfo.object;
468 CLevelEditorSharedState::CoordSystem coordSys = GetIEditor()->GetLevelEditorSharedState()->GetCoordSystem();
469 Vec3 gridPosition = (hitObj) ? hitObj->GetWorldPos() : pos;
471 if (coordSys == CLevelEditorSharedState::CoordSystem::UserDefined)
473 Matrix34 userTM = Matrix34::CreateIdentity();
474 GetIEditor()->GetISelectionGroup()->GetManipulatorMatrix(userTM);
475 userTM.SetTranslation(Vec3(0, 0, 0));
476 userTM.SetTranslation(gridPosition);
477 view->SetConstructionMatrix(userTM);
479 else if (coordSys == CLevelEditorSharedState::CoordSystem::World || !hitObj)
481 Matrix34 tm = Matrix34::CreateIdentity();
482 tm.SetTranslation(gridPosition);
483 view->SetConstructionMatrix(tm);
485 else if (coordSys == CLevelEditorSharedState::CoordSystem::Parent)
487 if (hitInfo.object->GetParent())
489 Matrix34 parentTM = hitInfo.object->GetParent()->GetWorldTM();
490 parentTM.OrthonormalizeFast();
491 parentTM.SetTranslation(gridPosition);
492 view->SetConstructionMatrix(parentTM);
494 else
496 view->SetConstructionMatrix(hitInfo.object->GetWorldTM());
499 else if (coordSys == CLevelEditorSharedState::CoordSystem::Local)
501 view->SetConstructionMatrix(hitInfo.object->GetWorldTM());
504 if (gSnappingPreferences.IsSnapToTerrainEnabled() && GetIEditor()->GetLevelEditorSharedState()->GetAxisConstraint() != CLevelEditorSharedState::Axis::Z)
506 m_mouseDownWorldPos = view->ViewToWorld(point);
508 else
510 m_mouseDownWorldPos = view->MapViewToCP(point);
513 if (editMode != CLevelEditorSharedState::EditMode::Tool)
515 // Check for Move to position.
516 if (bCtrlClick && bShiftClick)
518 // Ctrl-Click on terrain will move selected objects to specified location.
519 MoveSelectionToPos(view, pos, bAltClick, point);
520 bLockSelection = true;
524 if (editMode == CLevelEditorSharedState::EditMode::Move)
526 if (!bNoRemoveSelection)
527 SetCommandMode(MoveMode);
529 if (hitObj && hitObj->IsSelected() && !bNoRemoveSelection)
530 bLockSelection = true;
532 else if (editMode == CLevelEditorSharedState::EditMode::Rotate)
534 if (!bNoRemoveSelection)
535 SetCommandMode(RotateMode);
536 if (hitObj && hitObj->IsSelected() && !bNoRemoveSelection)
537 bLockSelection = true;
539 else if (editMode == CLevelEditorSharedState::EditMode::Scale)
541 if (!bNoRemoveSelection)
543 SetCommandMode(ScaleMode);
546 if (hitObj && hitObj->IsSelected() && !bNoRemoveSelection)
547 bLockSelection = true;
549 else if (hitObj != 0 && GetIEditor()->GetSelectedObject() == hitObj && !bAddSelect && !bToggle)
551 bLockSelection = true;
554 if (!bLockSelection)
556 // If not selection locked.
557 view->BeginUndo();
559 IObjectManager* pObjectManager = GetIEditor()->GetObjectManager();
561 if (!bNoRemoveSelection)
563 // Current selection should be cleared
564 numSelected = GetIEditor()->GetISelectionGroup()->GetCount();
565 pObjectManager->ClearSelection();
568 if (hitObj)
570 numSelected = 1;
572 if (!bToggle)
574 pObjectManager->SelectObject(hitObj);
576 else
578 if (hitObj->IsSelected())
580 pObjectManager->UnselectObject(hitObj);
582 else
584 pObjectManager->SelectObject(hitObj);
588 if (view->IsUndoRecording())
590 view->AcceptUndo("Select Object(s)");
593 if (numSelected == 0 || editMode == CLevelEditorSharedState::EditMode::Select || editMode == CLevelEditorSharedState::EditMode::SelectArea)
595 // If object is not selected.
596 // Capture mouse input for this window.
597 SetCommandMode(SelectMode);
601 if (GetCommandMode() == MoveMode ||
602 GetCommandMode() == RotateMode ||
603 GetCommandMode() == ScaleMode)
605 m_suspendHighlightChange = true;
606 view->BeginUndo();
609 //////////////////////////////////////////////////////////////////////////
610 // Change cursor, must be before Capture mouse.
611 //////////////////////////////////////////////////////////////////////////
612 SetObjectCursor(view, hitObj, bAddSelect ? IObjectManager::ESelectOp::eSelect : IObjectManager::ESelectOp::eNone);
614 m_bTransformChanged = false;
616 if (m_pDeepSelection->GetMode() == CDeepSelection::DSM_POP)
617 return OnLButtonUp(view, nFlags, point);
619 return true;
622 //////////////////////////////////////////////////////////////////////////
623 bool CObjectMode::OnLButtonUp(CViewport* view, int nFlags, CPoint point)
625 if (GetIEditor()->IsInGameMode())
627 // Ignore clicks while in game.
628 return true;
631 if (m_bTransformChanged)
633 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
634 if (pSelection)
635 pSelection->FinishChanges();
636 m_bTransformChanged = false;
639 // Undo may have been accepted already for MoveMode (if mouse has moved from original position), so view->IsUndoRecording()
640 // will be false here, but we still need to cleanup. AcceptUndo is safe in any case because it checks if we are using undo.
641 if (view->IsUndoRecording())
643 if (GetCommandMode() == MoveMode)
645 view->AcceptUndo("Move Selection");
647 else if (GetCommandMode() == RotateMode)
649 view->AcceptUndo("Rotate Selection");
651 else if (GetCommandMode() == ScaleMode)
653 view->AcceptUndo("Scale Selection");
655 else
657 view->CancelUndo();
661 if (GetCommandMode() == MoveMode)
663 m_bDragThresholdExceeded = false;
666 //////////////////////////////////////////////////////////////////////////
668 if (GetCommandMode() == SelectMode && (!GetIEditor()->IsSelectionLocked()))
670 const bool bUnselect = (nFlags & MK_ALT);
671 const bool bToggle = (nFlags & MK_CONTROL);
673 const IObjectManager::ESelectOp selectOp = bUnselect ? IObjectManager::ESelectOp::eUnselect :
674 (bToggle ? IObjectManager::ESelectOp::eToggle : IObjectManager::ESelectOp::eSelect);
676 CRect selectRect = view->GetSelectionRectangle();
677 if (!selectRect.IsRectEmpty())
679 // Ignore too small rectangles.
680 if (selectRect.Width() > 5 && selectRect.Height() > 5)
682 GetIEditor()->GetObjectManager()->SelectObjectsInRect(view, selectRect, selectOp);
686 if (GetIEditor()->GetLevelEditorSharedState()->GetEditMode() == CLevelEditorSharedState::EditMode::SelectArea)
688 GetIEditor()->ClearSelection();
692 // If command mode is still "NothingMode" this object was just created and placed and needs this update too.
693 if (GetCommandMode() == ScaleMode || GetCommandMode() == MoveMode || GetCommandMode() == RotateMode || GetCommandMode() == NothingMode)
695 m_suspendHighlightChange = false;
696 GetIEditor()->GetISelectionGroup()->ObjectModified();
699 if (GetIEditor()->GetLevelEditorSharedState()->GetEditMode() != CLevelEditorSharedState::EditMode::SelectArea)
701 view->ResetSelectionRegion();
703 // Reset selected rectangle.
704 view->SetSelectionRectangle(CPoint(0, 0), CPoint(0, 0));
706 SetCommandMode(NothingMode);
708 return true;
711 //////////////////////////////////////////////////////////////////////////
712 bool CObjectMode::OnLButtonDblClk(CViewport* view, int nFlags, CPoint point)
714 // If shift clicked, Move the camera to this place.
715 if (nFlags & MK_SHIFT)
717 // Get the heightmap coordinates for the click position
718 Vec3 v = view->ViewToWorld(point);
719 if (!(v.x == 0 && v.y == 0 && v.z == 0))
721 Matrix34 tm = view->GetViewTM();
722 Vec3 p = tm.GetTranslation();
723 float height = p.z - GetIEditor()->GetTerrainElevation(p.x, p.y);
724 if (height < 1) height = 1;
725 p.x = v.x;
726 p.y = v.y;
727 p.z = GetIEditor()->GetTerrainElevation(p.x, p.y) + height;
728 tm.SetTranslation(p);
729 view->SetViewTM(tm);
732 else
734 // Check if double clicked on object.
735 HitContext hitInfo;
736 view->HitTest(point, hitInfo);
738 CBaseObject* hitObj = hitInfo.object;
739 if (hitObj)
741 // Fire double click event on hit object.
742 hitObj->OnEvent(EVENT_DBLCLICK);
745 return true;
748 //////////////////////////////////////////////////////////////////////////
749 bool CObjectMode::OnRButtonDown(CViewport* view, int nFlags, CPoint point)
751 if (gViewportPreferences.enableContextMenu)
753 // Check if right clicked on object.
754 HitContext hitInfo;
755 if (view->HitTest(point, hitInfo) && hitInfo.object)
757 m_openContext = true;
758 m_rMouseDownPos = point;
759 return true;
762 return false;
765 //////////////////////////////////////////////////////////////////////////
766 bool CObjectMode::OnRButtonUp(CViewport* view, int nFlags, CPoint point)
768 if (m_openContext)
770 m_openContext = false;
772 // check if we are close to the original mouse position
773 int halfLength = gViewportPreferences.dragSquareSize / 2;
774 CRect rcDrag(m_rMouseDownPos.x, m_rMouseDownPos.y, m_rMouseDownPos.x, m_rMouseDownPos.y);
775 InflateRect(rcDrag, halfLength, halfLength);
777 if (!PtInRect(rcDrag, point))
778 return false;
780 // Check if right clicked on object.
781 HitContext hitInfo;
782 if (!view->HitTest(point, hitInfo))
783 return false;
784 if (!hitInfo.object)
785 return false;
787 CDynamicPopupMenu menu;
788 hitInfo.object->OnContextMenu(&menu.GetRoot());
790 if (!menu.GetRoot().Empty())
792 // suspend highlight change for the duration of the popup...
793 m_suspendHighlightChange = true;
795 // and resume on close
796 menu.SetOnHideFunctor([ = ]
798 if (GetIEditor()->GetLevelEditorSharedState()->GetEditTool() == this)
800 AllowHighlightChange();
803 menu.SpawnAtCursor();
805 return true;
807 return false;
810 //////////////////////////////////////////////////////////////////////////
811 bool CObjectMode::OnMButtonDown(CViewport* view, int nFlags, CPoint point)
813 if (GetIEditor()->GetGameEngine()->GetSimulationMode())
815 // Get control key status.
816 const bool bAltClick = (nFlags & MK_ALT);
817 const bool bCtrlClick = (nFlags & MK_CONTROL);
818 const bool bShiftClick = (nFlags & MK_SHIFT);
820 if (bCtrlClick)
822 // In simulation mode awake objects under the cursor when Ctrl+MButton pressed.
823 AwakeObjectAtPoint(view, point);
824 return true;
827 return false;
830 //////////////////////////////////////////////////////////////////////////
831 void CObjectMode::AwakeObjectAtPoint(CViewport* view, CPoint point)
833 // In simulation mode awake objects under the cursor.
834 // Check if double clicked on object.
835 HitContext hitInfo;
836 view->HitTest(point, hitInfo);
837 CBaseObject* hitObj = hitInfo.object;
838 if (hitObj)
840 IPhysicalEntity* pent = hitObj->GetCollisionEntity();
841 if (pent)
843 pe_action_awake pa;
844 pa.bAwake = true;
845 pent->Action(&pa);
850 //////////////////////////////////////////////////////////////////////////
851 bool CObjectMode::CheckVirtualKey(int virtualKey)
853 GetAsyncKeyState(virtualKey);
854 if (GetAsyncKeyState(virtualKey))
855 return true;
856 return false;
859 //////////////////////////////////////////////////////////////////////////
860 void CObjectMode::MoveSelectionToPos(CViewport* view, Vec3& pos, bool align, const CPoint& point)
862 view->BeginUndo();
863 // Find center of selection.
864 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
865 Vec3 center = pSelection->GetCenter();
866 pSelection->Move(pos - center, ISelectionGroup::eMS_None, point, true);
868 if (align)
869 GetIEditor()->GetISelectionGroup()->Align();
871 view->AcceptUndo("Move Selection");
874 //////////////////////////////////////////////////////////////////////////
875 bool CObjectMode::OnMouseMove(CViewport* view, int nFlags, CPoint point)
877 if (GetIEditor()->IsInGameMode())
879 // Ignore while in game.
880 return true;
882 const bool bAltClick = (nFlags & MK_ALT);
883 const bool bCtrlClick = (nFlags & MK_CONTROL);
884 const bool bShiftClick = (nFlags & MK_SHIFT);
886 bool bSomethingDone = false;
888 bool bAdding = false;
889 bool bRemoving = false;
891 m_openContext = false;
893 // get current axis constrains.
894 if (GetCommandMode() == MoveMode)
896 if (!m_bDragThresholdExceeded)
898 int halfLength = gViewportPreferences.dragSquareSize / 2;
899 CRect rcDrag(m_cMouseDownPos.x, m_cMouseDownPos.y, m_cMouseDownPos.x, m_cMouseDownPos.y);
900 InflateRect(rcDrag, halfLength, halfLength);
902 if (!PtInRect(rcDrag, point))
904 // Save the current positions, move tool relies on them - change from relying on undo system.
905 GetIEditor()->GetISelectionGroup()->FilterParents();
906 GetIEditor()->GetISelectionGroup()->SaveFilteredTransform();
908 else
910 return true;
914 Vec3 v;
915 Vec3 p1 = m_mouseDownWorldPos;
916 if (gSnappingPreferences.IsSnapToTerrainEnabled() && GetIEditor()->GetLevelEditorSharedState()->GetAxisConstraint() != CLevelEditorSharedState::Axis::Z)
918 Vec3 p2 = view->SnapToGrid(view->ViewToWorld(point));
919 v = view->GetCPVector(p1, p2);
920 v.z = 0;
922 else
924 Vec3 p2 = view->MapViewToCP(point);
925 if (p1.IsZero() || p2.IsZero())
926 return true;
928 v = view->GetCPVector(p1, p2);
931 int selectionFlags = ISelectionGroup::eMS_None;
932 if (gSnappingPreferences.IsSnapToTerrainEnabled() && GetIEditor()->GetLevelEditorSharedState()->GetAxisConstraint() != CLevelEditorSharedState::Axis::Z)
933 selectionFlags = ISelectionGroup::eMS_FollowTerrain;
935 if (gSnappingPreferences.IsSnapToGeometryEnabled())
936 selectionFlags |= ISelectionGroup::eMS_FollowGeometry;
938 if (gSnappingPreferences.IsSnapToNormalEnabled())
939 selectionFlags |= ISelectionGroup::eMS_SnapToNormal;
941 if ((nFlags & MK_CONTROL) && !(nFlags & MK_SHIFT))
942 selectionFlags = ISelectionGroup::eMS_FollowGeometry | ISelectionGroup::eMS_SnapToNormal;
944 if (!v.IsEquivalent(Vec3(0, 0, 0)))
945 m_bTransformChanged = true;
947 #pragma message("TODO")
948 //CTrackViewSequenceNoNotificationContext context(pSequence);
950 if (m_bDragThresholdExceeded)
952 // All moved objects should have pushed an undo after moving, so suspend here
953 // if we don't we'll end up with a huge undo step with duplicate entries for the objects.
954 // TODO: improve undo system to only push when really necessary.
955 GetIEditor()->GetIUndoManager()->Suspend();
958 GetIEditor()->GetISelectionGroup()->Move(v, selectionFlags, point, true);
960 if (m_bDragThresholdExceeded)
962 GetIEditor()->GetIUndoManager()->Resume();
965 m_bDragThresholdExceeded = true;
966 bSomethingDone = true;
968 else if (GetCommandMode() == RotateMode)
970 GetIEditor()->GetIUndoManager()->Restore();
972 Ang3 ang(0, 0, 0);
973 float ax = point.x - m_cMouseDownPos.x;
974 float ay = point.y - m_cMouseDownPos.y;
975 switch (GetIEditor()->GetLevelEditorSharedState()->GetAxisConstraint())
977 case CLevelEditorSharedState::Axis::X:
978 ang.x = ay;
979 break;
980 case CLevelEditorSharedState::Axis::Y:
981 ang.y = ay;
982 break;
983 case CLevelEditorSharedState::Axis::Z:
984 ang.z = ay;
985 break;
986 case CLevelEditorSharedState::Axis::XY:
987 ang(ax, ay, 0);
988 break;
989 case CLevelEditorSharedState::Axis::XZ:
990 ang(ax, 0, ay);
991 break;
992 case CLevelEditorSharedState::Axis::YZ:
993 ang(0, ay, ax);
994 break;
998 ang = gSnappingPreferences.SnapAngle(ang);
1000 if (!ang.IsEquivalent(Ang3(0, 0, 0)))
1001 m_bTransformChanged = true;
1003 //m_cMouseDownPos = point;
1004 GetIEditor()->GetISelectionGroup()->Rotate(ang);
1005 bSomethingDone = true;
1007 else if (GetCommandMode() == ScaleMode)
1009 Vec3 scale;
1011 GetScale(view, point, scale);
1013 if (!m_bTransformChanged)
1015 if (!scale.IsEquivalent(Vec3(0, 0, 0)))
1017 // Save the current positions, scale tool relies on them - change from relying on undo system.
1018 GetIEditor()->GetISelectionGroup()->FilterParents();
1019 GetIEditor()->GetISelectionGroup()->SaveFilteredTransform();
1021 else
1023 return true;
1027 if (m_bTransformChanged)
1029 GetIEditor()->GetIUndoManager()->Suspend();
1031 GetIEditor()->GetISelectionGroup()->Scale(scale);
1032 if (m_bTransformChanged)
1034 GetIEditor()->GetIUndoManager()->Resume();
1036 m_bTransformChanged = true;
1037 bSomethingDone = true;
1039 else if (GetCommandMode() == SelectMode)
1041 // Ignore select when selection locked.
1042 if (GetIEditor()->IsSelectionLocked())
1043 return true;
1045 if (bShiftClick)
1047 bAdding = true;
1049 else if (bAltClick)
1051 bRemoving = true;
1054 CRect rc(m_cMouseDownPos, point);
1055 if (GetIEditor()->GetLevelEditorSharedState()->GetEditMode() == CLevelEditorSharedState::EditMode::SelectArea)
1057 view->OnDragSelectRectangle(CPoint(rc.left, rc.top), CPoint(rc.right, rc.bottom), false);
1059 else
1061 view->SetSelectionRectangle(rc.TopLeft(), rc.BottomRight());
1063 //else
1064 //OnDragSelectRectangle( CPoint(rc.left,rc.top),CPoint(rc.right,rc.bottom),true );
1066 bSomethingDone = true;
1069 if (!(nFlags & MK_RBUTTON || nFlags & MK_MBUTTON))
1071 const IObjectManager::ESelectOp selectOp = bAdding ? IObjectManager::ESelectOp::eSelect :
1072 (bRemoving ? IObjectManager::ESelectOp::eUnselect : IObjectManager::ESelectOp::eNone);
1074 // Track mouse movements.
1075 CGizmo* highlightedGizmo = GetIEditor()->GetGizmoManager()->GetHighlightedGizmo();
1077 HitContext hitInfo;
1078 if (!highlightedGizmo && view->HitTest(point, hitInfo))
1080 SetObjectCursor(view, hitInfo.object, selectOp);
1082 else
1084 SetObjectCursor(view, nullptr, selectOp);
1087 HandleMoveByFaceNormal(hitInfo);
1089 else
1091 // pan/rotate case
1092 SetObjectCursor(view, nullptr, IObjectManager::ESelectOp::eNone);
1095 if ((nFlags & MK_MBUTTON) && GetIEditor()->GetGameEngine()->GetSimulationMode())
1097 // Get control key status.
1098 if (bCtrlClick)
1100 // In simulation mode awake objects under the cursor when Ctrl+MButton pressed.
1101 AwakeObjectAtPoint(view, point);
1105 return bSomethingDone;
1108 //////////////////////////////////////////////////////////////////////////
1109 void CObjectMode::SetObjectCursor(CViewport* view, CBaseObject* hitObj, IObjectManager::ESelectOp selectMode)
1111 EStdCursor cursor = STD_CURSOR_DEFAULT;
1112 string m_cursorStr;
1114 CBaseObject* pMouseOverObject = NULL;
1115 if (m_MouseOverObject != CryGUID::Null() && !m_suspendHighlightChange)
1117 pMouseOverObject = GetIEditor()->GetObjectManager()->FindObject(m_MouseOverObject);
1120 //HCURSOR hPrevCursor = m_hCurrCursor;
1121 if (pMouseOverObject)
1123 pMouseOverObject->SetHighlight(false);
1126 if (!m_suspendHighlightChange)
1128 if (hitObj)
1129 m_MouseOverObject = hitObj->GetId();
1130 else
1131 m_MouseOverObject = CryGUID::Null();
1132 pMouseOverObject = hitObj;
1135 bool bHitSelectedObject = false;
1136 if (pMouseOverObject)
1138 if (GetCommandMode() != SelectMode && !GetIEditor()->IsSelectionLocked())
1140 if (pMouseOverObject->CanBeHightlighted() && GetIEditor()->IsHelpersDisplayed())
1141 pMouseOverObject->SetHighlight(true);
1143 m_cursorStr = pMouseOverObject->GetName();
1145 string comment(pMouseOverObject->GetComment());
1146 if (!comment.IsEmpty())
1148 m_cursorStr += "\n";
1149 m_cursorStr += comment;
1152 if (gViewportDebugPreferences.showMeshStatsOnMouseOver)
1154 const string triangleCountText = pMouseOverObject->GetMouseOverStatisticsText();
1156 if (!triangleCountText.IsEmpty())
1158 m_cursorStr += triangleCountText;
1162 string warnings(pMouseOverObject->GetWarningsText());
1163 if (!warnings.IsEmpty())
1165 m_cursorStr += warnings;
1168 cursor = STD_CURSOR_HIT;
1169 if (pMouseOverObject->IsSelected())
1170 bHitSelectedObject = true;
1173 else
1175 m_cursorStr = "";
1176 cursor = STD_CURSOR_DEFAULT;
1179 const bool bAddSelect = (selectMode == IObjectManager::ESelectOp::eSelect);
1180 const bool bUnselect = (selectMode == IObjectManager::ESelectOp::eUnselect);
1181 const bool bNoRemoveSelection = bAddSelect || bUnselect;
1183 bool bLockSelection = GetIEditor()->IsSelectionLocked();
1185 if (GetCommandMode() == SelectMode || GetCommandMode() == NothingMode)
1187 if (bAddSelect)
1188 cursor = STD_CURSOR_SEL_PLUS;
1189 if (bUnselect)
1190 cursor = STD_CURSOR_SEL_MINUS;
1192 if ((bHitSelectedObject && !bNoRemoveSelection) || bLockSelection)
1194 CLevelEditorSharedState::EditMode editMode = GetIEditor()->GetLevelEditorSharedState()->GetEditMode();
1195 if (editMode == CLevelEditorSharedState::EditMode::Move)
1197 cursor = STD_CURSOR_MOVE;
1199 else if (editMode == CLevelEditorSharedState::EditMode::Rotate)
1201 cursor = STD_CURSOR_ROTATE;
1203 else if (editMode == CLevelEditorSharedState::EditMode::Scale)
1205 cursor = STD_CURSOR_SCALE;
1209 else if (GetCommandMode() == MoveMode)
1211 cursor = STD_CURSOR_MOVE;
1213 else if (GetCommandMode() == RotateMode)
1215 cursor = STD_CURSOR_ROTATE;
1217 else if (GetCommandMode() == ScaleMode)
1219 cursor = STD_CURSOR_SCALE;
1222 view->SetCurrentCursor(cursor, m_cursorStr);
1225 //////////////////////////////////////////////////////////////////////////
1226 // Class description.
1227 //////////////////////////////////////////////////////////////////////////
1228 class CObjectMode_ClassDesc : public IClassDesc
1230 //! This method returns an Editor defined GUID describing the class this plugin class is associated with.
1231 virtual ESystemClassID SystemClassID() { return ESYSTEM_CLASS_EDITTOOL; }
1233 //! This method returns the human readable name of the class.
1234 virtual const char* ClassName() { return "EditTool.ObjectMode"; };
1236 //! This method returns Category of this class, Category is specifing where this plugin class fits best in
1237 //! create panel.
1238 virtual const char* Category() { return "Select"; };
1239 virtual CRuntimeClass* GetRuntimeClass() { return RUNTIME_CLASS(CObjectMode); }
1240 //////////////////////////////////////////////////////////////////////////
1243 REGISTER_CLASS_DESC(CObjectMode_ClassDesc);
1245 //////////////////////////////////////////////////////////////////////////
1246 void CObjectMode::CheckDeepSelection(HitContext& hitContext, CViewport* pWnd)
1248 if (hitContext.pDeepSelection)
1250 m_pDeepSelection->CollectCandidate(hitContext.dist, gDeepSelectionPreferences.deepSelectionRange);
1253 if (m_pDeepSelection->GetCandidateObjectCount() > 1)
1255 // Deep Selection Pop Mode
1256 if (m_pDeepSelection->GetMode() == CDeepSelection::DSM_POP)
1258 CMenu popUpDeepSelect;
1259 popUpDeepSelect.CreatePopupMenu();
1261 for (int i = 0; i < m_pDeepSelection->GetCandidateObjectCount(); ++i)
1263 popUpDeepSelect.AppendMenu(MF_STRING, i + 1, m_pDeepSelection->GetCandidateObject(i)->GetName());
1266 CPoint p;
1267 ::GetCursorPos(&p);
1268 int nSelect = popUpDeepSelect.TrackPopupMenu(TPM_NONOTIFY | TPM_RETURNCMD | TPM_CENTERALIGN, p.x, p.y, CWnd::FromHandle((HWND)pWnd->GetSafeHwnd()), NULL);
1270 if (nSelect > 0)
1272 // Update HitContext hitInfo.
1273 hitContext.object = m_pDeepSelection->GetCandidateObject(nSelect - 1);
1274 m_pDeepSelection->ExcludeHitTest(nSelect - 1);
1277 else if (m_pDeepSelection->GetMode() == CDeepSelection::DSM_CYCLE)
1279 int selPos = m_pDeepSelection->GetCurrentSelectPos();
1280 hitContext.object = m_pDeepSelection->GetCandidateObject(selPos + 1);
1281 m_pDeepSelection->ExcludeHitTest(selPos + 1);
1286 Vec3& CObjectMode::GetScale(const CViewport* view, const CPoint& point, Vec3& OutScale)
1288 float ay = 1.0f - 0.01f * (point.y - m_cMouseDownPos.y);
1290 if (ay < 0.01f) ay = 0.01f;
1292 Vec3 scl(ay, ay, ay);
1294 CLevelEditorSharedState::Axis axisConstraint = GetIEditor()->GetLevelEditorSharedState()->GetAxisConstraint();
1296 switch (axisConstraint)
1298 case CLevelEditorSharedState::Axis::X:
1299 scl(ay, 1, 1);
1300 break;
1301 case CLevelEditorSharedState::Axis::Y:
1302 scl(1, ay, 1);
1303 break;
1304 case CLevelEditorSharedState::Axis::Z:
1305 scl(1, 1, ay);
1306 break;
1307 case CLevelEditorSharedState::Axis::XY:
1308 scl(ay, ay, ay);
1309 break;
1310 case CLevelEditorSharedState::Axis::XZ:
1311 scl(ay, ay, ay);
1312 break;
1313 case CLevelEditorSharedState::Axis::YZ:
1314 scl(ay, ay, ay);
1315 break;
1316 case CLevelEditorSharedState::Axis::XYZ:
1317 scl(ay, ay, ay);
1318 break;
1322 if (gSnappingPreferences.IsSnapToTerrainEnabled())
1323 scl(ay, ay, ay);
1325 OutScale = scl;
1327 return OutScale;
1330 void CObjectMode::OnManipulatorBeginDrag(IDisplayViewport* view, ITransformManipulator* pManipulator, const Vec2i& point, int flags)
1332 m_cMouseDownPos = CPoint(point.x, point.y);
1333 m_bGizmoDrag = true;
1334 CLevelEditorSharedState::EditMode editMode = GetIEditor()->GetLevelEditorSharedState()->GetEditMode();
1336 if (editMode == CLevelEditorSharedState::EditMode::Scale || editMode == CLevelEditorSharedState::EditMode::Move)
1338 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
1339 pSelection->FilterParents();
1340 pSelection->SaveFilteredTransform();
1342 // Hack: Only for move mode, set the command mode to move, so that grid shows up in the viewport
1343 if (editMode == CLevelEditorSharedState::EditMode::Move)
1345 m_bDragThresholdExceeded = false;
1349 if (editMode == CLevelEditorSharedState::EditMode::Move)
1351 m_commandMode = MoveMode;
1353 else if (editMode == CLevelEditorSharedState::EditMode::Scale)
1355 m_commandMode = ScaleMode;
1357 else if (editMode == CLevelEditorSharedState::EditMode::Rotate)
1359 m_commandMode = RotateMode;
1362 ((CViewport*)view)->DegradateQuality(true);
1365 void CObjectMode::OnManipulatorEndDrag(IDisplayViewport* view, ITransformManipulator* pManipulator)
1367 if (m_commandMode == ScaleMode || m_commandMode == MoveMode)
1369 if (m_bDragThresholdExceeded)
1371 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
1372 pSelection->FinishChanges();
1373 m_bDragThresholdExceeded = false;
1376 m_commandMode = NothingMode;
1377 m_bGizmoDrag = false;
1379 ((CViewport*)view)->DegradateQuality(false);
1380 GetIEditor()->UpdateViews(eUpdateObjects);
1383 //////////////////////////////////////////////////////////////////////////
1384 // This callback is currently called only to handle the case of the 'move by the face normal'.
1385 // Other movements of the object are handled in the 'CObjectMode::OnMouseMove()' method.
1386 void CObjectMode::OnManipulatorDrag(IDisplayViewport* view, ITransformManipulator* pManipulator, const Vec2i& point, const Vec3& value, int flags)
1388 CPoint p(point.x, point.y);
1390 if (m_commandMode == MoveMode)
1392 int halfLength = gViewportPreferences.dragSquareSize / 2;
1393 CRect rcDrag(m_cMouseDownPos.x, m_cMouseDownPos.y, m_cMouseDownPos.x, m_cMouseDownPos.y);
1394 InflateRect(rcDrag, halfLength, halfLength);
1396 if (PtInRect(rcDrag, p))
1398 return;
1401 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
1403 if (m_bDragThresholdExceeded)
1405 // All moved objects should have pushed an undo after moving, so suspend here
1406 // if we don't we'll end up with a huge undo step with duplicate entries for the objects.
1407 // TODO: improve undo system to only push when really necessary.
1408 GetIEditor()->GetIUndoManager()->Suspend();
1411 int selectionFlags = ISelectionGroup::eMS_None;
1412 if (gSnappingPreferences.IsSnapToTerrainEnabled())
1413 selectionFlags = ISelectionGroup::eMS_FollowTerrain;
1415 if (gSnappingPreferences.IsSnapToNormalEnabled())
1416 selectionFlags |= ISelectionGroup::eMS_SnapToNormal;
1418 pSelection->Move(value, selectionFlags, p, true);
1420 if (m_pHitObject)
1421 UpdateMoveByFaceNormGizmo(m_pHitObject);
1423 if (m_bDragThresholdExceeded)
1425 GetIEditor()->GetIUndoManager()->Resume();
1427 m_bDragThresholdExceeded = true;
1429 else if (m_commandMode == RotateMode)
1431 GetIEditor()->GetIUndoManager()->Restore();
1432 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
1434 // unfortunately more conversion to fit selectiongroup format
1435 Ang3 angles = RAD2DEG(-value);
1436 pSelection->Rotate(angles);
1438 else if (m_commandMode == ScaleMode)
1440 const ISelectionGroup* pSelection = GetIEditor()->GetISelectionGroup();
1441 pSelection->Scale(value);
1442 m_bDragThresholdExceeded = true;
1446 void CObjectMode::HandleMoveByFaceNormal(HitContext& hitInfo)
1448 CBaseObject* pHitObject = hitInfo.object;
1449 bool bFaceNormalMovePossible = pHitObject && GetIEditor()->GetLevelEditorSharedState()->GetEditMode() == CLevelEditorSharedState::EditMode::Move
1450 && (pHitObject->GetType() == OBJTYPE_SOLID || pHitObject->GetType() == OBJTYPE_BRUSH)
1451 && pHitObject->IsSelected();
1452 bool bNKeyPressed = CheckVirtualKey('N');
1453 if (bFaceNormalMovePossible && bNKeyPressed)
1455 // Test a hit for its faces.
1456 hitInfo.nSubObjFlags = SO_HIT_POINT | SO_HIT_SELECT | SO_HIT_NO_EDIT | SO_HIT_ELEM_FACE;
1457 pHitObject->SetFlags(OBJFLAG_SUBOBJ_EDITING);
1458 pHitObject->HitTest(hitInfo);
1459 pHitObject->ClearFlags(OBJFLAG_SUBOBJ_EDITING);
1461 UpdateMoveByFaceNormGizmo(pHitObject);
1463 else if (m_bMoveByFaceNormManipShown && !bNKeyPressed)
1465 HideMoveByFaceNormGizmo();
1469 void CObjectMode::UpdateMoveByFaceNormGizmo(CBaseObject* pHitObject)
1471 Matrix34 refFrame;
1472 refFrame.SetIdentity();
1473 SubObjectSelectionReferenceFrameCalculator calculator(SO_ELEM_FACE);
1474 pHitObject->CalculateSubObjectSelectionReferenceFrame(&calculator);
1475 if (calculator.GetFrame(refFrame) == false)
1477 HideMoveByFaceNormGizmo();
1479 else
1481 if (!m_normalMoveGizmo)
1483 m_normalMoveGizmo = GetIEditor()->GetGizmoManager()->AddManipulator(&m_normalGizmoOwner);
1485 m_bMoveByFaceNormManipShown = true;
1486 m_pHitObject = pHitObject;
1487 m_normalMoveGizmo->SetCustomTransform(true, refFrame);
1491 void CObjectMode::HideMoveByFaceNormGizmo()
1493 if (m_normalMoveGizmo)
1495 m_normalMoveGizmo->signalBeginDrag.DisconnectAll();
1496 m_normalMoveGizmo->signalDragging.DisconnectAll();
1497 m_normalMoveGizmo->signalEndDrag.DisconnectAll();
1498 GetIEditor()->GetGizmoManager()->RemoveManipulator(m_normalMoveGizmo);
1499 m_normalMoveGizmo = nullptr;
1501 m_bMoveByFaceNormManipShown = false;
1502 m_pHitObject = NULL;
1505 // only display the grid when we actually transform something
1506 bool CObjectMode::IsDisplayGrid()
1508 return
1509 (!m_bGizmoDrag && m_commandMode != NothingMode)
1510 || (m_commandMode == MoveMode);