!XT (BREAK-16) (Sandbox) Remove double-newlines at the end of files.
[CRYENGINE.git] / Code / Sandbox / Plugins / CryDesigner / Tools / Edit / ExtrudeTool.cpp
blob96794ca5248ac7d983787fec3f4c105d3330569e
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "ExtrudeTool.h"
5 #include "ViewManager.h"
6 #include "Core/Model.h"
7 #include "Core/Helper.h"
8 #include "Util/HeightManipulator.h"
9 #include "Util/ExtrusionSnappingHelper.h"
10 #include "DesignerEditor.h"
11 #include "Util/OffsetManipulator.h"
12 #include "Objects/DisplayContext.h"
13 #include "Util/MFCUtil.h"
15 namespace Designer
17 void ExtrudeTool::Enter()
19 __super::Enter();
20 m_ec.pushPull = ePP_None;
23 void ExtrudeTool::Serialize(Serialization::IArchive& ar)
25 if (ar.openBlock("ScalePolygon", " "))
27 ar(m_Params.bScalePolygonPhase, "ScalePolygon", "^^Scale Polygon");
28 ar.closeBlock();
30 if (ar.openBlock("LeaveLoopEdges", " "))
32 ar(m_Params.bLeaveLoopEdges, "LeaveLoopEdges", "^^Leave loop edges");
33 ar.closeBlock();
35 if (ar.openBlock("Alignment", " "))
37 ar(m_Params.bAlignment, "Alignment", "^^Alignment to another polygon");
38 ar.closeBlock();
40 DISPLAY_MESSAGE("ALT + LMB : Repeat the previous");
42 m_Phase = eExtrudePhase_Select;
43 s_OffsetManipulator.Invalidate();
46 bool ExtrudeTool::StartPushPull(CViewport* view, UINT nFlags, CPoint point)
48 m_ec.pArgumentModel = NULL;
49 m_ec.pushPull = m_ec.initPushPull = ePP_None;
51 if (m_pSelectedPolygon)
53 GetIEditor()->GetIUndoManager()->Begin();
54 GetModel()->RecordUndo("Designer : Extrusion", GetBaseObject());
56 BrushVec3 vPivot;
57 BrushPlane plane;
58 if (GetModel()->QueryPosition(GetDesigner()->GetRay(), vPivot, &plane))
59 s_HeightManipulator.Init(plane, vPivot);
60 else
61 s_HeightManipulator.Init(plane, m_pSelectedPolygon->GetCenterPosition());
63 m_ec.pCompiler = GetCompiler();
64 m_ec.pObject = GetBaseObject();
65 m_ec.pModel = GetModel();
66 m_ec.pPolygon = m_pSelectedPolygon;
68 return PrepareExtrusion(m_ec);
70 return false;
73 bool ExtrudeTool::PrepareExtrusion(ExtrusionContext& ec)
75 DESIGNER_ASSERT(ec.pPolygon);
76 if (!ec.pPolygon)
77 return false;
79 MODEL_SHELF_RECONSTRUCTOR(ec.pModel);
81 ec.backupPolygons.clear();
82 ec.pModel->QueryPerpendicularPolygons(ec.pPolygon, ec.backupPolygons);
83 MirrorUtil::RemoveMirroredPolygons(ec.backupPolygons);
84 if (ec.pModel->CheckFlag(eModelFlag_Mirror))
86 ec.mirroredBackupPolygons.clear();
87 ec.pModel->QueryPerpendicularPolygons(ec.pPolygon->Clone()->Mirror(ec.pModel->GetMirrorPlane()), ec.mirroredBackupPolygons);
88 MirrorUtil::RemoveNonMirroredPolygons(ec.mirroredBackupPolygons);
91 MakeArgumentBrush(ec);
92 if (ec.pArgumentModel)
94 s_SnappingHelper.SearchForOppositePolygons(ec.pArgumentModel->GetCapPolygon());
95 ec.pModel->SetShelf(eShelf_Base);
96 ec.pModel->RemovePolygon(ec.pPolygon);
97 MirrorUtil::RemoveMirroredPolygon(ec.pModel, ec.pPolygon);
98 ec.bFirstUpdate = true;
99 ec.pModel->InvalidateSmoothingGroups();
100 return true;
103 return false;
106 bool ExtrudeTool::OnLButtonDown(CViewport* view, UINT nFlags, CPoint point)
108 s_SnappingHelper.Init(GetModel());
110 if (Deprecated::CheckVirtualKey(VK_MENU))
112 RepeatPrevAction(view, nFlags, point);
113 return true;
116 if (m_Phase == eExtrudePhase_Select && m_Params.bScalePolygonPhase)
118 if (m_pSelectedPolygon)
120 s_OffsetManipulator.Init(m_pSelectedPolygon->Clone(), GetModel(), true);
121 m_Phase = eExtrudePhase_Resize;
124 else if (m_Phase == eExtrudePhase_Select || (m_Phase == eExtrudePhase_Resize && m_Params.bScalePolygonPhase))
126 m_ec.pArgumentModel = NULL;
127 m_ec.bTouchedMirrorPlane = false;
128 m_Phase = eExtrudePhase_Extrude;
131 return true;
134 void ExtrudeTool::RaiseHeight(const CPoint& point, CViewport* view, int nFlags)
136 BrushFloat fHeight = s_HeightManipulator.Update(GetWorldTM(), view, GetDesigner()->GetRay());
138 m_ec.pArgumentModel->SetHeight(fHeight);
139 m_ec.pArgumentModel->Update(ArgumentModel::eBAU_None);
140 m_ec.bTouchedMirrorPlane = false;
142 if (!GetModel()->CheckFlag(eModelFlag_Mirror))
143 return;
145 PolygonPtr pCapPolygon = m_ec.pArgumentModel->GetCapPolygon();
146 for (int i = 0, iVertexCount(pCapPolygon->GetVertexCount()); i < iVertexCount; ++i)
148 const BrushVec3& v = pCapPolygon->GetPos(i);
149 BrushFloat distFromVertexToMirrorPlane = GetModel()->GetMirrorPlane().Distance(v);
150 if (distFromVertexToMirrorPlane <= -kDesignerEpsilon)
151 continue;
152 BrushFloat distFromVertexMirrorPlaneInCapNormalDir = 0;
153 GetModel()->GetMirrorPlane().HitTest(BrushRay(v, pCapPolygon->GetPlane().Normal()), &distFromVertexMirrorPlaneInCapNormalDir);
154 m_ec.pArgumentModel->SetHeight(fHeight + distFromVertexMirrorPlaneInCapNormalDir);
155 m_ec.pArgumentModel->Update(ArgumentModel::eBAU_None);
156 m_ec.bTouchedMirrorPlane = true;
157 break;
161 void ExtrudeTool::RaiseLowerPolygon(CViewport* view, UINT nFlags, CPoint point)
163 if (!(nFlags & MK_LBUTTON))
164 return;
166 BrushFloat fPrevHeight = m_ec.pArgumentModel->GetHeight();
168 if (!m_Params.bAlignment || AlignHeight(m_ec.pArgumentModel->GetCapPolygon(), view, point) == NULL)
169 RaiseHeight(point, view, nFlags);
171 BrushFloat fHeight = m_ec.pArgumentModel->GetHeight();
172 BrushFloat fHeightDifference(fHeight - fPrevHeight);
173 if (fHeightDifference > 0)
174 m_ec.pushPull = ePP_Pull;
175 else if (fHeightDifference < 0)
176 m_ec.pushPull = ePP_Push;
178 if (m_ec.initPushPull == ePP_None)
179 m_ec.initPushPull = m_ec.pushPull;
181 m_ec.bLeaveLoopEdges = m_Params.bLeaveLoopEdges;
183 CheckBoundary(m_ec);
184 UpdateModel(m_ec);
187 void ExtrudeTool::ResizePolygon(CViewport* view, UINT nFlags, CPoint point)
189 s_OffsetManipulator.UpdateOffset(view, point);
190 m_ec.fScale = s_OffsetManipulator.GetScale();
193 void ExtrudeTool::SelectPolygon(CViewport* view, UINT nFlags, CPoint point)
195 if (GetBaseObject() == NULL)
196 return;
198 DesignerSession* pSession = DesignerSession::GetInstance();
200 int nPolygonIndex(0);
201 if (GetModel()->QueryPolygon(GetDesigner()->GetRay(), nPolygonIndex))
203 PolygonPtr pCandidatePolygon = GetModel()->GetPolygon(nPolygonIndex);
204 if (pCandidatePolygon != m_pSelectedPolygon && !pCandidatePolygon->CheckFlags(ePolyFlag_Mirrored) && !pCandidatePolygon->IsOpen())
206 m_pSelectedPolygon = pCandidatePolygon;
207 m_ec.fScale = 0;
208 pSession->UpdateSelectionMesh(m_pSelectedPolygon, GetCompiler(), GetBaseObject());
211 else
213 m_pSelectedPolygon = NULL;
214 pSession->UpdateSelectionMesh(NULL, GetCompiler(), GetBaseObject());
218 bool ExtrudeTool::OnMouseMove(CViewport* view, UINT nFlags, CPoint point)
220 DesignerSession* pSession = DesignerSession::GetInstance();
222 if (m_Phase == eExtrudePhase_Select)
224 SelectPolygon(view, nFlags, point);
226 else if (m_Phase == eExtrudePhase_Resize)
228 ResizePolygon(view, nFlags, point);
230 else if (m_Phase == eExtrudePhase_Extrude)
232 if (!(nFlags & MK_LBUTTON))
233 return true;
235 if (m_ec.pArgumentModel)
237 RaiseLowerPolygon(view, nFlags, point);
239 else
241 if (StartPushPull(view, nFlags, point))
243 if (s_OffsetManipulator.IsValid())
244 pSession->UpdateSelectionMesh(s_OffsetManipulator.GetScaledPolygon(), GetCompiler(), GetBaseObject());
245 else
246 pSession->UpdateSelectionMesh(m_pSelectedPolygon, GetCompiler(), GetBaseObject());
251 return true;
254 void ExtrudeTool::Display(SDisplayContext& dc)
256 __super::Display(dc);
258 s_OffsetManipulator.Display(dc);
259 s_HeightManipulator.Display(dc);
261 if (m_ec.pArgumentModel && gDesignerSettings.bDisplayDimensionHelper)
263 Matrix34 poppedTM = dc.GetMatrix();
264 dc.PopMatrix();
265 GetBaseObject()->DrawDimensionsImpl(dc, m_ec.pArgumentModel->GetBoundBox());
266 dc.PushMatrix(poppedTM);
270 bool ExtrudeTool::CheckBoundary(ExtrusionContext& ec)
272 if (ec.pushPull == ePP_None || ec.pArgumentModel == NULL)
273 return false;
275 ec.bIsLocatedAtOpposite = false;
277 if (s_SnappingHelper.IsOverOppositePolygon(ec.pArgumentModel->GetCapPolygon(), ec.pushPull))
279 ec.pArgumentModel->SetHeight(s_SnappingHelper.GetNearestDistanceToOpposite(ec.pushPull));
280 ec.pArgumentModel->Update(ArgumentModel::eBAU_None);
281 ec.bIsLocatedAtOpposite = true;
282 return true;
285 return false;
288 void ExtrudeTool::MakeArgumentBrush(ExtrusionContext& ec)
290 if (ec.pPolygon == NULL)
291 return;
293 ec.pArgumentModel = new ArgumentModel(ec.pPolygon, ec.fScale, ec.pObject, &ec.backupPolygons, ec.pModel->GetDB());
294 ec.pArgumentModel->SetHeight(0);
295 ec.pArgumentModel->Update(ArgumentModel::eBAU_None);
298 void ExtrudeTool::FinishPushPull(ExtrusionContext& ec)
300 if (!ec.pArgumentModel)
301 return;
303 if (!ec.bIsLocatedAtOpposite)
305 ec.pModel->MovePolygonsBetweenShelves(eShelf_Construction, eShelf_Base);
307 else
309 s_SnappingHelper.ApplyOppositePolygons(ec.pArgumentModel->GetCapPolygon(), ec.pushPull);
310 ec.pModel->MovePolygonsBetweenShelves(eShelf_Construction, eShelf_Base);
311 DesignerSession::GetInstance()->UpdateSelectionMesh(NULL, ec.pCompiler, ec.pObject, true);
312 MirrorUtil::UpdateMirroredPartWithPlane(ec.pModel, ec.pArgumentModel->GetCapPolygon()->GetPlane());
313 MirrorUtil::UpdateMirroredPartWithPlane(ec.pModel, ec.pArgumentModel->GetCapPolygon()->GetPlane().GetInverted());
314 ec.bIsLocatedAtOpposite = false;
317 Designer::ApplyPostProcess(ec, ePostProcess_ExceptSyncPrefab);
318 ec.pArgumentModel = NULL;
321 bool ExtrudeTool::AlignHeight(PolygonPtr pCapPolygon, CViewport* view, const CPoint& point)
323 PolygonPtr pAlignedPolygon = s_SnappingHelper.FindAlignedPolygon(pCapPolygon, GetWorldTM(), view, point);
324 if (!pAlignedPolygon)
325 return false;
327 BrushFloat updatedHeight = m_ec.pArgumentModel->GetBasePlane().Distance() - pAlignedPolygon->GetPlane().Distance();
328 if (updatedHeight != m_ec.pArgumentModel->GetHeight())
330 if (!CheckBoundary(m_ec))
332 m_ec.pArgumentModel->SetHeight(updatedHeight);
333 m_ec.pArgumentModel->Update(ArgumentModel::eBAU_None);
335 m_ec.pArgumentModel->GetCapPolygon()->UpdatePlane(m_ec.pArgumentModel->GetCurrentCapPlane());
338 return true;
341 bool ExtrudeTool::OnLButtonUp(CViewport* view, UINT nFlags, CPoint point)
343 if (m_Phase != eExtrudePhase_Extrude)
344 return true;
346 if (m_ec.pArgumentModel)
348 if (m_ec.pushPull != ePP_None)
350 m_PrevAction.m_Type = m_ec.pushPull;
351 m_PrevAction.m_Distance = m_ec.pArgumentModel ? m_ec.pArgumentModel->GetHeight() : 0;
352 m_PrevAction.m_Scale = m_ec.fScale;
354 CheckBoundary(m_ec);
355 UpdateSurfaceInfo();
356 FinishPushPull(m_ec);
357 GetIEditor()->GetIUndoManager()->Accept("Designer : Extrude");
358 ApplyPostProcess(ePostProcess_SyncPrefab);
360 else
362 GetIEditor()->GetIUndoManager()->Cancel();
364 s_OffsetManipulator.Invalidate();
365 m_Phase = eExtrudePhase_Select;
367 return true;
370 void ExtrudeTool::Extrude(MainContext& mc, PolygonPtr pPolygon, float fHeight, float fScale)
372 ExtrusionContext ec;
373 ec.pObject = mc.pObject;
374 ec.pCompiler = mc.pCompiler;
375 ec.pModel = mc.pModel;
376 ec.pPolygon = pPolygon;
377 ec.pushPull = ec.initPushPull = fHeight > 0 ? ePP_Pull : ePP_Push;
378 ec.fScale = fScale;
379 ec.bUpdateBrush = false;
380 s_SnappingHelper.Init(ec.pModel);
381 PrepareExtrusion(ec);
382 ec.pArgumentModel->SetHeight(fHeight);
383 ec.pArgumentModel->Update(ArgumentModel::eBAU_None);
384 CheckBoundary(ec);
385 UpdateModel(ec);
386 FinishPushPull(ec);
389 void ExtrudeTool::RepeatPrevAction(CViewport* view, UINT nFlags, CPoint point)
391 if (m_ec.pArgumentModel)
393 m_ec.pArgumentModel = NULL;
394 return;
397 if (std::abs(m_PrevAction.m_Distance) < kDesignerEpsilon || m_PrevAction.m_Type == ePP_None)
398 return;
400 m_ec.fScale = m_PrevAction.m_Scale;
401 if (!StartPushPull(view, nFlags, point))
402 return;
404 m_ec.pushPull = m_PrevAction.m_Type;
405 m_ec.pArgumentModel->SetHeight(m_PrevAction.m_Distance);
406 m_ec.pArgumentModel->Update(ArgumentModel::eBAU_None);
407 m_ec.bLeaveLoopEdges = m_Params.bLeaveLoopEdges;
409 CheckBoundary(m_ec);
410 UpdateModel(m_ec);
411 if (m_ec.pArgumentModel)
413 FinishPushPull(m_ec);
414 GetIEditor()->GetIUndoManager()->Accept("Designer : Extrude");
416 else
418 GetIEditor()->GetIUndoManager()->Cancel();
420 ApplyPostProcess(ePostProcess_SyncPrefab);
421 m_Phase = eExtrudePhase_Select;
422 s_OffsetManipulator.Invalidate();
423 m_ec.pArgumentModel = NULL;
426 void ExtrudeTool::UpdateModel(ExtrusionContext& ec)
428 MODEL_SHELF_RECONSTRUCTOR(ec.pModel);
430 if (!ec.pArgumentModel)
431 return;
433 if (ec.bFirstUpdate)
435 ec.pModel->SetShelf(eShelf_Base);
436 for (int i = 0, iPolygonCount(ec.backupPolygons.size()); i < iPolygonCount; ++i)
437 ec.pModel->RemovePolygon(ec.backupPolygons[i]);
438 for (int i = 0, iPolygonCount(ec.mirroredBackupPolygons.size()); i < iPolygonCount; ++i)
439 ec.pModel->RemovePolygon(ec.mirroredBackupPolygons[i]);
442 ec.pModel->SetShelf(eShelf_Construction);
443 if (ec.bResetShelf)
445 ec.pModel->Clear();
446 for (int i = 0, iPolygonCount(ec.backupPolygons.size()); i < iPolygonCount; ++i)
447 ec.pModel->AddPolygon(ec.backupPolygons[i]->Clone());
450 const BrushPlane& mirrorPlane = ec.pModel->GetMirrorPlane();
451 BrushPlane invertedMirrorPlane = mirrorPlane.GetInverted();
453 std::vector<PolygonPtr> sidePolygons;
454 if (ec.pArgumentModel->GetSidePolygonList(sidePolygons))
456 for (int i = 0, iSidePolygonSize(sidePolygons.size()); i < iSidePolygonSize; ++i)
458 if (mirrorPlane.IsEquivalent(sidePolygons[i]->GetPlane()) || invertedMirrorPlane.IsEquivalent(sidePolygons[i]->GetPlane()))
459 continue;
461 bool bOperated = false;
462 PolygonPtr pSidePolygon = sidePolygons[i];
463 PolygonPtr pSideFlipedPolygon = sidePolygons[i]->Clone()->Flip();
465 std::set<int> removedBackUpPolygons;
467 for (int k = 0, iBackupPolygonSize(ec.backupPolygons.size()); k < iBackupPolygonSize; ++k)
469 bool bIncluded = pSidePolygon->IncludeAllEdges(ec.backupPolygons[k]);
470 bool bFlippedIncluded = pSideFlipedPolygon->IncludeAllEdges(ec.backupPolygons[k]);
471 if (!bIncluded && !bFlippedIncluded)
472 continue;
473 ec.pModel->RemovePolygon(ec.pModel->QueryEquivalentPolygon(ec.backupPolygons[k]));
474 if (bIncluded)
476 pSidePolygon->Subtract(ec.backupPolygons[k]);
477 pSideFlipedPolygon->Subtract(ec.backupPolygons[k]->Clone()->Flip());
479 if (bFlippedIncluded)
481 pSideFlipedPolygon->Subtract(ec.backupPolygons[k]);
482 pSidePolygon->Subtract(ec.backupPolygons[k]->Clone()->Flip());
484 removedBackUpPolygons.insert(k);
487 if (!pSidePolygon->IsValid() || !pSideFlipedPolygon->IsValid())
488 continue;
490 for (int k = 0, iBackupPolygonSize(ec.backupPolygons.size()); k < iBackupPolygonSize; ++k)
492 if (removedBackUpPolygons.find(k) != removedBackUpPolygons.end())
493 continue;
495 EIntersectionType it = Polygon::HasIntersection(ec.backupPolygons[k], pSidePolygon);
496 EIntersectionType itFliped = Polygon::HasIntersection(ec.backupPolygons[k], pSideFlipedPolygon);
498 if (it == eIT_None && itFliped == eIT_None)
499 continue;
501 PolygonPtr pThisSidePolygon = pSidePolygon;
502 if (itFliped == eIT_Intersection || itFliped == eIT_JustTouch && ec.pArgumentModel->GetHeight() < 0)
504 it = itFliped;
505 pThisSidePolygon = pSideFlipedPolygon;
508 if (it == eIT_Intersection || it == eIT_JustTouch && ec.backupPolygons[k]->HasOverlappedEdges(pThisSidePolygon))
510 if (ec.bLeaveLoopEdges)
512 if (ec.pArgumentModel->GetHeight() > 0)
513 ec.pModel->AddPolygon(pThisSidePolygon->Clone());
514 else
515 ec.pModel->AddXORPolygon(pThisSidePolygon, false);
517 else
519 ec.pModel->AddXORPolygon(pThisSidePolygon);
521 bOperated = true;
522 break;
525 if (!bOperated)
527 if (ec.pArgumentModel->GetHeight() < 0)
529 PolygonPtr pFlipPolygon = sidePolygons[i]->Clone()->Flip();
530 ec.pModel->AddPolygon(pFlipPolygon);
532 else
534 ec.pModel->AddPolygon(sidePolygons[i]->Clone());
540 PolygonPtr pCapPolygon(ec.pArgumentModel->GetCapPolygon());
541 if (pCapPolygon)
543 if (!ec.bTouchedMirrorPlane || !Comparison::IsEquivalent(ec.pModel->GetMirrorPlane().Normal(), pCapPolygon->GetPlane().Normal()))
544 ec.pModel->AddPolygon(pCapPolygon);
546 if (GetDesigner() && ec.bUpdateSelectionMesh)
548 Matrix34 worldTM = ec.pObject->GetWorldTM();
549 worldTM.SetTranslation(worldTM.GetTranslation() + worldTM.TransformVector(pCapPolygon->GetPlane().Normal() * (ec.pArgumentModel->GetHeight() + 0.01f)));
550 DesignerSession::GetInstance()->GetSelectionMesh()->SetWorldTM(worldTM);
554 if (ec.bUpdateBrush)
556 Designer::ApplyPostProcess(ec, ePostProcess_Mirror);
557 if (ec.bFirstUpdate)
559 Designer::ApplyPostProcess(ec, ePostProcess_Mesh);
560 ec.bFirstUpdate = false;
562 else
564 ec.pCompiler->Compile(ec.pObject, ec.pModel, eShelf_Construction);
569 void ExtrudeTool::OnEditorNotifyEvent(EEditorNotifyEvent event)
571 __super::OnEditorNotifyEvent(event);
572 switch (event)
574 case eNotify_OnEndUndoRedo:
575 DesignerSession::GetInstance()->UpdateSelectionMesh(NULL, GetCompiler(), GetBaseObject(), true);
576 break;
581 REGISTER_DESIGNER_TOOL_WITH_PROPERTYTREE_PANEL_AND_COMMAND(eDesigner_Extrude, eToolGroup_Edit, "Extrude", ExtrudeTool,
582 extrude, "runs extrude tool", "designer.extrude")