diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/ToolMorph.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'hammer/ToolMorph.cpp')
| -rw-r--r-- | hammer/ToolMorph.cpp | 2161 |
1 files changed, 2161 insertions, 0 deletions
diff --git a/hammer/ToolMorph.cpp b/hammer/ToolMorph.cpp new file mode 100644 index 0000000..75499f9 --- /dev/null +++ b/hammer/ToolMorph.cpp @@ -0,0 +1,2161 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "stdafx.h" +#include "GlobalFunctions.h" +#include "History.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/imesh.h" +#include "MainFrm.h" +#include "MapDefs.h" +#include "MapDoc.h" +#include "MapSolid.h" +#include "MapView2D.h" +#include "MapView3D.h" +#include "Material.h" +#include "ObjectProperties.h" +#include "ToolManager.h" +#include "ToolMorph.h" +#include "Options.h" +#include "Render2D.h" +#include "StatusBarIDs.h" +#include "hammer.h" +#include "mathlib/vmatrix.h" +#include "vgui/Cursor.h" +#include "Selection.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + + +//----------------------------------------------------------------------------- +// Purpose: Callback function to add objects to the morph selection. +// Input : pSolid - Solid to add. +// pMorph - Morph tool. +// Output : Returns TRUE to continue enumerating. +//----------------------------------------------------------------------------- +static BOOL AddToMorph(CMapSolid *pSolid, Morph3D *pMorph) +{ + pMorph->SelectObject(pSolid, scSelect); + return TRUE; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Morph3D::Morph3D(void) +{ + m_SelectedType = shtNothing; + m_HandleMode = hmBoth; + m_bBoxSelecting = false; + m_bScaling = false; + m_pOrigPosList = NULL; + + m_vLastMouseMovement.Init(); + + m_bHit = false; + m_bUpdateOrg = false; + m_bLButtonDownControlState = false; + + SetDrawColors(Options.colors.clrToolHandle, Options.colors.clrToolMorph); + + memset(&m_DragHandle, 0, sizeof(m_DragHandle)); + m_bMorphing = false; + m_bMovingSelected = false; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Morph3D::~Morph3D(void) +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Morph3D::OnActivate() +{ + if (IsActiveTool()) + { + // + // Already active - change modes and redraw views. + // + ToggleMode(); + } + else + { + // + // Put all selected objects into morph + // + const CMapObjectList *pSelection = m_pDocument->GetSelection()->GetList(); + for (int i = 0; i < pSelection->Count(); i++) + { + CMapClass *pobj = pSelection->Element(i); + if (pobj->IsMapClass(MAPCLASS_TYPE(CMapSolid))) + { + SelectObject((CMapSolid *)pobj, scSelect); + } + pobj->EnumChildren((ENUMMAPCHILDRENPROC)AddToMorph, (DWORD)this, MAPCLASS_TYPE(CMapSolid)); + } + + m_pDocument->SelectObject(NULL, scClear|scSaveChanges ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Can we deactivate this tool. +//----------------------------------------------------------------------------- +bool Morph3D::CanDeactivate( void ) +{ + return CanDeselectList(); +} + +//----------------------------------------------------------------------------- +// Purpose: Called when the tool is deactivated. +// Input : eNewTool - The ID of the tool that is being activated. +//----------------------------------------------------------------------------- +void Morph3D::OnDeactivate() +{ + if (IsScaling()) + { + OnScaleCmd(); + } + + if ( !IsEmpty() ) + { + CUtlVector <CMapClass *>List; + GetMorphingObjects(List); + + // Empty morph tool (Save contents). + SetEmpty(); + + // + // Select the solids that we were morphing. + // + int nObjectCount = List.Count(); + for (int i = 0; i < nObjectCount; i++) + { + CMapClass *pSolid = List.Element(i); + CMapClass *pSelect = pSolid->PrepareSelection(m_pDocument->GetSelection()->GetMode()); + if (pSelect) + { + m_pDocument->SelectObject(pSelect, scSelect); + } + } + + m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the given solid is being morphed, false if not. +// Input : pSolid - The solid. +// pStrucSolidRvl - The corresponding structured solid. +//----------------------------------------------------------------------------- +BOOL Morph3D::IsMorphing(CMapSolid *pSolid, CSSolid **pStrucSolidRvl) +{ + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pSSolid = m_StrucSolids.Element(pos); + if(pSSolid->m_pMapSolid == pSolid) + { + if(pStrucSolidRvl) + pStrucSolidRvl[0] = pSSolid; + return TRUE; + } + } + + return FALSE; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns the bounding box of the objects being morphed. +// Input : bReset - +//----------------------------------------------------------------------------- +void Morph3D::GetMorphBounds(Vector &mins, Vector &maxs, bool bReset) +{ + mins = m_MorphBounds.bmins; + maxs = m_MorphBounds.bmaxs; + + if (bReset) + { + m_MorphBounds.ResetBounds(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Can we deslect the list? All current SSolid with displacements +// are valid. +//----------------------------------------------------------------------------- +bool Morph3D::CanDeselectList( void ) +{ + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pSSolid = m_StrucSolids.Element( pos ); + if ( pSSolid ) + { + if ( !pSSolid->IsValidWithDisps() ) + { + // Ask + if( AfxMessageBox( "Invalid solid, destroy displacement(s)?", MB_YESNO ) == IDYES ) + { + // Destroy the displacement data. + pSSolid->DestroyDisps(); + } + else + { + return false; + } + } + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Selects a solid for vertex manipulation. An SSolid class is created +// and the CMapSolid is attached to it. The map solid is removed from +// the views, since it will be represented by the structured solid +// until vertex manipulation is finished. +// Input : pSolid - Map solid to select. +// cmd - scClear, scToggle, scUnselect +//----------------------------------------------------------------------------- +void Morph3D::SelectObject(CMapSolid *pSolid, UINT cmd) +{ + // construct temporary list to pass to document functions: + CMapObjectList List; + List.AddToTail( pSolid ); + + if( cmd & scClear ) + SetEmpty(); + + CSSolid *pStrucSolid; + if ( IsMorphing( pSolid, &pStrucSolid ) ) + { + if ( cmd & scToggle || cmd & scUnselect ) + { + // stop morphing solid + Vector mins,maxs; + pSolid->GetRender2DBox(mins, maxs); + m_MorphBounds.UpdateBounds(mins, maxs); + pStrucSolid->Convert(FALSE); + pSolid->GetRender2DBox(mins, maxs); + m_MorphBounds.UpdateBounds(mins, maxs); + + pStrucSolid->Detach(); + m_pDocument->SetModifiedFlag(); + + // want to draw in 2d views again + pSolid->SetVisible2D(true); + + // remove from linked list + m_StrucSolids.FindAndRemove(pStrucSolid); + + // make sure none of its handles are selected + for(int i = m_SelectedHandles.Count()-1; i >=0; i--) + { + if(m_SelectedHandles[i].pStrucSolid == pStrucSolid) + { + m_SelectedHandles.Remove(i); + } + } + + delete pStrucSolid; + + pSolid->SetSelectionState(SELECT_NONE); + } + + return; + } + + pStrucSolid = new CSSolid; + + // convert to structured solid + pStrucSolid->Attach(pSolid); + pStrucSolid->Convert(); + + pStrucSolid->ShowHandles(m_HandleMode & hmVertex, m_HandleMode & hmEdge); + + // don't draw this solid in the 2D views anymore + pSolid->SetVisible2D(false); + pSolid->SetSelectionState(SELECT_MORPH); + + // add to list of structured solids + m_StrucSolids.AddToTail(pStrucSolid); +} + + +int Morph3D::HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles ) +{ + if (m_bBoxSelecting) + { + return Box3D::HitTest( pView, ptClient, bTestHandles); + } + + return FALSE; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CompareMorphHandles(const MORPHHANDLE &mh1, const MORPHHANDLE &mh2) +{ + return ((mh1.pMapSolid == mh2.pMapSolid) && + (mh1.pStrucSolid == mh2.pStrucSolid) && + (mh1.ssh == mh2.ssh)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns whether or not the given morph handle is selected. +//----------------------------------------------------------------------------- +bool Morph3D::IsSelected(MORPHHANDLE &mh) +{ + for (int i = 0; i < m_SelectedHandles.Count(); i++) + { + if (CompareMorphHandles(m_SelectedHandles[i], mh)) + { + return true; + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Hit tests against all the handles. Sets the mouse cursor if we are +// over a handle. +// Input : pt - +// pInfo - +// Output : Returns TRUE if the mouse cursor is over a handle, FALSE if not. +//----------------------------------------------------------------------------- +bool Morph3D::MorphHitTest(CMapView *pView, const Vector2D &vPoint, MORPHHANDLE *pInfo) +{ + SSHANDLE hnd = 0; + + bool bIs2D = pView->IsOrthographic(); + + if ( pInfo ) + { + memset(pInfo, 0, sizeof(MORPHHANDLE)); + } + + // check scaling position first + if ( bIs2D && m_bScaling && pInfo) + { + if ( HitRect( pView, vPoint, m_ScaleOrg, 8 ) ) + { + pInfo->ssh = SSH_SCALEORIGIN; + return true; + } + } + + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pStrucSolid = m_StrucSolids.Element(pos); + + // do a hit test on all handles: + if (m_HandleMode & hmVertex) + { + for(int i = 0; i < pStrucSolid->m_nVertices; i++) + { + CSSVertex &v = pStrucSolid->m_Vertices[i]; + + if( HitRect( pView, vPoint, v.pos, HANDLE_RADIUS ) ) + { + hnd = v.id; + break; + } + } + } + + if (!hnd && (m_HandleMode & hmEdge)) + { + for (int i = 0; i < pStrucSolid->m_nEdges; i++) + { + CSSEdge &e = pStrucSolid->m_Edges[i]; + + if( HitRect( pView, vPoint, e.ptCenter, HANDLE_RADIUS ) ) + { + hnd = e.id; + break; + } + } + } + + if (hnd) + { + if ( bIs2D ) + { + SSHANDLEINFO hi; + pStrucSolid->GetHandleInfo(&hi, hnd); + + // see if there is a 2d match that is already selected - if + // there is, select that instead + SSHANDLE hMatch = Get2DMatches( dynamic_cast<CMapView2D*>(pView), pStrucSolid, hi); + + if(hMatch) + hnd = hMatch; + } + + if(pInfo) + { + pInfo->pMapSolid = pStrucSolid->m_pMapSolid; + pInfo->pStrucSolid = pStrucSolid; + pInfo->ssh = hnd; + } + break; + } + } + + return hnd != 0; +} + +void Morph3D::GetHandlePos(MORPHHANDLE *pInfo, Vector& pt) +{ + SSHANDLEINFO hi; + pInfo->pStrucSolid->GetHandleInfo(&hi, pInfo->ssh); + pt = hi.pos; +} + + +//----------------------------------------------------------------------------- +// Purpose: Fills out a list of handles in the given solid that are at the same +// position as the given handle in the current 2D view. +// Input : pStrucSolid - +// hi - +// hAddSimilarList - +// pnAddSimilar - +// Output : Returns a selected handle at the same position as the given handle, +// if one exists, otherwise returns 0. +//----------------------------------------------------------------------------- +SSHANDLE Morph3D::Get2DMatches( CMapView2D *pView, CSSolid *pStrucSolid, SSHANDLEINFO &hi, CUtlVector<SSHANDLE>*pSimilarList) +{ + SSHANDLE hNewMoveHandle = 0; + + int axHorz = pView->axHorz; + int axVert = pView->axVert; + + if(hi.Type == shtVertex) + { + for(int i = 0; i < pStrucSolid->m_nVertices; i++) + { + CSSVertex & v = pStrucSolid->m_Vertices[i]; + + // YWB Fixme, scale olerance to zoom amount? + if( (fabs(hi.pos[axHorz] - v.pos[axHorz]) < 0.5) && + (fabs(hi.pos[axVert] - v.pos[axVert]) < 0.5) ) + { + if(v.m_bSelected) + { + hNewMoveHandle = v.id; + } + + // add it to the array to select + if( pSimilarList ) + pSimilarList->AddToTail(v.id); + } + } + } + else if(hi.Type == shtEdge) + { + for(int i = 0; i < pStrucSolid->m_nEdges; i++) + { + CSSEdge& e = pStrucSolid->m_Edges[i]; + + if( (fabs(hi.pos[axHorz] - e.ptCenter[axHorz]) < 0.5) && + (fabs(hi.pos[axVert] - e.ptCenter[axVert]) < 0.5) ) + { + if(e.m_bSelected) + { + hNewMoveHandle = e.id; + } + + // add it to the array to select + if( pSimilarList ) + pSimilarList->AddToTail(e.id); + } + } + } + + return hNewMoveHandle; +} + + +//----------------------------------------------------------------------------- +// Purpose: Selects all the handles that are of the same type and at the same +// position in the current 2D projection as the given handle. +// Input : pInfo - +// cmd - +// Output : Returns the number of handles that were selected by this call. +//----------------------------------------------------------------------------- +void Morph3D::SelectHandle2D( CMapView2D *pView, MORPHHANDLE *pInfo, UINT cmd) +{ + SSHANDLEINFO hi; + + if ( !pInfo ) + return; + + if( pInfo->ssh == SSH_SCALEORIGIN ) + return; + + if (!pInfo->pStrucSolid->GetHandleInfo(&hi, pInfo->ssh)) + { + // Can't find the handle info, bail. + DeselectHandle(pInfo); + return; + } + + // + // Check to see if there is a same type handle at the same + // 2d coordinates. + // + CUtlVector<SSHANDLE> addSimilarList; + + Get2DMatches( pView, pInfo->pStrucSolid, hi, &addSimilarList ); + + for (int i = 0; i < addSimilarList.Count(); i++) + { + MORPHHANDLE mh; + mh.ssh = addSimilarList[i]; + mh.pStrucSolid = pInfo->pStrucSolid; + mh.pMapSolid = pInfo->pStrucSolid->m_pMapSolid; + + SelectHandle(&mh, cmd); + + if (i == 0) + { + cmd &= ~scClear; + } + } + + m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); + + return; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pInfo - +//----------------------------------------------------------------------------- +void Morph3D::DeselectHandle(MORPHHANDLE *pInfo) +{ + for (int i = 0; i <m_SelectedHandles.Count(); i++) + { + if (!memcmp(&m_SelectedHandles[i], pInfo, sizeof(*pInfo))) + { + m_SelectedHandles.Remove(i); + break; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pInfo - +// cmd - +//----------------------------------------------------------------------------- +void Morph3D::SelectHandle(MORPHHANDLE *pInfo, UINT cmd) +{ + m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); + + if( pInfo && pInfo->ssh == SSH_SCALEORIGIN ) + return; + + if(cmd & scSelectAll) + { + MORPHHANDLE mh; + + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pStrucSolid = m_StrucSolids.Element(pos); + + for(int i = 0; i < pStrucSolid->m_nVertices; i++) + { + CSSVertex& v = pStrucSolid->m_Vertices[i]; + mh.ssh = v.id; + mh.pStrucSolid = pStrucSolid; + mh.pMapSolid = pStrucSolid->m_pMapSolid; + SelectHandle(&mh, scSelect); + } + } + + return; + } + + if(cmd & scClear) + { + // clear handles first + while( m_SelectedHandles.Count()>0) + { + SelectHandle(&m_SelectedHandles[0], scUnselect); + } + } + + if(cmd == scClear) + { + if(m_bScaling) + OnScaleCmd(TRUE); // update scaling + + return; // nothing else to do here + } + + SSHANDLEINFO hi; + if (!pInfo->pStrucSolid->GetHandleInfo(&hi, pInfo->ssh)) + { + // Can't find the handle info, bail. + DeselectHandle(pInfo); + return; + } + + if(hi.Type != m_SelectedType) + SelectHandle(NULL, scClear); // clear selection first + + m_SelectedType = hi.Type; + + bool bAlreadySelected = (hi.p2DHandle->m_bSelected == TRUE); + bool bChanged = false; + + // toggle selection: + if(cmd & scToggle) + { + cmd &= ~scToggle; + cmd |= bAlreadySelected ? scUnselect : scSelect; + } + + if(cmd & scSelect && !(hi.p2DHandle->m_bSelected)) + { + hi.p2DHandle->m_bSelected = TRUE; + bChanged = true; + } + else if(cmd & scUnselect && hi.p2DHandle->m_bSelected) + { + hi.p2DHandle->m_bSelected = FALSE; + bChanged = true; + } + + if(!bChanged) + return; + + if(hi.p2DHandle->m_bSelected) + { + m_SelectedHandles.AddToTail(*pInfo); + } + else + { + DeselectHandle(pInfo); + } + + if(m_bScaling) + OnScaleCmd(TRUE); +} + + +void Morph3D::MoveSelectedHandles(const Vector &Delta) +{ + + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pStrucSolid = m_StrucSolids.Element(pos); + pStrucSolid->MoveSelectedHandles(Delta); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pRender - +//----------------------------------------------------------------------------- +void Morph3D::RenderTool2D(CRender2D *pRender) +{ + pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_SQUARE ); + + for (int nPass = 0; nPass < 2; nPass++) + { + + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pStrucSolid = m_StrucSolids.Element(pos); + + // + // Draw the edges. + // + for (int i = 0; i < pStrucSolid->m_nEdges; i++) + { + CSSEdge *pEdge = & pStrucSolid->m_Edges[i]; + + if (((pEdge->m_bSelected) && (nPass == 0)) || + ((!pEdge->m_bSelected) && (nPass == 1))) + { + continue; + } + + pRender->SetDrawColor( 255, 0, 0 ); + + SSHANDLEINFO hi1; + SSHANDLEINFO hi2; + pStrucSolid->GetHandleInfo(&hi1, pEdge->hvStart); + pStrucSolid->GetHandleInfo(&hi2, pEdge->hvEnd); + + pRender->DrawLine(hi1.pos, hi2.pos); + + if (!(m_HandleMode & hmEdge)) + { + // Don't draw edge handles. + continue; + } + + // Draw the edge center handle. + + if (pEdge->m_bSelected) + { + pRender->SetHandleColor( GetRValue(Options.colors.clrSelection), GetGValue(Options.colors.clrSelection), GetBValue(Options.colors.clrSelection) ); + } + else + { + pRender->SetHandleColor( 255,255,0 ) ; + } + + pRender->DrawHandle( pEdge->ptCenter ); + } + + if (!(m_HandleMode & hmVertex)) + { + // Don't draw vertex handles. + continue; + } + + // + // Draw vertex handles. + bool bClientSpace = pRender->BeginClientSpace(); + + for (int i = 0; i < pStrucSolid->m_nVertices; i++) + { + CSSVertex &v = pStrucSolid->m_Vertices[i]; + + if (((v.m_bSelected) && (nPass == 0)) || + ((!v.m_bSelected) && (nPass == 1))) + { + continue; + } + + if (v.m_bSelected) + { + pRender->SetHandleColor( GetRValue(Options.colors.clrSelection), GetGValue(Options.colors.clrSelection), GetBValue(Options.colors.clrSelection) ); + } + else + { + pRender->SetHandleColor( GetRValue(Options.colors.clrToolHandle), GetGValue(Options.colors.clrToolHandle), GetBValue(Options.colors.clrToolHandle)); + } + + pRender->DrawHandle( v.pos ); + } + + if ( bClientSpace ) + pRender->EndClientSpace(); + + } + } + + // + // Draw scaling point. + // + if (m_bScaling && m_SelectedHandles.Count() ) + { + pRender->SetHandleStyle( 8, CRender::HANDLE_CIRCLE ); + pRender->SetHandleColor( GetRValue(Options.colors.clrToolHandle), GetGValue(Options.colors.clrToolHandle), GetBValue(Options.colors.clrToolHandle) ); + pRender->DrawHandle( m_ScaleOrg ); + } + + if ( m_bBoxSelecting ) + { + Box3D::RenderTool2D(pRender); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Finishes the morph, committing changes made to the selected objects. +//----------------------------------------------------------------------------- +void Morph3D::SetEmpty() +{ + GetHistory()->MarkUndoPosition(NULL, "Morphing"); + + while(m_StrucSolids.Count()>0) + { + // keep getting the head position because SelectObject (below) + // removes the object from the list. + + CSSolid *pStrucSolid = m_StrucSolids[0]; + + // + // Save this solid. BUT, before doing so, set it as visible in the 2D views. + // Otherwise, it will vanish if the user does an "Undo Morphing". + // + pStrucSolid->m_pMapSolid->SetVisible2D(true); + GetHistory()->Keep(pStrucSolid->m_pMapSolid); + pStrucSolid->m_pMapSolid->SetVisible2D(false); + + // calling SelectObject with scUnselect SAVES the contents + // of the morph. + SelectObject(pStrucSolid->m_pMapSolid, scUnselect); + } +} + + +// 3d translation -- +void Morph3D::StartTranslation( CMapView *pView, const Vector2D &vPoint, MORPHHANDLE *pInfo ) +{ + if(m_bScaling) + { + // back to 1 + m_bScaling = false; // don't want it to update here + m_ScaleDlg.m_cScale.SetWindowText("1.0"); + m_bScaling = true; + } + + if(pInfo->ssh == SSH_SCALEORIGIN) + m_OrigHandlePos = m_ScaleOrg; + else + + GetHandlePos(pInfo, m_OrigHandlePos); + + Vector vOrigin, vecHorz, vecVert, vecThird; + pView->GetBestTransformPlane( vecHorz, vecVert, vecThird ); + SetTransformationPlane( m_OrigHandlePos, vecHorz, vecVert, vecThird ); + + // align translation plane to world origin + ProjectOnTranslationPlane( vec3_origin, vOrigin, 0 ); + + // set transformation plane + SetTransformationPlane(vOrigin, vecHorz, vecVert, vecThird ); + + Tool3D::StartTranslation( pView, vPoint, false ); + + m_DragHandle = *pInfo; +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pt - +// uFlags - +// & - +// Output : Returns TRUE on success, FALSE on failure. +//----------------------------------------------------------------------------- +bool Morph3D::UpdateTranslation(const Vector &vUpdate, UINT uFlags) +{ + if (m_bBoxSelecting) + { + return Box3D::UpdateTranslation(vUpdate, uFlags); + } + + if ( !Tool3D::UpdateTranslation( vUpdate, uFlags) ) + return false; + + bool bSnap = uFlags & constrainSnap; + + if (m_DragHandle.ssh == SSH_SCALEORIGIN) + { + m_ScaleOrg = m_OrigHandlePos + vUpdate; + + if (bSnap) + { + m_pDocument->Snap( m_ScaleOrg, uFlags ); + } + + m_bUpdateOrg = false; + + return true; + } + + // + // Get the current handle position. + // + + Vector vCurPos; + GetHandlePos(&m_DragHandle, vCurPos); + + // We don't want to snap edge handles to the grid, because they don't + // necessarily belong on the grid in the first place. + if ( uFlags!=0 ) + { + ProjectOnTranslationPlane( m_OrigHandlePos+m_vTranslation, m_vTranslation, uFlags ); + m_vTranslation -= m_OrigHandlePos; + } + + Vector vDelta = (m_OrigHandlePos+m_vTranslation)-vCurPos; + + // + // Create delta and determine if it is large enough to warrant an update. + // + + if ( vDelta.Length() < 0.5 ) + { + return false; // no need to update. + } + + MoveSelectedHandles( vDelta ); + + return true; +} + +bool Morph3D::StartBoxSelection(CMapView *pView, const Vector2D &vPoint, const Vector& vStart) +{ + m_bBoxSelecting = true; + + SetDrawColors(RGB(255, 255, 255), RGB(50, 255, 255)); + + Box3D::StartNew( pView, vPoint, vStart, Vector(0,0,0) ); + + return true; +} + + +void Morph3D::SelectInBox() +{ + if(!m_bBoxSelecting) + return; + + // select all vertices within the box, and finish box + // selection. + + EndBoxSelection(); // may as well do it here + + // expand box along 0-depth axes + int countzero = 0; + for(int i = 0; i < 3; i++) + { + if(bmaxs[i] - bmins[i] == 0) + { + bmaxs[i] = COORD_NOTINIT; + bmins[i] = -COORD_NOTINIT; + countzero++; + } + } + if(countzero > 1) + return; + + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pStrucSolid = m_StrucSolids.Element(pos); + + for(int i = 0; i < pStrucSolid->m_nVertices; i++) + { + CSSVertex& v = pStrucSolid->m_Vertices[i]; + int i2; + for(i2 = 0; i2 < 3; i2++) + { + if(v.pos[i2] < bmins[i2] || v.pos[i2] > bmaxs[i2]) + break; + } + + if(i2 == 3) + { + // completed loop - intersects - select handle + MORPHHANDLE mh; + mh.ssh = v.id; + mh.pStrucSolid = pStrucSolid; + mh.pMapSolid = pStrucSolid->m_pMapSolid; + SelectHandle(&mh, scSelect); + } + } + } +} + +void Morph3D::EndBoxSelection() +{ + m_bBoxSelecting = false; + m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bSave - +//----------------------------------------------------------------------------- +void Morph3D::FinishTranslation(bool bSave) +{ + if (m_bBoxSelecting) + { + Box3D::FinishTranslation(bSave); + return; + } + else if (bSave && m_DragHandle.ssh != SSH_SCALEORIGIN) + { + // figure out all the affected solids + CUtlVector<CSSolid*> Affected; + + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pStrucSolid = m_StrucSolids.Element(pos); + if(Affected.Find(pStrucSolid) == -1) + Affected.AddToTail(pStrucSolid); + } + + int iConfirm = -1; + FOR_EACH_OBJ( Affected, pos ) + { + CSSolid *pStrucSolid = Affected.Element(pos); + if(pStrucSolid->CanMergeVertices() && iConfirm != 0) + { + if(iConfirm == -1) + { + // ask + if(AfxMessageBox("Merge vertices?", MB_YESNO) == IDYES) + iConfirm = 1; + else + iConfirm = 0; + } + if(iConfirm == 1) + { + int nDeleted; + SSHANDLE *pDeleted = pStrucSolid->MergeSameVertices(nDeleted); + // ensure deleted handles are not marked + for(int i = 0; i < nDeleted; i++) + { + MORPHHANDLE mh; + mh.ssh = pDeleted[i]; + mh.pStrucSolid = pStrucSolid; + mh.pMapSolid = pStrucSolid->m_pMapSolid; + SelectHandle(&mh, scUnselect); + } + } + } +// pStrucSolid->CheckFaces(); + } + } + + Tool3D::FinishTranslation(bSave); + + if(!bSave) + { + // move back to original positions + Vector curpos; + GetHandlePos(&m_DragHandle, curpos); + MoveSelectedHandles(m_OrigHandlePos - curpos); + } + else if(m_bScaling) + { + OnScaleCmd(TRUE); + } +} + + +bool Morph3D::SplitFace() +{ + if(!CanSplitFace()) + return false; + + if(m_SelectedHandles[0].pStrucSolid->SplitFace(m_SelectedHandles[0].ssh, + m_SelectedHandles[1].ssh)) + { + // unselect those invalid edges + if(m_SelectedType == shtVertex) + { + // proper deselection + SelectHandle(NULL, scClear); + } + else // selection is invalid; set count to 0 + m_SelectedHandles.RemoveAll(); + + m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_OBJECTS ); + + return false; + } + + return true; +} + + +bool Morph3D::CanSplitFace() +{ + // along two edges. + if(m_SelectedHandles.Count() != 2 || (m_SelectedType != shtEdge && + m_SelectedType != shtVertex)) + return false; + + // make sure same solid. + if(m_SelectedHandles[0].pStrucSolid != m_SelectedHandles[1].pStrucSolid) + return false; + + return true; +} + + +void Morph3D::ToggleMode() +{ + if(m_HandleMode == hmBoth) + m_HandleMode = hmVertex; + else if(m_HandleMode == hmVertex) + m_HandleMode = hmEdge; + else + m_HandleMode = hmBoth; + + // run through selected solids and tell them the new mode + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pStrucSolid = m_StrucSolids.Element(pos); + pStrucSolid->ShowHandles(m_HandleMode & hmVertex, m_HandleMode & hmEdge); + } + + m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns the center of the morph selection. +// Input : pt - Point at the center of the selection or selected handles. +//----------------------------------------------------------------------------- +void Morph3D::GetSelectedCenter(Vector& pt) +{ + BoundBox box; + + // + // If we have selected handles, our bounds center is the center of those handles. + // + if (m_SelectedHandles.Count() > 0) + { + SSHANDLEINFO hi; + + for (int i = 0; i < m_SelectedHandles.Count(); i++) + { + MORPHHANDLE *mh = &m_SelectedHandles[i]; + mh->pStrucSolid->GetHandleInfo(&hi, mh->ssh); + + box.UpdateBounds(hi.pos); + } + } + // + // If no handles are selected, our bounds center is the center of all selected solids. + // + else + { + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pStrucSolid = m_StrucSolids.Element(pos); + for (int nVertex = 0; nVertex < pStrucSolid->m_nVertices; nVertex++) + { + CSSVertex &v = pStrucSolid->m_Vertices[nVertex]; + box.UpdateBounds(v.pos); + } + } + } + + box.GetBoundsCenter(pt); +} + + +//----------------------------------------------------------------------------- +// Purpose: Fills out a list of the objects selected for morphing. +//----------------------------------------------------------------------------- +void Morph3D::GetMorphingObjects(CUtlVector<CMapClass *> &List) +{ + FOR_EACH_OBJ( m_StrucSolids, pos ) + { + CSSolid *pStrucSolid = m_StrucSolids.Element(pos); + List.AddToTail(pStrucSolid->m_pMapSolid); + } +} + + +void Morph3D::OnScaleCmd(BOOL bReInit) +{ + if(m_pOrigPosList) + { + delete[] m_pOrigPosList; + m_pOrigPosList = NULL; + } + + if(m_bScaling && !bReInit) + { + m_ScaleDlg.ShowWindow(SW_HIDE); + m_ScaleDlg.DestroyWindow(); + m_bScaling = false; + return; + } + + // start scaling + if(!bReInit) + { + m_ScaleDlg.Create(IDD_SCALEVERTICES); + CPoint pt; + GetCursorPos(&pt); + m_bUpdateOrg = true; + + m_ScaleDlg.SetWindowPos(NULL, pt.x, pt.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW); + } + else + { + m_bScaling = false; // don't want an update + m_ScaleDlg.m_cScale.SetWindowText("1.0"); + m_bScaling = true; + } + + if(m_SelectedHandles.Count()==0) + { + m_bScaling = true; + return; + } + + m_pOrigPosList = new Vector[m_SelectedHandles.Count()]; + + BoundBox box; + + // save original positions of vertices + for(int i = 0; i < m_SelectedHandles.Count(); i++) + { + MORPHHANDLE &hnd = m_SelectedHandles[i]; + SSHANDLEINFO hi; + hnd.pStrucSolid->GetHandleInfo(&hi, hnd.ssh); + + if(hi.Type != shtVertex) + continue; + + m_pOrigPosList[i] = hi.pos; + box.UpdateBounds(hi.pos); + } + + // center is default origin + if(m_bUpdateOrg) + box.GetBoundsCenter(m_ScaleOrg); + + m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); + + m_bScaling = true; +} + + +void Morph3D::UpdateScale() +{ + // update scale with data in dialog box + if(!m_bScaling) + return; + + float fScale = m_ScaleDlg.m_fScale; + + // match up selected vertices to original position in m_pOrigPosList. + int iMoved = 0; + for(int i = 0; i < m_SelectedHandles.Count(); i++) + { + MORPHHANDLE &hnd = m_SelectedHandles[i]; + SSHANDLEINFO hi; + hnd.pStrucSolid->GetHandleInfo(&hi, hnd.ssh); + + if(hi.Type != shtVertex) + continue; + + // ** scale ** + Vector& pOrigPos = m_pOrigPosList[iMoved++]; + Vector newpos; + for(int d = 0; d < 3; d++) + { + float delta = pOrigPos[d] - m_ScaleOrg[d]; + // YWB rounding + newpos[d] = /*V_rint*/(m_ScaleOrg[d] + (delta * fScale)); + } + + hnd.pStrucSolid->SetVertexPosition(hi.iIndex, newpos[0], + newpos[1], newpos[2]); + + // find edge that references this vertex + int nEdges; + CSSEdge **pEdges = hnd.pStrucSolid->FindAffectedEdges(&hnd.ssh, 1, + nEdges); + for(int e = 0; e < nEdges; e++) + { + hnd.pStrucSolid->CalcEdgeCenter(pEdges[e]); + } + } + + m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Renders an object that is currently selected for morphing. The +// object is rendered in three passes: +// +// 1. Flat shaded grey with transparency. +// 2. Wireframe in white. +// 3. Edges and/or vertices are rendered as boxes. +// +// Input : pSolid - The structured solid to render. +//----------------------------------------------------------------------------- +void Morph3D::RenderSolid3D(CRender3D *pRender, CSSolid *pSolid) +{ + VMatrix ViewMatrix; + Vector ViewPos; + bool bClientSpace = false; + + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + for (int nPass = 1; nPass <= 3; nPass++) + { + if (nPass == 1) + { + pRender->PushRenderMode( RENDER_MODE_SELECTION_OVERLAY ); + } + else if (nPass == 2) + { + pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); + } + else + { + pRender->PushRenderMode( RENDER_MODE_FLAT_NOZ ); + pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_SQUARE ); + bClientSpace = pRender->BeginClientSpace(); + pRender->GetCamera()->GetViewMatrix( ViewMatrix ); + } + + IMesh* pMesh = pRenderContext->GetDynamicMesh(); + CMeshBuilder meshBuilder; + + int nFaceCount = pSolid->GetFaceCount(); + for (int nFace = 0; nFace < nFaceCount; nFace++) + { + CSSFace *pFace = pSolid->GetFace(nFace); + + int nEdgeCount = pFace->GetEdgeCount(); + + unsigned char color[4]; + if (nPass == 1) + { + meshBuilder.Begin( pMesh, MATERIAL_POLYGON, nEdgeCount ); + color[0] = color[1] = color[2] = color[3] = 128; + } + else if (nPass == 2) + { + meshBuilder.Begin( pMesh, MATERIAL_LINE_LOOP, nEdgeCount ); + color[0] = color[1] = color[2] = color[3] = 255; + } + + for (int nEdge = 0; nEdge < nEdgeCount; nEdge++) + { + // + // Calc next edge so we can see which is the next clockwise point. + // + int nEdgeNext = nEdge + 1; + if (nEdgeNext == nEdgeCount) + { + nEdgeNext = 0; + } + + SSHANDLE hEdge = pFace->GetEdgeHandle(nEdge); + CSSEdge *pEdgeCur = (CSSEdge *)pSolid->GetHandleData(hEdge); + + SSHANDLE hEdgeNext = pFace->GetEdgeHandle(nEdgeNext); + CSSEdge *pEdgeNext = (CSSEdge *)pSolid->GetHandleData(hEdgeNext); + + if (!pEdgeCur || !pEdgeNext) + { + return; + } + + if ((nPass == 1) || (nPass == 2)) + { + SSHANDLE hVertex = pSolid->GetConnectionVertex(pEdgeCur, pEdgeNext); + + if (!hVertex) + { + return; + } + + CSSVertex *pVertex = (CSSVertex *)pSolid->GetHandleData(hVertex); + + if (!pVertex) + { + return; + } + + Vector Vertex; + pVertex->GetPosition(Vertex); + meshBuilder.Position3f(Vertex[0], Vertex[1], Vertex[2]); + meshBuilder.Color4ubv( color ); + meshBuilder.AdvanceVertex(); + } + else + { + if (pSolid->ShowEdges()) + { + // + // Project the edge midpoint into screen space. + // + Vector CenterPoint; + pEdgeCur->GetCenterPoint(CenterPoint); + + ViewMatrix.V3Mul( CenterPoint, ViewPos ); + + if (ViewPos[2] < 0) + { + Vector2D ClientPos; + pRender->TransformPoint(ClientPos, CenterPoint); + + pEdgeCur->m_bVisible = TRUE; + pEdgeCur->m_r.left = ClientPos.x - HANDLE_RADIUS; + pEdgeCur->m_r.top = ClientPos.y - HANDLE_RADIUS; + pEdgeCur->m_r.right = ClientPos.x + HANDLE_RADIUS + 1; + pEdgeCur->m_r.bottom = ClientPos.y + HANDLE_RADIUS + 1; + + if (pEdgeCur->m_bSelected) + { + color[0] = 220; color[1] = color[2] = 0; color[3] = 255; + } + else + { + color[0] = color[1] = 255; color[2] = 0; color[3] = 255; + + } + + // + // Render the edge handle as a box. + // + pRender->SetHandleColor( color[0], color[1], color[2] ); + pRender->DrawHandle( CenterPoint ); + } + else + { + pEdgeCur->m_bVisible = FALSE; + } + } + + if (pSolid->ShowVertices()) + { + SSHANDLE hVertex = pSolid->GetConnectionVertex(pEdgeCur, pEdgeNext); + CSSVertex *pVertex = (CSSVertex *)pSolid->GetHandleData(hVertex); + + // + // Project the vertex into screen space. + // + Vector vPoint; + + pVertex->GetPosition(vPoint); + + ViewMatrix.V3Mul( vPoint, ViewPos ); + + if (ViewPos[2] < 0) + { + Vector2D ClientPos; + pRender->TransformPoint(ClientPos, vPoint); + + pVertex->m_bVisible = TRUE; + pVertex->m_r.left = ClientPos.x - HANDLE_RADIUS; + pVertex->m_r.top = ClientPos.y - HANDLE_RADIUS; + pVertex->m_r.right = ClientPos.x + HANDLE_RADIUS + 1; + pVertex->m_r.bottom = ClientPos.y + HANDLE_RADIUS + 1; + + if (pVertex->m_bSelected) + { + color[0] = 220; color[1] = color[2] = 0; color[3] = 255; + } + else + { + color[0] = color[1] = color[2] = 255; color[3] = 255; + } + + // + // Render the vertex as a box. + // + pRender->SetHandleColor( color[0], color[1], color[2] ); + pRender->DrawHandle( vPoint ); + } + else + { + pVertex->m_bVisible = FALSE; + } + } + } + } + + if ((nPass == 1) || (nPass == 2)) + { + meshBuilder.End(); + pMesh->Draw(); + } + } + + if ( bClientSpace ) + { + pRender->EndClientSpace(); + bClientSpace = false; + } + + pRender->PopRenderMode(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Renders a our selection bounds while we are drag-selecting. +// Input : pRender - Rendering interface. +//----------------------------------------------------------------------------- +void Morph3D::RenderTool3D(CRender3D *pRender) +{ + if (m_bBoxSelecting) + { + Box3D::RenderTool3D(pRender); + } + + for( int pos=0; pos < m_StrucSolids.Count(); pos++ ) + { + RenderSolid3D(pRender, m_StrucSolids[pos] ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles key down events in the 2D view. +// Input : Per CWnd::OnKeyDown. +// Output : Returns true if the message was handled, false if not. +//----------------------------------------------------------------------------- +bool Morph3D::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) +{ + bool bSnap = m_pDocument->IsSnapEnabled() && !(GetKeyState(VK_CONTROL) & 0x8000); + if (nChar == VK_UP || nChar == VK_DOWN || nChar == VK_LEFT || nChar == VK_RIGHT) + { + if ( NudgeHandles( pView, nChar, bSnap ) ) + return true; + } + + switch (nChar) + { + case VK_RETURN: + { + if ( IsBoxSelecting() ) + { + SelectInBox(); + } + break; + } + + case VK_ESCAPE: + { + OnEscape(); + return true; + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles character events in the 2D view. +// Input : Per CWnd::OnChar. +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool Morph3D::OnChar2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) +{ + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles left mouse button down events in the 2D view. +// Input : Per CWnd::OnLButtonDown. +// Output : Returns true if the message was handled, false if not. +//----------------------------------------------------------------------------- +bool Morph3D::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) +{ + Tool3D::OnLMouseDown2D(pView, nFlags, vPoint); + + m_bLButtonDownControlState = (nFlags & MK_CONTROL) != 0; + + m_vLastMouseMovement = vPoint; + + m_DragHandle.ssh = 0; + + MORPHHANDLE mh; + if ( IsBoxSelecting() ) + { + if ( HitTest( pView, vPoint, true ) ) + { + Box3D::StartTranslation( pView, vPoint, m_LastHitTestHandle ); + } + } + else if (MorphHitTest( pView, vPoint, &mh)) + { + // + // If they clicked on a valid handle, remember which one. We may need it in + // left button up or mouse move messages. + // + m_DragHandle = mh; + + if (!m_bLButtonDownControlState) + { + // + // If they are not holding down control and they clicked on an unselected + // handle, select the handle they clicked on straightaway. + // + if (!IsSelected(m_DragHandle)) + { + // Clear the selected handles and select this handle. + UINT cmd = scClear | scSelect; + SelectHandle2D( pView, &m_DragHandle, cmd); + } + } + } + else + { + // Try to put another solid into morph mode. + SelectAt(pView, nFlags, vPoint); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles left mouse button up events in the 2D view. +// Input : Per CWnd::OnLButtonUp. +// Output : Returns true if the message was handled, false if not. +//----------------------------------------------------------------------------- +bool Morph3D::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) +{ + Tool3D::OnLMouseUp2D(pView, nFlags, vPoint); + + if (!IsTranslating()) + { + if (m_DragHandle.ssh != 0) + { + // + // They clicked on a handle and released the left button without moving the mouse. + // Change the selection state of the handle that was clicked on. + // + UINT cmd = scClear | scSelect; + if (m_bLButtonDownControlState) + { + // Control-click: toggle. + cmd = scToggle; + } + SelectHandle2D( pView, &m_DragHandle, cmd); + } + } + else + { + // + // Dragging out a selection box or dragging the selected vertices. + // + FinishTranslation(true); + + if (IsBoxSelecting() && Options.view2d.bAutoSelect) + { + SelectInBox(); + } + } + + m_pDocument->UpdateStatusbar(); + + return true; +} + +unsigned int Morph3D::GetConstraints(unsigned int nFlags) +{ + unsigned int uConstraints = Tool3D::GetConstraints(nFlags); + + if ( !IsBoxSelecting() ) + { + if ( nFlags & MK_CONTROL ) + uConstraints |= constrainOnlyVert; + + if ( nFlags & MK_SHIFT ) + uConstraints |= constrainOnlyHorz; + } + + return uConstraints; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles mouse move events in the 2D view. +// Input : Per CWnd::OnMouseMove. +// Output : Returns true if the message was handled, false if not. +//----------------------------------------------------------------------------- +bool Morph3D::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) +{ + vgui::HCursor hCursor = vgui::dc_none; + + unsigned int uConstraints = GetConstraints( nFlags ); + + Tool3D::OnMouseMove2D(pView, nFlags, vPoint); + + // Convert to world coords. + + Vector vecWorld; + pView->ClientToWorld(vecWorld, vPoint); + + // + // Update status bar position display. + // + char szBuf[128]; + m_pDocument->Snap(vecWorld,uConstraints); + sprintf(szBuf, " @%.0f, %.0f ", vecWorld[pView->axHorz], vecWorld[pView->axVert] ); + SetStatusText(SBI_COORDS, szBuf); + + if ( m_bMouseDown[MOUSE_LEFT] ) + { + if ( IsTranslating() ) + { + // If they are dragging a selection box or one or more handles, update + // the drag based on the cursor position. + + Tool3D::UpdateTranslation( pView, vPoint, uConstraints ); + } + else if ( m_bMouseDragged[MOUSE_LEFT] && m_DragHandle.ssh != 0 ) + { + // + // If they are not already dragging a handle and they clicked on a valid handle, + // see if they have moved the mouse far enough to begin dragging the handle. + // + + if (m_bLButtonDownControlState && !IsSelected(m_DragHandle)) + { + // + // If they control-clicked on an unselected handle and then dragged the mouse, + // select the handle that they clicked on now. + // + SelectHandle2D( pView, &m_DragHandle, scSelect); + } + + StartTranslation( pView, m_vMouseStart[MOUSE_LEFT], &m_DragHandle ); + } + else if ( m_bMouseDragged[MOUSE_LEFT] && !IsBoxSelecting() ) + { + // + // Left dragging, didn't click on a handle, and we aren't yet dragging a + // selection box. Start dragging the selection box. + // + if (!(nFlags & MK_CONTROL)) + { + SelectHandle(NULL, scClear); + } + + Vector ptOrg; + pView->ClientToWorld(ptOrg, m_vMouseStart[MOUSE_LEFT] ); + + // set best third axis value + ptOrg[pView->axThird] = COORD_NOTINIT; + m_pDocument->GetBestVisiblePoint(ptOrg); + StartBoxSelection( pView, m_vMouseStart[MOUSE_LEFT], ptOrg); + } + } + else if (!IsEmpty()) + { + // + // Left button is not down, just see what's under the cursor + // position to update the cursor. + // + + hCursor = vgui::dc_arrow; + + // + // Check to see if the mouse is over a vertex handle. + // + + if (!IsBoxSelecting() && MorphHitTest( pView, vPoint, NULL)) + { + hCursor = vgui::dc_crosshair; + } + // + // Check to see if the mouse is over a box handle. + // + else if ( HitTest(pView, vPoint, true) ) + { + hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode ); + } + } + else + { + hCursor = vgui::dc_arrow; + } + + if ( hCursor != vgui::dc_none ) + pView->SetCursor( hCursor ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles left mouse button down events in the 3D view. +// Input : Per CWnd::OnLButtonDown. +// Output : Returns true if the message was handled, false if not. +//----------------------------------------------------------------------------- +bool Morph3D::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) +{ + m_bHit = false; + + Tool3D::OnLMouseDown3D(pView, nFlags, vPoint); + + // + // Select morph handles? + // + MORPHHANDLE mh; + if ( MorphHitTest(pView, vPoint, &mh) ) + { + m_bHit = true; + m_DragHandle = mh; + m_bMorphing = true; + m_vLastMouseMovement = vPoint; + m_bMovingSelected = false; // not moving them yet - might just select this + StartTranslation( pView, vPoint, &m_DragHandle ); + SetCursor(AfxGetApp()->LoadStandardCursor(IDC_CROSS)); + } + else + { + SelectAt( pView, nFlags, vPoint ); + } + + return true; +} + +bool Morph3D::SelectAt( CMapView *pView, UINT nFlags, const Vector2D &vPoint ) +{ + CMapClass *pMorphObject = NULL; + bool bUpdateView = false; + m_pDocument->GetSelection()->ClearHitList(); + CMapObjectList SelectList; + + // Find out how many (and what) map objects are under the point clicked on. + + HitInfo_t Objects[MAX_PICK_HITS]; + int nHits = pView->ObjectsAt( vPoint, Objects, sizeof(Objects) / sizeof(Objects[0])); + + // We now have an array of pointers to CMapAtoms. Any that can be upcast to CMapClass + // we add to a list of hits. + + for (int i = 0; i < nHits; i++) + { + CMapClass *pMapClass = dynamic_cast <CMapClass *>(Objects[i].pObject); + if (pMapClass != NULL) + { + SelectList.AddToTail(pMapClass); + } + } + + // + // Actual selection occurs here. + // + if (!SelectList.Count()) + { + // + // Clicked on nothing - clear selection. + // + pView->GetMapDoc()->SelectFace(NULL, 0, scClear); + pView->GetMapDoc()->SelectObject(NULL, scClear ); + return false; + } + + bool bFirst = true; + SelectMode_t eSelectMode = m_pDocument->GetSelection()->GetMode(); + + // Can we de-select objects? + if ( !CanDeselectList() ) + return true; + + FOR_EACH_OBJ( SelectList, pos ) + { + CMapClass *pObject = SelectList.Element(pos); + + // get hit object type and add it to the hit list + CMapClass *pHitObject = pObject->PrepareSelection( eSelectMode ); + if (pHitObject) + { + m_pDocument->GetSelection()->AddHit( pHitObject ); + + if (bFirst) + { + if (pObject->IsMapClass(MAPCLASS_TYPE(CMapSolid))) + { + CMapSolid *pSolid = (CMapSolid *)pObject; + + UINT cmd = scClear | scSelect; + if (nFlags & MK_CONTROL) + { + cmd = scToggle; + } + SelectObject(pSolid, cmd); + pMorphObject = pSolid; + bUpdateView = true; + break; + } + } + + bFirst = false; + } + } + + // do we want to deselect all morphs? + if (!pMorphObject && !IsEmpty()) + { + SetEmpty(); + bUpdateView = true; + } + + if (bUpdateView) + { + GetMainWnd()->pObjectProperties->MarkDataDirty(); + m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_SELECTION ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles left mouse button up events in the 3D view. +// Input : Per CWnd::OnLButtonUp. +// Output : Returns true if the message was handled, false if not. +//----------------------------------------------------------------------------- +bool Morph3D::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) +{ + Tool3D::OnLMouseUp3D(pView, nFlags, vPoint); + + if (m_bHit) + { + m_bHit = false; + UINT cmd = scClear | scSelect; + if (nFlags & MK_CONTROL) + { + cmd = scToggle; + } + + SelectHandle(&m_DragHandle, cmd); + } + + if (m_bMorphing) + { + FinishTranslation( true ); + m_bMorphing = false; + } + + ReleaseCapture(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Snap the selected handles to the grid +// Input : +//----------------------------------------------------------------------------- +void Morph3D::SnapSelectedToGrid( int nGridSpacing ) +{ + CUtlVector<MORPHHANDLE> vecHandles; + + if ( GetSelectedHandleCount() != 0 ) + { + // Remember selected verts + vecHandles.AddVectorToTail( m_SelectedHandles ); + } + else + { + // None selected. Do nothing + return; + } + + FOR_EACH_VEC( vecHandles, i ) + { + // Set as sole-selection + SelectHandle( &vecHandles[i], scSelect | scClear ); + + // Get current position + Vector vCurPos; + SSHANDLEINFO hi; + vecHandles[i].pStrucSolid->GetHandleInfo(&hi, vecHandles[i].ssh); + vCurPos = hi.pos; + + // Get snapped position + Vector vSnappedPos( V_rint(vCurPos[0] / nGridSpacing) * nGridSpacing, + V_rint(vCurPos[1] / nGridSpacing) * nGridSpacing, + V_rint(vCurPos[2] / nGridSpacing) * nGridSpacing ); + + // Get delta to move original position into snapped position + Vector vDelta = vSnappedPos - vCurPos; + + // Move! + MoveSelectedHandles( vDelta ); + } + + // Re-select all the handles + SelectHandle( NULL, scClear ); + FOR_EACH_VEC( vecHandles, i ) + { + SelectHandle( &vecHandles[i], scSelect ); + } + + FinishTranslation( true ); + m_pDocument->SetModifiedFlag(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : axes - +// nChar - +// bSnap - +//----------------------------------------------------------------------------- +bool Morph3D::NudgeHandles(CMapView *pView, UINT nChar, bool bSnap) +{ + if ( GetSelectedHandleCount() < 1 || !Options.view2d.bNudge ) + return false; + + Vector vecDelta, vHorz, vVert, vThrd; + pView->GetBestTransformPlane( vHorz, vVert, vThrd ); + m_pDocument->GetNudgeVector( vHorz, vVert, nChar, bSnap, vecDelta); + + if ( bSnap && (GetSelectedHandleCount() == 1) && (GetSelectedType() == shtVertex)) + { + // we have one vertex selected, so make sure + // it's going to snap to grid. + Vector pos; GetSelectedCenter(pos); + + SetTransformationPlane( pos, vHorz, vVert, vThrd ); + + // calculate new delta + ProjectOnTranslationPlane( pos + vecDelta, vecDelta, constrainSnap ); + vecDelta -= pos; + } + + MoveSelectedHandles(vecDelta); + FinishTranslation( true ); // force checking for merges + + m_pDocument->SetModifiedFlag(); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles the key down event in the 3D view. +// Input : Per CWnd::OnKeyDown. +// Output : Returns true if the message was handled, false if not. +//----------------------------------------------------------------------------- +bool Morph3D::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) +{ + bool bSnap = m_pDocument->IsSnapEnabled() && !(GetAsyncKeyState(VK_CONTROL) & 0x8000); + + switch (nChar) + { + case VK_ESCAPE: + { + OnEscape(); + return true; + } + + case VK_UP : + case VK_DOWN : + case VK_LEFT : + case VK_RIGHT : + { + if ( NudgeHandles( pView, nChar, bSnap ) ) + return true; + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles the escape key in the 2D or 3D views. +//----------------------------------------------------------------------------- +void Morph3D::OnEscape(void) +{ + // + // If we're box selecting with the morph tool, stop. + // + if ( IsBoxSelecting() ) + { + EndBoxSelection(); + } + // + // If we have handle(s) selected, deselect them. + // + else if (!IsEmpty() && (GetSelectedHandleCount() != 0)) + { + SelectHandle(NULL, scClear); + } + // + // Stop using the morph tool. + // + else + { + ToolManager()->SetTool(TOOL_POINTER); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Handles the move move event in the 3D view. +// Input : Per CWnd::OnMouseMove. +// Output : Returns true if the message was handled, false if not. +//----------------------------------------------------------------------------- +bool Morph3D::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) +{ + Tool3D::OnMouseMove3D(pView, nFlags, vPoint); + + if (m_bMorphing) + { + // + // Check distance moved since left button down and don't start + // moving unless it's greater than the threshold. + // + if (!m_bMovingSelected) + { + Vector2D sizeMoved = vPoint - m_vLastMouseMovement; + if ((abs(sizeMoved.x) > 3) || (abs(sizeMoved.y) > 3)) + { + m_bMovingSelected = true; + + if (m_bHit) + { + m_bHit = false; + SSHANDLEINFO hi; + m_DragHandle.pStrucSolid->GetHandleInfo(&hi, m_DragHandle.ssh); + unsigned uSelFlags = scSelect; + + if (!(nFlags & MK_CONTROL) && !hi.p2DHandle->m_bSelected) + { + uSelFlags |= scClear; + } + + SelectHandle(&m_DragHandle, uSelFlags); + } + } + else + { + return true; + } + } + + unsigned int uConstraints = GetConstraints( nFlags ); + + Tool3D::UpdateTranslation( pView, vPoint, uConstraints ); + + m_vLastMouseMovement = vPoint; + } + else if ( MorphHitTest(pView, vPoint, NULL )) + { + SetCursor(AfxGetApp()->LoadStandardCursor(IDC_CROSS)); + } + else + { + SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW)); + } + + return true; +}
\ No newline at end of file |