/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "ObjectCloneTool.h" #include "ObjectTypeBrowser.h" #include "PanelTreeBrowser.h" #include "Viewport.h" #include "ViewManager.h" #include "MainWindow.h" #include <AzToolsFramework/API/ToolsApplicationAPI.h> #include <AzToolsFramework/Metrics/LyEditorMetricsBus.h> #include "QtUtil.h" ////////////////////////////////////////////////////////////////////////// // Class description. ////////////////////////////////////////////////////////////////////////// class CObjectCloneTool_ClassDesc : public CRefCountClassDesc { virtual ESystemClassID SystemClassID() { return ESYSTEM_CLASS_EDITTOOL; } virtual REFGUID ClassID() { // {6A73E865-71DF-4ED0-ABA2-457E66119B35} static const GUID guid = { 0x6a73e865, 0x71df, 0x4ed0,{ 0xab, 0xa2, 0x45, 0x7e, 0x66, 0x11, 0x9b, 0x35 } }; return guid; } virtual QString ClassName() { return "EditTool.Clone"; }; virtual QString Category() { return "EditTool"; }; }; CObjectCloneTool_ClassDesc g_cloneClassDesc; ////////////////////////////////////////////////////////////////////////// CObjectCloneTool::CObjectCloneTool() : m_currentUndoBatch(nullptr) { m_pClassDesc = &g_cloneClassDesc; m_bSetConstrPlane = true; GetIEditor()->SuperBeginUndo(); GetIEditor()->BeginUndo(); m_selection = nullptr; if (!GetIEditor()->GetSelection()->IsEmpty()) { QWaitCursor wait; CloneSelection(); m_selection = GetIEditor()->GetSelection(); m_origin = m_selection->GetCenter(); } GetIEditor()->AcceptUndo("Clone"); GetIEditor()->BeginUndo(); if (!gSettings.deepSelectionSettings.bStickDuplicate) { SetStatusText("Clone object at the same location"); } else { SetStatusText("Left click to clone object"); } } ////////////////////////////////////////////////////////////////////////// CObjectCloneTool::~CObjectCloneTool() { EndUndoBatch(); if (GetIEditor()->IsUndoRecording()) { GetIEditor()->SuperCancelUndo(); } } ////////////////////////////////////////////////////////////////////////// void CObjectCloneTool::CloneSelection() { // Allow component application to intercept cloning behavior. // This is to allow support for "smart" cloning of prefabs, and other contextual features. AZ_Assert(!m_currentUndoBatch, "CloneSelection undo batch already created."); EBUS_EVENT_RESULT(m_currentUndoBatch, AzToolsFramework::ToolsApplicationRequests::Bus, BeginUndoBatch, "Clone Selection"); bool handled = false; EBUS_EVENT(AzToolsFramework::EditorRequests::Bus, CloneSelection, handled); if (handled) { GetIEditor()->GetObjectManager()->CheckAndFixSelection(); return; } // This is the legacy case. We're not cloning AZ entities, so abandon the AZ undo batch. EndUndoBatch(); CSelectionGroup selObjects; CSelectionGroup sel; CSelectionGroup* currSelection = GetIEditor()->GetSelection(); currSelection->Clone(selObjects); GetIEditor()->ClearSelection(); for (int i = 0; i < selObjects.GetCount(); i++) { if (selObjects.GetObject(i)) { GetIEditor()->SelectObject(selObjects.GetObject(i)); } } MainWindow::instance()->setFocus(); } ////////////////////////////////////////////////////////////////////////// void CObjectCloneTool::SetConstrPlane(CViewport* view, const QPoint& point) { Matrix34 originTM; originTM.SetIdentity(); CSelectionGroup* selection = GetIEditor()->GetSelection(); if (selection->GetCount() == 1) { originTM = selection->GetObject(0)->GetWorldTM(); } else if (selection->GetCount() > 1) { originTM = selection->GetObject(0)->GetWorldTM(); Vec3 center = view->SnapToGrid(originTM.GetTranslation()); originTM.SetTranslation(center); } view->SetConstructionMatrix(COORDS_LOCAL, originTM); } //static Vec3 gP1,gP2; ////////////////////////////////////////////////////////////////////////// void CObjectCloneTool::Display(DisplayContext& dc) { //dc.SetColor( 1,1,0,1 ); //dc.DrawBall( gP1,1.1f ); //dc.DrawBall( gP2,1.1f ); } ////////////////////////////////////////////////////////////////////////// bool CObjectCloneTool::MouseCallback(CViewport* view, EMouseEvent event, QPoint& point, int flags) { if (m_selection) { // Set construction plane origin to selection origin. if (m_bSetConstrPlane) { SetConstrPlane(view, point); m_bSetConstrPlane = false; } if (event == eMouseLDown) { // Accept group. Accept(); GetIEditor()->GetSelection()->FinishChanges(); return true; } if (event == eMouseMove) { // Move selection. CSelectionGroup* selection = GetIEditor()->GetSelection(); if (selection != m_selection) { Abort(); } else if (!selection->IsEmpty()) { GetIEditor()->RestoreUndo(); Vec3 v; bool followTerrain = false; CSelectionGroup* pSelection = GetIEditor()->GetSelection(); Vec3 selectionCenter = view->SnapToGrid(pSelection->GetCenter()); int axis = GetIEditor()->GetAxisConstrains(); if (axis == AXIS_TERRAIN) { bool hitTerrain; v = view->ViewToWorld(point, &hitTerrain) - selectionCenter; if (axis == AXIS_TERRAIN) { v = view->SnapToGrid(v); if (hitTerrain) { followTerrain = true; v.z = 0; } } } else { Vec3 p1 = selectionCenter; Vec3 p2 = view->MapViewToCP(point); if (p2.IsZero()) { return true; } v = view->GetCPVector(p1, p2); // Snap v offset to grid if its enabled. view->SnapToGrid(v); } CSelectionGroup::EMoveSelectionFlag selectionFlag = CSelectionGroup::eMS_None; if (followTerrain) { selectionFlag = CSelectionGroup::eMS_FollowTerrain; } // Disable undo recording for these move commands as the only operation we need // to undo is the creation of the new object. Undo commands are queued so it's // possible that the object creation could be undone before attempting to undo // these move operations causing undesired behavior. bool wasRecording = CUndo::IsRecording(); if (wasRecording) { GetIEditor()->SuspendUndo(); } GetIEditor()->GetSelection()->Move(v, selectionFlag, GetIEditor()->GetReferenceCoordSys(), point); if (wasRecording) { GetIEditor()->ResumeUndo(); } } } if (event == eMouseWheel) { CSelectionGroup* selection = GetIEditor()->GetSelection(); if (selection != m_selection) { Abort(); } else if (!selection->IsEmpty()) { double angle = 1; if (view->GetViewManager()->GetGrid()->IsAngleSnapEnabled()) { angle = view->GetViewManager()->GetGrid()->GetAngleSnap(); } for (int i = 0; i < selection->GetCount(); ++i) { CBaseObject* pObj = selection->GetFilteredObject(i); Quat rot = pObj->GetRotation(); rot.SetRotationXYZ(Ang3(0, 0, rot.GetRotZ() + DEG2RAD(flags > 0 ? angle * (-1) : angle))); pObj->SetRotation(rot); } GetIEditor()->AcceptUndo("Rotate Selection"); } } } return true; } ////////////////////////////////////////////////////////////////////////// void CObjectCloneTool::Abort() { EndUndoBatch(); // Abort GetIEditor()->SetEditTool(0); } ////////////////////////////////////////////////////////////////////////// void CObjectCloneTool::Accept(bool resetPosition) { // Close the az undo batch so it can add the appropriate objects to the cry undo stack EndUndoBatch(); if (resetPosition) { GetIEditor()->GetSelection()->MoveTo(m_origin, CSelectionGroup::eMS_None, GetIEditor()->GetReferenceCoordSys()); } if (GetIEditor()->IsUndoRecording()) { GetIEditor()->SuperAcceptUndo("Clone"); } GetIEditor()->SetEditTool(0); } ////////////////////////////////////////////////////////////////////////// void CObjectCloneTool::EndUndoBatch() { if (m_currentUndoBatch) { AzToolsFramework::UndoSystem::URSequencePoint* undoBatch = nullptr; EBUS_EVENT_RESULT(undoBatch, AzToolsFramework::ToolsApplicationRequests::Bus, GetCurrentUndoBatch); AZ_Error("ObjectCloneTool", undoBatch == m_currentUndoBatch, "Undo batch is not in sync."); if (undoBatch == m_currentUndoBatch) { EBUS_EVENT(AzToolsFramework::ToolsApplicationRequests::Bus, EndUndoBatch); } m_currentUndoBatch = nullptr; } } ////////////////////////////////////////////////////////////////////////// void CObjectCloneTool::BeginEditParams(IEditor* ie, int flags) { } ////////////////////////////////////////////////////////////////////////// void CObjectCloneTool::EndEditParams() { if (m_selection) { m_selection->EndEditParams(); } } ////////////////////////////////////////////////////////////////////////// bool CObjectCloneTool::OnKeyDown(CViewport* view, uint32 nChar, uint32 nRepCnt, uint32 nFlags) { if (nChar == VK_ESCAPE) { Abort(); } return false; } #include <ObjectCloneTool.moc>