//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include #include "hammer.h" #include "ToolDisplace.h" #include "MainFrm.h" #include "FaceEditSheet.h" #include "GlobalFunctions.h" #include "MapAtom.h" #include "MapSolid.h" #include "MapView3D.h" #include "History.h" #include "Camera.h" #include "MapDoc.h" #include "ChunkFile.h" #include "ToolManager.h" #include "SculptOptions.h" // memdbgon must be the last include file in a .cpp file!!! #include //============================================================================= //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- CToolDisplace::CToolDisplace() { m_uiTool = DISPTOOL_SELECT; m_uiEffect = DISPPAINT_EFFECT_RAISELOWER; m_uiBrushType = DISPPAINT_BRUSHTYPE_SOFT; m_iPaintChannel = DISPPAINT_CHANNEL_POSITION; m_flPaintValueGeo = 5.0f; m_flPaintValueData = 25.0f; m_iPaintAxis = DISPPAINT_AXIS_FACE; m_vecPaintAxis.Init( 0.0f, 0.0f, 1.0f ); m_bAutoSew = false; m_bSpatial = false; m_flSpatialRadius = 15.0f; m_bSpatialRadius = false; m_bSelectMaskTool = true; m_bGridMaskTool = false; m_bLMBDown = false; m_bRMBDown = false; m_bNudge = false; m_bNudgeInit = false; m_EditDispHandle = EDITDISPHANDLE_INVALID; // load filters from file static char szProgramDir[MAX_PATH]; APP()->GetDirectory( DIR_PROGRAM, szProgramDir ); strcat( szProgramDir, "filters\\dispfilters.txt" ); LoadFilters( szProgramDir ); AddFiltersToManagers(); m_SculptTool = NULL; m_MousePoint.Init( 0.0f, 0.0f ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- CToolDisplace::~CToolDisplace() { // destroy filters m_FilterRaiseLowerMgr.Destroy(); m_FilterRaiseToMgr.Destroy(); m_FilterSmoothMgr.Destroy(); } //----------------------------------------------------------------------------- // Purpose: Called when the tool is activated. // Input : eOldTool - The ID of the previously active tool. //----------------------------------------------------------------------------- void CToolDisplace::OnActivate() { // // initialize masks // CMapDisp::SetSelectMask( m_bSelectMaskTool ); CMapDisp::SetGridMask( m_bGridMaskTool ); } //----------------------------------------------------------------------------- // Purpose: Called when the tool is deactivated. // Input : eNewTool - The ID of the tool that is being activated. //----------------------------------------------------------------------------- void CToolDisplace::OnDeactivate() { // // reset masks // CMapDisp::SetSelectMask( false ); CMapDisp::SetGridMask( false ); if ( m_pDocument->GetTools()->GetActiveToolID() != TOOL_FACEEDIT_MATERIAL ) { // Clear the selected faces when we are deactivated. m_pDocument->SelectFace(NULL, 0, scClear ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline void CToolDisplace::UpdateMapViews( CMapView3D *pView ) { CMapDoc *pDoc = pView->GetMapDoc(); if( pDoc ) { pDoc->SetModifiedFlag(); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline void CToolDisplace::CalcViewCenter( CMapView3D *pView ) { CRect windowRect; pView->GetWindowRect( windowRect ); m_viewCenter = windowRect.CenterPoint(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CToolDisplace::OnLMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint ) { // Set down flags m_bLMBDown = true; if( m_uiTool == DISPTOOL_PAINT_SCULPT ) { m_SculptTool->OnLMouseDown3D( pView, nFlags, vPoint ); m_SculptTool->BeginPaint( pView, vPoint ); ApplySculptSpatialPaintTool( pView, nFlags, vPoint ); // update UpdateMapViews( pView ); return true; } // Selection. if( m_uiTool == DISPTOOL_SELECT || ( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) ) { // handle selection at point HandleSelection( pView, vPoint ); // update UpdateMapViews( pView ); return true; } // Tagging. if ( m_uiTool == DISPTOOL_TAG_WALKABLE || m_uiTool == DISPTOOL_TAG_BUILDABLE || m_uiTool == DISPTOOL_TAG_REMOVE ) { // Do tagging. HandleTagging( pView, vPoint ); return true; } // Resize the spatial painting sphere. if( ( m_uiTool == DISPTOOL_PAINT ) && ( IsSpatialPainting() ) && ( GetAsyncKeyState( VK_MENU ) & 0x8000 ) ) { ResizeSpatialRadius_Activate( pView ); return true; } // Nudging. if( ( m_uiTool == DISPTOOL_PAINT ) && ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) ) { // is the current effect raise/lower (nudge only works in raise/lower mode) if( m_uiEffect == DISPPAINT_EFFECT_RAISELOWER ) { EditDispHandle_t handle = GetHitPos( pView, vPoint ); if( handle != EDITDISPHANDLE_INVALID ) { m_EditDispHandle = handle; Nudge_Activate( pView, handle ); UpdateMapViews( pView ); return true; } } } // Painting. if( m_uiTool == DISPTOOL_PAINT ) { // get hit info EditDispHandle_t handle = GetHitPos( pView, vPoint ); if( handle == EDITDISPHANDLE_INVALID ) return false; m_EditDispHandle = handle; CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( !pDispMgr ) return false; pDispMgr->PreUndo( "Displacement Modifier" ); // Paint using the correct mode. if ( m_bSpatial ) { int nDispCount = pDispMgr->SelectCount(); for ( int iDisp = 0; iDisp < nDispCount; iDisp++ ) { CMapDisp *pDispSelect = pDispMgr->GetFromSelect( iDisp ); if ( pDispSelect ) { pDispSelect->Paint_Init( DISPPAINT_CHANNEL_POSITION ); } } // setup for undo - modifying the displacement (painting) ApplySpatialPaintTool( nFlags, vPoint, pDisp ); } else { // setup for undo - modifying the displacement (painting) pDispMgr->Undo( handle, true ); pDisp = EditDispMgr()->GetDisp( handle ); ApplyPaintTool( nFlags, vPoint, pDisp ); } // update UpdateMapViews( pView ); } return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CToolDisplace::OnLMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint ) { // left button up m_bLMBDown = false; if( m_uiTool == DISPTOOL_PAINT_SCULPT ) { m_SculptTool->OnLMouseUp3D( pView, nFlags, vPoint ); return true; } if ( m_bNudge ) { Nudge_Deactivate(); } if ( m_bSpatialRadius ) { ResizeSpatialRadius_Deactivate(); } IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( pDispMgr ) { pDispMgr->PostUndo(); } return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CToolDisplace::OnRMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint ) { // left button down m_bRMBDown = true; if( m_uiTool == DISPTOOL_PAINT_SCULPT ) { m_SculptTool->OnRMouseDown3D( pView, nFlags, vPoint ); m_SculptTool->BeginPaint( pView, vPoint ); ApplySculptSpatialPaintTool( pView, nFlags, vPoint ); // update UpdateMapViews( pView ); return true; } // // lifting the face normal - painting with the axis set to "Face Normal" // if( ( m_uiTool == DISPTOOL_PAINT ) && ( m_iPaintAxis == DISPPAINT_AXIS_FACE ) && ( GetAsyncKeyState( VK_MENU ) & 0x8000 ) ) { LiftFaceNormal( pView, vPoint ); return true; } // Tagging. if ( m_uiTool == DISPTOOL_TAG_WALKABLE || m_uiTool == DISPTOOL_TAG_BUILDABLE || m_uiTool == DISPTOOL_TAG_REMOVE ) { // Do tagging. HandleTaggingReset( pView, vPoint ); return true; } // // handle the normal paint procedure // if( m_uiTool == DISPTOOL_PAINT ) { // get hit info EditDispHandle_t handle = GetHitPos( pView, vPoint ); if( handle == EDITDISPHANDLE_INVALID ) return false; m_EditDispHandle = handle; CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( !pDispMgr ) return false; pDispMgr->PreUndo( "Displacement Modifier" ); // apply the current displacement tool if ( m_bSpatial ) { int nDispCount = pDispMgr->SelectCount(); for ( int iDisp = 0; iDisp < nDispCount; iDisp++ ) { CMapDisp *pDispSelect = pDispMgr->GetFromSelect( iDisp ); if ( pDispSelect ) { pDispSelect->Paint_Init( DISPPAINT_CHANNEL_POSITION ); } } ApplySpatialPaintTool( nFlags, vPoint, pDisp ); } else { // setup for undo pDispMgr->Undo( handle, true ); pDisp = EditDispMgr()->GetDisp( handle ); ApplyPaintTool( nFlags, vPoint, pDisp ); } // update UpdateMapViews( pView ); } return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CToolDisplace::OnRMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint ) { // left button up m_bRMBDown = false; if( m_uiTool == DISPTOOL_PAINT_SCULPT ) { m_SculptTool->OnRMouseUp3D( pView, nFlags, vPoint ); return true; } IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( pDispMgr ) { pDispMgr->PostUndo(); } return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CToolDisplace::OnMouseMove3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint ) { m_MousePoint = vPoint; if( m_uiTool == DISPTOOL_PAINT_SCULPT ) { m_SculptTool->OnMouseMove3D( pView, nFlags, vPoint ); if( ( m_bLMBDown || m_bRMBDown ) ) { ApplySculptSpatialPaintTool( pView, nFlags, vPoint ); } // update UpdateMapViews( pView ); return true; } // nudging if ( ( m_uiTool == DISPTOOL_PAINT ) && ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) && m_bLMBDown && m_bNudge ) { Nudge_Do(); } // Resizing the spatial sphere. else if ( ( m_uiTool == DISPTOOL_PAINT ) && ( GetAsyncKeyState( VK_MENU ) & 0x8000 ) && m_bLMBDown && m_bSpatialRadius ) { ResizeSpatialRadius_Do(); } // painting else { // get hit info EditDispHandle_t handle = GetHitPos( pView, vPoint ); if( handle == EDITDISPHANDLE_INVALID ) return false; m_EditDispHandle = handle; CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); // // continue with tool operation?! // if( ( m_bLMBDown || m_bRMBDown ) && !( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) && ( m_uiTool == DISPTOOL_PAINT ) ) { if ( m_bSpatial ) { ApplySpatialPaintTool( nFlags, vPoint, pDisp ); } else { ApplyPaintTool( nFlags, vPoint, pDisp ); } } // not nudging anymore -- if we were if( m_bNudge ) { Nudge_Deactivate(); } if ( m_bSpatialRadius ) { ResizeSpatialRadius_Deactivate(); } } // update UpdateMapViews( pView ); return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::LiftFaceNormal( CMapView3D *pView, const Vector2D &vPoint ) { // // check for closest solid object // ULONG ulFace; CMapClass *pObject; if( ( ( pObject = pView->NearestObjectAt( vPoint, ulFace ) ) != NULL ) ) { if( pObject->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) ) { // get the solid CMapSolid *pSolid = ( CMapSolid* )pObject; if( !pSolid ) return; // trace a line and get the normal -- will get a displacement normal // if one exists CMapFace *pFace = pSolid->GetFace( ulFace ); if( !pFace ) return; Vector vRayStart, vRayEnd; pView->GetCamera()->BuildRay( vPoint, vRayStart, vRayEnd ); Vector vHitPos, vHitNormal; if( pFace->TraceLine( vHitPos, vHitNormal, vRayStart, vRayEnd ) ) { // set the paint direction m_vecPaintAxis = vHitNormal; } else { // will default to z if no normal found m_vecPaintAxis.Init( 0.0f, 0.0f, 1.0f ); } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::Nudge_Activate( CMapView3D *pView, EditDispHandle_t dispHandle ) { IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( !pDispMgr ) return; pDispMgr->PreUndo( "Displacement Nudge" ); // Setup paint (nudge) using the correct mode. if ( m_bSpatial ) { int nDispCount = pDispMgr->SelectCount(); for ( int iDisp = 0; iDisp < nDispCount; iDisp++ ) { CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp ); if ( pDisp ) { pDisp->Paint_Init( DISPPAINT_CHANNEL_POSITION ); } } } else { // setup for undo pDispMgr->Undo( dispHandle, true ); } // setup the cursor for "nudging" CalcViewCenter( pView ); SetCursorPos( m_viewCenter.x, m_viewCenter.y ); pView->SetCapture(); // set nudging m_bNudge = true; m_bNudgeInit = true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::Nudge_Deactivate( void ) { ReleaseCapture(); m_bNudge = false; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::Nudge_Do( void ) { CMapDisp *pNudgeDisp = GetEditDisp(); if (pNudgeDisp == NULL) { return; } // // find the greatest delta and "nudge" // // NOTE: using get cursor position, because it is different than the // "point" incoming into mouse move???? // CPoint nudgePos; GetCursorPos( &nudgePos ); CPoint nudgeDelta; nudgeDelta.x = nudgePos.x - m_viewCenter.x; nudgeDelta.y = nudgePos.y - m_viewCenter.y; float delta; if( abs( nudgeDelta.x ) < abs( nudgeDelta.y ) ) { delta = nudgeDelta.y; } else { delta = nudgeDelta.x; } delta = -delta; if ( !IsSpatialPainting() ) { CDispMapImageFilter *pFilter = m_FilterRaiseLowerMgr.GetActiveFilter(); if( !pFilter ) return; // set the dynamic filter data pFilter->m_DataType = DISPPAINT_CHANNEL_POSITION; pFilter->m_Scale = ( delta * 0.25 ) * ( float )( ( int )( m_flPaintValueGeo / 10.0f ) + 1 ) ; // apply the filter to the displacement surface(s) m_FilterRaiseLowerMgr.Apply( pFilter, pNudgeDisp, m_iPaintAxis, m_vecPaintAxis, m_bAutoSew ); } else { // Get the hit index and check for validity. int iHit = pNudgeDisp->GetTexelHitIndex(); if ( iHit != -1 ) { // Initialize the spatial paint data. SpatialPaintData_t spatialData; spatialData.m_nEffect = DISPPAINT_EFFECT_RAISELOWER; spatialData.m_uiBrushType = m_uiBrushType; spatialData.m_flRadius = m_flSpatialRadius; spatialData.m_flScalar = delta; spatialData.m_bNudge = true; spatialData.m_bNudgeInit = m_bNudgeInit; pNudgeDisp->GetVert( iHit, spatialData.m_vCenter ); VectorCopy( m_vecPaintAxis, spatialData.m_vPaintAxis ); m_DispPaintMgr.Paint( spatialData, m_bAutoSew ); // Done with the init. m_bNudgeInit = false; } } // reset the cursor pos SetCursorPos( m_viewCenter.x, m_viewCenter.y ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::ApplyPaintTool( UINT nFlags, const Vector2D &vPoint, CMapDisp *pDisp ) { switch( m_uiEffect ) { case DISPPAINT_EFFECT_RAISELOWER: { CDispMapImageFilter *pFilter = m_FilterRaiseLowerMgr.GetActiveFilter(); if( pFilter ) { pFilter->m_DataType = m_iPaintChannel; if ( m_iPaintChannel == DISPPAINT_CHANNEL_POSITION ) { pFilter->m_Scale = m_flPaintValueGeo; } else if ( m_iPaintChannel == DISPPAINT_CHANNEL_ALPHA ) { pFilter->m_Scale = m_flPaintValueData; } if( m_bRMBDown ) { pFilter->m_Scale = -pFilter->m_Scale; } // apply the filter to the displacement surface(s) m_FilterRaiseLowerMgr.Apply( pFilter, pDisp, m_iPaintAxis, m_vecPaintAxis, m_bAutoSew ); } return; } case DISPPAINT_EFFECT_MODULATE: { // no modulate filters or filter manager currently! return; } case DISPPAINT_EFFECT_SMOOTH: { CDispMapImageFilter *pFilter = m_FilterSmoothMgr.GetActiveFilter(); if( pFilter ) { pFilter->m_DataType = m_iPaintChannel; pFilter->m_Scale = 1.0f; int areaValue = 3; if ( m_iPaintChannel == DISPPAINT_CHANNEL_POSITION ) { areaValue = ( m_flPaintValueGeo * 2 ) + 1; } else if ( m_iPaintChannel == DISPPAINT_CHANNEL_ALPHA ) { areaValue = ( m_flPaintValueData * 2 ) + 1; } if( areaValue < 3 ) { areaValue = 3; } if( areaValue > 7 ) { areaValue = 7; } pFilter->m_AreaHeight = areaValue; pFilter->m_AreaWidth = areaValue; // apply the filter to the displacement surface(s) m_FilterSmoothMgr.Apply( pFilter, pDisp, m_iPaintAxis, m_vecPaintAxis, m_bAutoSew ); } return; } case DISPPAINT_EFFECT_RAISETO: { CDispMapImageFilter *pFilter = m_FilterRaiseToMgr.GetActiveFilter(); if( pFilter ) { pFilter->m_DataType = m_iPaintChannel; if ( m_iPaintChannel == DISPPAINT_CHANNEL_POSITION ) { pFilter->m_Scale = m_flPaintValueGeo; } else if ( m_iPaintChannel == DISPPAINT_CHANNEL_ALPHA ) { pFilter->m_Scale = m_flPaintValueData; } // apply the filter to the displacement surface(s) m_FilterRaiseToMgr.Apply( pFilter, pDisp, m_iPaintAxis, m_vecPaintAxis, m_bAutoSew ); } return; } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::ApplySpatialPaintTool( UINT nFlags, const Vector2D &vPoint, CMapDisp *pDisp ) { // Right mouse button only used to paint in a Raise/Lower situation. if ( ( m_uiEffect != DISPPAINT_EFFECT_RAISELOWER ) && m_bRMBDown ) return; // Get the hit index and check for validity. int iHit = pDisp->GetTexelHitIndex(); if ( iHit == -1 ) return; // Initialize the spatial paint data. SpatialPaintData_t spatialData; spatialData.m_nEffect = m_uiEffect; spatialData.m_uiBrushType = m_uiBrushType; spatialData.m_flRadius = m_flSpatialRadius; spatialData.m_flScalar = m_flPaintValueGeo; spatialData.m_bNudge = false; if ( m_bRMBDown ) { spatialData.m_flScalar = -spatialData.m_flScalar; } pDisp->GetVert( iHit, spatialData.m_vCenter ); VectorCopy( m_vecPaintAxis, spatialData.m_vPaintAxis ); m_DispPaintMgr.Paint( spatialData, m_bAutoSew ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::ApplySculptSpatialPaintTool( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint ) { // Initialize the spatial paint data. SpatialPaintData_t spatialData; spatialData.m_vCenter.Init(); // get hit info EditDispHandle_t handle = GetHitPos( pView, vPoint ); if( handle != EDITDISPHANDLE_INVALID ) { m_EditDispHandle = handle; CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); // Get the hit index and check for validity. int iHit = pDisp->GetTexelHitIndex(); if ( iHit != -1 ) { pDisp->GetVert( iHit, spatialData.m_vCenter ); } } spatialData.m_nEffect = m_uiEffect; spatialData.m_uiBrushType = m_uiBrushType; spatialData.m_flRadius = m_flSpatialRadius; spatialData.m_flScalar = m_flPaintValueGeo; spatialData.m_bNudge = false; if ( m_bRMBDown ) { spatialData.m_flScalar = -spatialData.m_flScalar; } VectorCopy( m_vecPaintAxis, spatialData.m_vPaintAxis ); m_SculptTool->Paint( pView, vPoint, spatialData ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::ResizeSpatialRadius_Activate( CMapView3D *pView ) { // Calculate the center of the view and capture the mouse cursor. CalcViewCenter( pView ); SetCursorPos( m_viewCenter.x, m_viewCenter.y ); pView->SetCapture(); m_bSpatialRadius = true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::ResizeSpatialRadius_Deactivate( void ) { ReleaseCapture(); m_bSpatialRadius = false; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::ResizeSpatialRadius_Do( void ) { CPoint cursorPos; GetCursorPos( &cursorPos ); // Calculate the delta between the cursor from last frame and this one. CPoint cursorDelta; cursorDelta.x = cursorPos.x - m_viewCenter.x; cursorDelta.y = cursorPos.y - m_viewCenter.y; float flDelta; if( abs( cursorDelta.x ) < abs( cursorDelta.y ) ) { flDelta = cursorDelta.y; } else { flDelta = cursorDelta.x; } flDelta = -flDelta; // Adjust the sphere radius. m_flSpatialRadius += flDelta; // reset the cursor pos SetCursorPos( m_viewCenter.x, m_viewCenter.y ); // // Update the paint dialog. // CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet(); if( pSheet ) { pSheet->m_DispPage.UpdatePaintDialogs(); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::HandleSelection( CMapView3D *pView, const Vector2D &vPoint ) { // // check for closest solid object // ULONG ulFace; CMapClass *pObject; bool bShift = ( ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) != 0 ); if( ( ( pObject = pView->NearestObjectAt( vPoint, ulFace ) ) != NULL ) ) { if( pObject->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) ) { // get the solid CMapSolid *pSolid = ( CMapSolid* )pObject; // setup initial command state int cmd = scToggle | scClear; // // don't "clear" if CTRL is pressed // if( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) { cmd &= ~scClear; } CMapDoc *pDoc = pView->GetMapDoc(); if( !pDoc ) return; // If they are holding down SHIFT, select the entire solid. if ( bShift ) { pDoc->SelectFace( pSolid, -1, cmd ); } // Otherwise, select a single face. else { pDoc->SelectFace( pSolid, ulFace, cmd ); } } } } //----------------------------------------------------------------------------- // Purpose: Handle the overriding of displacement triangle tag. //----------------------------------------------------------------------------- void CToolDisplace::HandleTagging( CMapView3D *pView, const Vector2D &vPoint ) { // Get the displacement face (if any) at the 2d point. ULONG ulFace; CMapClass *pObject = NULL; if( ( ( pObject = pView->NearestObjectAt( vPoint, ulFace ) ) != NULL ) ) { if( pObject->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) ) { // Get the face and check for a displacement. CMapSolid *pSolid = ( CMapSolid* )pObject; CMapFace *pFace = pSolid->GetFace( ( int )ulFace ); if ( pFace && pFace->HasDisp() ) { EditDispHandle_t hDisp = pFace->GetDisp(); CMapDisp *pDisp = EditDispMgr()->GetDisp( hDisp ); Vector vecStart, vecEnd; pView->GetCamera()->BuildRay( vPoint, vecStart, vecEnd ); float flFraction; int iTri = pDisp->CollideWithDispTri( vecStart, vecEnd, flFraction ); if ( iTri != -1 ) { if ( m_uiTool == DISPTOOL_TAG_WALKABLE ) { if ( pDisp->IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) ) { pDisp->ToggleTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_VAL ); } else { pDisp->SetTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_BIT ); if ( !pDisp->IsTriTag( iTri, COREDISPTRI_TAG_WALKABLE ) ) { pDisp->SetTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_VAL ); } else { pDisp->ResetTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_VAL ); } } pDisp->UpdateWalkable(); } else if ( m_uiTool == DISPTOOL_TAG_BUILDABLE ) { if ( pDisp->IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) ) { pDisp->ToggleTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ); } else { pDisp->SetTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ); if ( !pDisp->IsTriTag( iTri, COREDISPTRI_TAG_BUILDABLE ) ) { pDisp->SetTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ); } else { pDisp->ResetTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ); } } pDisp->UpdateBuildable(); } else if ( m_uiTool == DISPTOOL_TAG_REMOVE ) { HandleTaggingRemove( pDisp, iTri ); } } } } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CToolDisplace::HandleTaggingRemove( CMapDisp *pDisp, int nTriIndex ) { pDisp->ToggleTriTag( nTriIndex, COREDISPTRI_TAG_FORCE_REMOVE_BIT ); pDisp->UpdateTriRemove(); } //----------------------------------------------------------------------------- // Purpose: Handle the overriding of displacement triangle tag. //----------------------------------------------------------------------------- void CToolDisplace::HandleTaggingReset( CMapView3D *pView, const Vector2D &vPoint ) { // Get the displacement face (if any) at the 2d point. ULONG ulFace; CMapClass *pObject = NULL; if( ( ( pObject = pView->NearestObjectAt( vPoint, ulFace ) ) != NULL ) ) { if( pObject->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) ) { // Get the face and check for a displacement. CMapSolid *pSolid = ( CMapSolid* )pObject; CMapFace *pFace = pSolid->GetFace( ( int )ulFace ); if ( pFace && pFace->HasDisp() ) { EditDispHandle_t hDisp = pFace->GetDisp(); CMapDisp *pDisp = EditDispMgr()->GetDisp( hDisp ); Vector vecStart, vecEnd; pView->GetCamera()->BuildRay( vPoint, vecStart, vecEnd ); float flFraction; int iTri = pDisp->CollideWithDispTri( vecStart, vecEnd, flFraction ); if ( iTri != -1 ) { if ( m_uiTool == DISPTOOL_TAG_WALKABLE ) { pDisp->ResetTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_BIT ); pDisp->UpdateWalkable(); } else if ( m_uiTool == DISPTOOL_TAG_BUILDABLE ) { pDisp->ResetTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ); pDisp->UpdateBuildable(); } else if ( m_uiTool == DISPTOOL_TAG_REMOVE ) { pDisp->ResetTriTag( iTri, COREDISPTRI_TAG_FORCE_REMOVE_BIT ); pDisp->UpdateBuildable(); } } } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- EditDispHandle_t CToolDisplace::GetHitPos( CMapView3D *pView, const Vector2D &vPoint ) { // // get ray info // Vector rayStart, rayEnd; pView->GetCamera()->BuildRay( vPoint, rayStart, rayEnd ); // generate selected displacement list int dispCount = GetSelectedDisps(); if( dispCount == 0 ) return NULL; // collide against all "active" displacements and set texel hit data return CollideWithSelectedDisps( rayStart, rayEnd ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int CToolDisplace::GetSelectedDisps( void ) { // // get a valid displacement manager // IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( !pDispMgr ) return 0; // clear the selection list pDispMgr->SelectClear(); // // add all selected displacements to "displacement manager"'s selection list // CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet(); if( !pSheet ) return 0; int faceCount = pSheet->GetFaceListCount(); for( int i = 0; i < faceCount; i++ ) { CMapFace *pFace = pSheet->GetFaceListDataFace( i ); if( !pFace ) continue; if( pFace->HasDisp() ) { EditDispHandle_t handle = pFace->GetDisp(); CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); pDisp->ResetTexelHitIndex(); pDispMgr->AddToSelect( handle ); } } // return the number of displacements in list return pDispMgr->SelectCount(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- EditDispHandle_t CToolDisplace::CollideWithSelectedDisps( const Vector &rayStart, const Vector &rayEnd ) { EditDispHandle_t handle = EDITDISPHANDLE_INVALID; float minDist = 99999.9f; int minIndex = -1; // // get a valid displacement manager // IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( !pDispMgr ) return NULL; int dispCount = pDispMgr->SelectCount(); for( int i = 0; i < dispCount; i++ ) { // get the current displacement CMapDisp *pDisp = pDispMgr->GetFromSelect( i ); if( !pDisp ) continue; bool bCollide = RayAABBTest( pDisp, rayStart, rayEnd ); if( bCollide ) { Vector point; int size = pDisp->GetSize(); for( int j = 0; j < size; j++ ) { // get current point pDisp->GetVert( j, point ); // find point closest to ray float dist = DistFromPointToRay( rayStart, rayEnd, point ); if( dist < minDist ) { CMapFace *pFace = ( CMapFace* )pDisp->GetParent(); handle = pFace->GetDisp(); minDist = dist; minIndex = j; } } } } // // set the texel hit index // if( handle != EDITDISPHANDLE_INVALID ) { CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); pDisp->SetTexelHitIndex( minIndex ); } return handle; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CToolDisplace::RayAABBTest( CMapDisp *pDisp, const Vector &rayStart, const Vector &rayEnd ) { // // make planes // PLANE planes[6]; Vector boxMin, boxMax; pDisp->GetBoundingBox( boxMin, boxMax ); BuildParallelepiped( boxMin, boxMax, planes ); bool bCollide = false; for( int planeIndex = 0; planeIndex < 6; planeIndex++ ) { bCollide = RayPlaneTest( &planes[planeIndex], rayStart, rayEnd ); if( !bCollide ) return false; } return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::BuildParallelepiped( const Vector &boxMin, const Vector &boxMax, PLANE planes[6] ) { int planeIndex = 0; for( int axis = 0; axis < 3; axis++ ) { for( int direction = -1; direction < 2; direction += 2 ) { // clear the current plane info VectorClear( planes[planeIndex].normal ); planes[planeIndex].normal[axis] = direction; if( direction == 1 ) { planes[planeIndex].dist = boxMax[axis]; } else { planes[planeIndex].dist = -boxMin[axis]; } planeIndex++; } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CToolDisplace::RayPlaneTest( PLANE *pPlane, const Vector& rayStart, const Vector& rayEnd /*, float *fraction*/ ) { // // get the distances both trace start and end from the bloated plane // float distStart = DotProduct( rayStart, pPlane->normal ) - pPlane->dist; float distEnd = DotProduct( rayEnd, pPlane->normal ) - pPlane->dist; // // no collision - both points are in front or behind of the given plane // if( ( distStart > 0.0f ) && ( distEnd > 0.0f ) ) return false; if( ( distStart > 0.0f ) && ( distEnd > 0.0f ) ) return false; // calculate the parameterized "t" component along the ray //*fraction = distStart / ( distStart - distEnd ); // collision return true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- float CToolDisplace::DistFromPointToRay( const Vector& rayStart, const Vector& rayEnd, const Vector& point ) { // // calculate the ray // Vector ray; VectorSubtract( rayEnd, rayStart, ray ); VectorNormalize( ray ); // // get a ray to point // Vector seg; VectorSubtract( point, rayStart, seg ); // // project point segment onto ray - get perpendicular point // float value = DotProduct( ray, seg ); VectorScale( ray, value, ray ); VectorAdd( rayStart, ray, ray ); // // find the distance between the perpendicular point and point // VectorSubtract( ray, point, seg ); float dist = VectorLength( seg ); return dist; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CToolDisplace::AddFiltersToManagers( void ) { int count = m_FilterLoaderMgr.GetFilterCount(); for( int ndxFilter = 0; ndxFilter < count; ndxFilter++ ) { CDispMapImageFilter *pFilter = m_FilterLoaderMgr.GetFilter( ndxFilter ); if( pFilter ) { switch( pFilter->m_Type ) { case DISPPAINT_EFFECT_RAISELOWER: { m_FilterRaiseLowerMgr.Add( pFilter ); break; } case DISPPAINT_EFFECT_RAISETO: { m_FilterRaiseToMgr.Add( pFilter ); break; } case DISPPAINT_EFFECT_SMOOTH: { m_FilterSmoothMgr.Add( pFilter ); break; } } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- bool CToolDisplace::LoadFilters( const char *filename ) { // // Open the file. // CChunkFile File; ChunkFileResult_t eResult = File.Open( filename, ChunkFile_Read ); if( eResult != ChunkFile_Ok ) { Msg( mwError, "Couldn't load filter file %s!\n", filename ); } // // Read the file. // if (eResult == ChunkFile_Ok) { // // Set up handlers for the subchunks that we are interested in. // CChunkHandlerMap Handlers; Handlers.AddHandler( "Filter", ( ChunkHandler_t )CToolDisplace::LoadFiltersCallback, this ); File.PushHandlers( &Handlers ); // // Read the sub-chunks. We ignore keys in the root of the file, so we don't pass a // key value callback to ReadChunk. // while (eResult == ChunkFile_Ok) { eResult = File.ReadChunk(); } if (eResult == ChunkFile_EOF) { eResult = ChunkFile_Ok; } File.PopHandlers(); } return( eResult == ChunkFile_Ok ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- ChunkFileResult_t CToolDisplace::LoadFiltersCallback( CChunkFile *pFile, CToolDisplace *pDisplaceTool ) { // // allocate a new filter // CDispMapImageFilter *pFilter = pDisplaceTool->m_FilterLoaderMgr.Create(); if( !pFilter ) return ChunkFile_Fail; // load the filter data ChunkFileResult_t eResult = pFilter->LoadFilter( pFile ); return( eResult ); } //----------------------------------------------------------------------------- // Purpose: // Input : *pRender - // *pTool - //----------------------------------------------------------------------------- void CToolDisplace::RenderPaintSphere( CRender3D *pRender ) { CMapDisp *pDisp = GetEditDisp(); if (pDisp == NULL) return; // Get the sphere center. int iHit = pDisp->GetTexelHitIndex(); if ( iHit == -1 ) return; // Get the sphere center and radius. Vector vCenter; pDisp->GetVert( iHit, vCenter ); float flRadius = GetSpatialRadius(); int size = ( int )( flRadius * 0.05f ); if ( size < 6 ) { size = 6; } if ( size > 12 ) { size = 12; } // Render the sphere. if ( !IsNudging() ) { pRender->RenderWireframeSphere( vCenter, flRadius, size, size, 0, 255, 0 ); } else { pRender->RenderWireframeSphere( vCenter, flRadius, size, size, 255, 255, 0 ); } // Render the displacement axis (as an arrow). int nPaintAxis; Vector vPaintAxis; GetPaintAxis( nPaintAxis, vPaintAxis ); if( nPaintAxis == DISPPAINT_AXIS_SUBDIV ) { pDisp->GetSubdivNormal( iHit, vPaintAxis ); } float flBloat = flRadius * 0.15f; pRender->RenderArrow( vCenter, vCenter + ( vPaintAxis * ( flRadius + flBloat ) ), 255, 255, 0 ); // Render cube at center point. Vector vBoxMin, vBoxMax; for ( int iAxis = 0; iAxis < 3; iAxis++ ) { vBoxMin[iAxis] = vCenter[iAxis] - ( flBloat * 0.25f ); vBoxMax[iAxis] = vCenter[iAxis] + ( flBloat * 0.25f ); } pRender->RenderBox( vBoxMin, vBoxMax, 255, 255, 0, SELECT_NONE ); } //----------------------------------------------------------------------------- // Purpose: // Input : pRender - // bNudge - //----------------------------------------------------------------------------- void CToolDisplace::RenderHitBox( CRender3D *pRender ) { CMapDisp *pDisp = GetEditDisp(); if (pDisp == NULL) return; // // get selection // int index = pDisp->GetTexelHitIndex(); if( index == -1 ) return; // // get the displacement map width and height // int width = pDisp->GetWidth(); int height = pDisp->GetHeight(); Vector seg[2]; Vector points[2]; pDisp->GetVert( 0, points[0] ); pDisp->GetVert( ( width - 1 ), points[1] ); VectorSubtract( points[1], points[0], seg[0] ); pDisp->GetVert( ( ( width - 1 ) * height ), points[1] ); VectorSubtract( points[1], points[0], seg[1] ); VectorAdd( seg[0], seg[1], seg[0] ); VectorScale( seg[0], 0.5f, seg[0] ); // // determine a good size to make the "box" surrounding the selected point // float length = VectorLength( seg[0] ); length *= 0.025f; // // render the box // pDisp->GetVert( index, points[0] ); Vector minb, maxb; minb[0] = points[0][0] - length; minb[1] = points[0][1] - length; minb[2] = points[0][2] - length; maxb[0] = points[0][0] + length; maxb[1] = points[0][1] + length; maxb[2] = points[0][2] + length; if( !IsNudging() ) { pRender->RenderWireframeBox( minb, maxb, 0, 255, 0 ); } else { pRender->RenderWireframeBox( minb, maxb, 255, 255, 0 ); } // // render the normal // // get hit box origin Vector hbOrigin; pDisp->GetVert( index, hbOrigin ); // get 4x length float length4 = length * 4.0f; int paintAxis; Vector vecPaint; GetPaintAxis( paintAxis, vecPaint ); if( paintAxis == DISPPAINT_AXIS_SUBDIV ) { pDisp->GetSubdivNormal( index, vecPaint ); } // // render the normal -- just a yellow line at this point // pRender->RenderArrow( hbOrigin, hbOrigin + ( vecPaint * length4 ), 255, 255, 0 ); #if 0 CMeshBuilder meshBuilder; IMesh *pMesh = MaterialSystemInterface()->GetDynamicMesh(); meshBuilder.Begin( pMesh, MATERIAL_LINES, 1 ); meshBuilder.Position3f( hbOrigin.x, hbOrigin.y, hbOrigin.z ); meshBuilder.Color3ub( 255, 255, 0 ); meshBuilder.AdvanceVertex(); meshBuilder.Position3f( hbOrigin.x + ( normal.x * length4 ), hbOrigin.y + ( normal.y * length4 ), hbOrigin.z + ( normal.z * length4 ) ); meshBuilder.Color3ub( 255, 255, 0 ); meshBuilder.AdvanceVertex(); meshBuilder.End(); pMesh->Draw(); #endif } //----------------------------------------------------------------------------- // Purpose: // Input : *pRender - //----------------------------------------------------------------------------- void CToolDisplace::RenderTool3D(CRender3D *pRender) { unsigned int uiTool = GetTool(); if ( uiTool == DISPTOOL_PAINT ) { if ( IsSpatialPainting() ) { RenderPaintSphere( pRender ); } else { RenderHitBox( pRender ); } } if ( uiTool == DISPTOOL_PAINT_SCULPT ) { m_SculptTool->RenderTool3D( pRender ); } }