You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2161 lines
51 KiB
2161 lines
51 KiB
//========= 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; |
|
} |