1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
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"
17 void ExtrudeTool::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");
30 if (ar
.openBlock("LeaveLoopEdges", " "))
32 ar(m_Params
.bLeaveLoopEdges
, "LeaveLoopEdges", "^^Leave loop edges");
35 if (ar
.openBlock("Alignment", " "))
37 ar(m_Params
.bAlignment
, "Alignment", "^^Alignment to another polygon");
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());
58 if (GetModel()->QueryPosition(GetDesigner()->GetRay(), vPivot
, &plane
))
59 s_HeightManipulator
.Init(plane
, vPivot
);
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
);
73 bool ExtrudeTool::PrepareExtrusion(ExtrusionContext
& ec
)
75 DESIGNER_ASSERT(ec
.pPolygon
);
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();
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
);
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
;
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
))
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
)
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;
161 void ExtrudeTool::RaiseLowerPolygon(CViewport
* view
, UINT nFlags
, CPoint point
)
163 if (!(nFlags
& MK_LBUTTON
))
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
;
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
)
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
;
208 pSession
->UpdateSelectionMesh(m_pSelectedPolygon
, GetCompiler(), GetBaseObject());
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
))
235 if (m_ec
.pArgumentModel
)
237 RaiseLowerPolygon(view
, nFlags
, point
);
241 if (StartPushPull(view
, nFlags
, point
))
243 if (s_OffsetManipulator
.IsValid())
244 pSession
->UpdateSelectionMesh(s_OffsetManipulator
.GetScaledPolygon(), GetCompiler(), GetBaseObject());
246 pSession
->UpdateSelectionMesh(m_pSelectedPolygon
, GetCompiler(), GetBaseObject());
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();
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
)
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;
288 void ExtrudeTool::MakeArgumentBrush(ExtrusionContext
& ec
)
290 if (ec
.pPolygon
== NULL
)
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
)
303 if (!ec
.bIsLocatedAtOpposite
)
305 ec
.pModel
->MovePolygonsBetweenShelves(eShelf_Construction
, eShelf_Base
);
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
)
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());
341 bool ExtrudeTool::OnLButtonUp(CViewport
* view
, UINT nFlags
, CPoint point
)
343 if (m_Phase
!= eExtrudePhase_Extrude
)
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
;
356 FinishPushPull(m_ec
);
357 GetIEditor()->GetIUndoManager()->Accept("Designer : Extrude");
358 ApplyPostProcess(ePostProcess_SyncPrefab
);
362 GetIEditor()->GetIUndoManager()->Cancel();
364 s_OffsetManipulator
.Invalidate();
365 m_Phase
= eExtrudePhase_Select
;
370 void ExtrudeTool::Extrude(MainContext
& mc
, PolygonPtr pPolygon
, float fHeight
, float fScale
)
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
;
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
);
389 void ExtrudeTool::RepeatPrevAction(CViewport
* view
, UINT nFlags
, CPoint point
)
391 if (m_ec
.pArgumentModel
)
393 m_ec
.pArgumentModel
= NULL
;
397 if (std::abs(m_PrevAction
.m_Distance
) < kDesignerEpsilon
|| m_PrevAction
.m_Type
== ePP_None
)
400 m_ec
.fScale
= m_PrevAction
.m_Scale
;
401 if (!StartPushPull(view
, nFlags
, point
))
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
;
411 if (m_ec
.pArgumentModel
)
413 FinishPushPull(m_ec
);
414 GetIEditor()->GetIUndoManager()->Accept("Designer : Extrude");
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
)
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
);
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()))
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
)
473 ec
.pModel
->RemovePolygon(ec
.pModel
->QueryEquivalentPolygon(ec
.backupPolygons
[k
]));
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())
490 for (int k
= 0, iBackupPolygonSize(ec
.backupPolygons
.size()); k
< iBackupPolygonSize
; ++k
)
492 if (removedBackUpPolygons
.find(k
) != removedBackUpPolygons
.end())
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
)
501 PolygonPtr pThisSidePolygon
= pSidePolygon
;
502 if (itFliped
== eIT_Intersection
|| itFliped
== eIT_JustTouch
&& ec
.pArgumentModel
->GetHeight() < 0)
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());
515 ec
.pModel
->AddXORPolygon(pThisSidePolygon
, false);
519 ec
.pModel
->AddXORPolygon(pThisSidePolygon
);
527 if (ec
.pArgumentModel
->GetHeight() < 0)
529 PolygonPtr pFlipPolygon
= sidePolygons
[i
]->Clone()->Flip();
530 ec
.pModel
->AddPolygon(pFlipPolygon
);
534 ec
.pModel
->AddPolygon(sidePolygons
[i
]->Clone());
540 PolygonPtr
pCapPolygon(ec
.pArgumentModel
->GetCapPolygon());
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
);
556 Designer::ApplyPostProcess(ec
, ePostProcess_Mirror
);
559 Designer::ApplyPostProcess(ec
, ePostProcess_Mesh
);
560 ec
.bFirstUpdate
= false;
564 ec
.pCompiler
->Compile(ec
.pObject
, ec
.pModel
, eShelf_Construction
);
569 void ExtrudeTool::OnEditorNotifyEvent(EEditorNotifyEvent event
)
571 __super::OnEditorNotifyEvent(event
);
574 case eNotify_OnEndUndoRedo
:
575 DesignerSession::GetInstance()->UpdateSelectionMesh(NULL
, GetCompiler(), GetBaseObject(), true);
581 REGISTER_DESIGNER_TOOL_WITH_PROPERTYTREE_PANEL_AND_COMMAND(eDesigner_Extrude
, eToolGroup_Edit
, "Extrude", ExtrudeTool
,
582 extrude
, "runs extrude tool", "designer.extrude")