summaryrefslogtreecommitdiff
path: root/hammer/faceedit_materialpage.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/faceedit_materialpage.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'hammer/faceedit_materialpage.cpp')
-rw-r--r--hammer/faceedit_materialpage.cpp1785
1 files changed, 1785 insertions, 0 deletions
diff --git a/hammer/faceedit_materialpage.cpp b/hammer/faceedit_materialpage.cpp
new file mode 100644
index 0000000..fb72068
--- /dev/null
+++ b/hammer/faceedit_materialpage.cpp
@@ -0,0 +1,1785 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include <stdafx.h>
+#include "hammer.h"
+#include "IEditorTexture.h"
+#include "FaceEditSheet.h"
+#include "MapFace.h"
+#include "MapWorld.h"
+#include "MainFrm.h"
+#include "History.h"
+#include "GlobalFunctions.h"
+#include "mathlib/vmatrix.h"
+#include "MapSolid.h"
+#include "TextureBrowser.h"
+#include "TextureSystem.h"
+#include "MapView3D.h"
+#include "ReplaceTexDlg.h"
+#include "WADTypes.h"
+#include "FaceEdit_MaterialPage.h"
+#include "Camera.h"
+#include "MapDoc.h"
+#include "MapDisp.h"
+#include "ToolManager.h"
+#include "Selection.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+//=============================================================================
+
+IMPLEMENT_DYNAMIC( CFaceEditMaterialPage, CPropertyPage )
+
+BEGIN_MESSAGE_MAP( CFaceEditMaterialPage, CPropertyPage )
+ //{{AFX_MSG_MAP( CFaceEditMaterialPage )
+ ON_BN_CLICKED( ID_FACEEDIT_APPLY, OnButtonApply )
+ ON_COMMAND_EX( IDC_ALIGN_WORLD, OnAlign )
+ ON_COMMAND_EX( IDC_ALIGN_FACE, OnAlign )
+ ON_BN_CLICKED( IDC_HIDEMASK, OnHideMask )
+ ON_COMMAND_EX( IDC_JUSTIFY_TOP, OnJustify )
+ ON_COMMAND_EX( IDC_JUSTIFY_BOTTOM, OnJustify )
+ ON_COMMAND_EX( IDC_JUSTIFY_LEFT, OnJustify )
+ ON_COMMAND_EX( IDC_JUSTIFY_CENTER, OnJustify )
+ ON_COMMAND_EX( IDC_JUSTIFY_RIGHT, OnJustify )
+ ON_COMMAND_EX( IDC_JUSTIFY_FITTOFACE, OnJustify )
+ ON_BN_CLICKED( IDC_MODE, OnMode )
+ ON_WM_VSCROLL()
+ ON_NOTIFY( UDN_DELTAPOS, IDC_SPINSCALEX, OnDeltaPosFloatSpin )
+ ON_NOTIFY( UDN_DELTAPOS, IDC_SPINSCALEY, OnDeltaPosFloatSpin )
+ ON_WM_SIZE()
+ ON_CBN_SELCHANGE( IDC_TEXTURES, OnSelChangeTexture )
+ ON_BN_CLICKED( IDC_Q2_LIGHT, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_SLICK, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_SKY, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_WARP, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_TRANS33, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_TRANS66, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_FLOWING, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_NODRAW, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_SOLID, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_WINDOW, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_AUX, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_LAVA, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_SLIME, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_WATER, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_MIST, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_CURRENT90, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_CURRENT180, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_CURRENT270, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_CURRENTUP, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_CURRENTDN, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_ORIGIN, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_MONSTER, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_CORPSE, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_DETAIL, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_TRANSLUCENT, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_LADDER, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_PLAYERCLIP, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_MONSTERCLIP, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_CURRENT0, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_HINT, OnCheckUnCheck )
+ ON_BN_CLICKED( IDC_Q2_SPLITTER, OnCheckUnCheck )
+ ON_COMMAND( IDC_TREAT_AS_ONE, OnTreatAsOne )
+ ON_BN_CLICKED( IDC_REPLACE, OnReplace )
+ ON_COMMAND_EX_RANGE( CFaceEditSheet::id_SwitchModeStart, CFaceEditSheet::id_SwitchModeEnd, OnSwitchMode )
+ ON_CBN_SELCHANGE( IDC_TEXTUREGROUPS, OnChangeTextureGroup )
+ ON_BN_CLICKED( IDC_BROWSE, OnBrowse )
+ ON_BN_CLICKED( ID_BUTTON_SMOOTHING_GROUPS, OnButtonSmoothingGroups )
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//=============================================================================
+
+#define CONTENTS_AREAPORTAL 0x8000
+#define CONTENTS_PLAYERCLIP 0x10000
+#define CONTENTS_MONSTERCLIP 0x20000
+
+// I don't think we need these currents. We'll stick to triggers for this
+#define CONTENTS_CURRENT_0 0x40000
+#define CONTENTS_CURRENT_90 0x80000
+#define CONTENTS_CURRENT_180 0x100000
+#define CONTENTS_CURRENT_270 0x200000
+#define CONTENTS_CURRENT_UP 0x400000
+#define CONTENTS_CURRENT_DOWN 0x800000
+#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity
+#define CONTENTS_MONSTER 0x2000000 // should never be on a brush, only in game
+#define CONTENTS_DEBRIS 0x4000000
+#define CONTENTS_DETAIL 0x8000000 // brushes to be added after vis leafs
+#define CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans
+#define CONTENTS_LADDER 0x20000000
+
+//=============================================================================
+
+const int NOT_INIT = -99999;
+
+unsigned int CFaceEditMaterialPage::m_FaceContents = 0;
+unsigned int CFaceEditMaterialPage::m_FaceSurface = 0;
+
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// This table defines the mapping of checkbox controls to flags which are set
+// in certain face attributes values.
+//-----------------------------------------------------------------------------
+CFaceEditMaterialPage::FaceAttributeInfo_t FaceAttributes[] =
+{
+ //
+ // Contents.
+ //
+ { IDC_CONTENTS_AREAPORTAL, &CFaceEditMaterialPage::m_FaceContents, CONTENTS_AREAPORTAL },
+ { IDC_CONTENTS_PLAYERCLIP, &CFaceEditMaterialPage::m_FaceContents, CONTENTS_PLAYERCLIP },
+ { IDC_CONTENTS_MONSTERCLIP, &CFaceEditMaterialPage::m_FaceContents, CONTENTS_MONSTERCLIP },
+ { IDC_CONTENTS_ORIGIN, &CFaceEditMaterialPage::m_FaceContents, CONTENTS_ORIGIN },
+ { IDC_CONTENTS_DETAIL, &CFaceEditMaterialPage::m_FaceContents, CONTENTS_DETAIL },
+ { IDC_CONTENTS_TRANSLUCENT, &CFaceEditMaterialPage::m_FaceContents, CONTENTS_TRANSLUCENT },
+ { IDC_CONTENTS_LADDER, &CFaceEditMaterialPage::m_FaceContents, CONTENTS_LADDER },
+
+ //
+ // Surface attributes.
+ //
+ { IDC_SURF_NODRAW, &CFaceEditMaterialPage::m_FaceSurface, SURF_NODRAW },
+ { IDC_SURF_HINT, &CFaceEditMaterialPage::m_FaceSurface, SURF_HINT },
+ { IDC_SURF_SKIP, &CFaceEditMaterialPage::m_FaceSurface, SURF_SKIP }
+};
+
+
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CFaceEditMaterialPage::CFaceEditMaterialPage() : CPropertyPage( IDD )
+{
+ m_bHideMask = FALSE;
+ m_bInitialized = FALSE;
+ m_bIgnoreResize = FALSE;
+ m_bTreatAsOneFace = FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CFaceEditMaterialPage::~CFaceEditMaterialPage()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+BOOL CFaceEditMaterialPage::PreTranslateMessage( MSG *pMsg )
+{
+ HACCEL hAccel = GetMainWnd()->GetAccelTable();
+ if( !(hAccel && ::TranslateAccelerator( GetMainWnd()->m_hWnd, hAccel, pMsg ) ) )
+ {
+ return CPropertyPage::PreTranslateMessage( pMsg );
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::Init( void )
+{
+ //
+ // Connect dialog control objects to their control IDs.
+ //
+ m_shiftX.SubclassDlgItem( IDC_SHIFTX, this );
+ m_shiftY.SubclassDlgItem( IDC_SHIFTY, this );
+ m_scaleX.SubclassDlgItem( IDC_SCALEX, this );
+ m_scaleY.SubclassDlgItem( IDC_SCALEY, this );
+ m_rotate.SubclassDlgItem( IDC_ROTATE, this );
+ m_cHideMask.SubclassDlgItem( IDC_HIDEMASK, this );
+ m_cExpand.SubclassDlgItem( IDC_EXPAND, this );
+ m_cLightmapScale.SubclassDlgItem( IDC_LIGHTMAP_SCALE, this );
+
+ //
+ // Set spin ranges.
+ //
+ CWnd *pWnd = GetDlgItem(IDC_SPINSHIFTX);
+ ::PostMessage(pWnd->m_hWnd, UDM_SETRANGE, 0, MAKELONG(MAX_TEXTUREWIDTH, -MAX_TEXTUREWIDTH));
+
+ pWnd = GetDlgItem(IDC_SPINSHIFTY);
+ ::PostMessage(pWnd->m_hWnd, UDM_SETRANGE, 0, MAKELONG(MAX_TEXTUREHEIGHT, -MAX_TEXTUREHEIGHT));
+
+ pWnd = GetDlgItem(IDC_SPINROTATE);
+ ::PostMessage(pWnd->m_hWnd, UDM_SETRANGE, 0, MAKELONG(359, -359));
+
+ pWnd = GetDlgItem(IDC_SPINSCALEX);
+ ::PostMessage(pWnd->m_hWnd, UDM_SETRANGE, 0, MAKELONG(UD_MAXVAL, UD_MINVAL));
+
+ pWnd = GetDlgItem(IDC_SPINSCALEY);
+ ::PostMessage(pWnd->m_hWnd, UDM_SETRANGE, 0, MAKELONG(UD_MAXVAL, UD_MINVAL));
+
+ pWnd = GetDlgItem(IDC_SPIN_LIGHTMAP_SCALE);
+ ::PostMessage(pWnd->m_hWnd, UDM_SETRANGE, 0, MAKELONG(UD_MAXVAL, 1));
+
+ // set the initial switch mode
+ OnSwitchMode( CFaceEditSheet::ModeLiftSelect );
+
+ //
+ // set up controls
+ //
+ m_TextureGroupList.SubclassDlgItem( IDC_TEXTUREGROUPS, this );
+ m_TextureList.SubclassDlgItem( IDC_TEXTURES, this );
+ m_TexturePic.SubclassDlgItem( IDC_TEXTUREPIC, this );
+
+ m_pCurTex = NULL;
+
+ //
+ // initially update the texture controls
+ //
+ NotifyGraphicsChanged();
+ UpdateTexture();
+
+ // initialized!
+ m_bInitialized = TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// NOTE: clean this up and make a global function!!!
+// Purpose:
+// Input : fValue -
+// *pSpin -
+// bMantissa -
+// Output : static void
+//-----------------------------------------------------------------------------
+void FloatToSpin(float fValue, CSpinButtonCtrl *pSpin, BOOL bMantissa)
+{
+ char szNew[128];
+
+ CWnd *pEdit = pSpin->GetBuddy();
+
+ if (fValue == NOT_INIT)
+ {
+ szNew[0] = 0;
+ }
+ else
+ {
+ if(bMantissa)
+ sprintf(szNew, "%.2f", fValue);
+ else
+ sprintf(szNew, "%.0f", fValue);
+ }
+
+ pSpin->SetPos(atoi(szNew));
+
+ char szCurrent[128];
+ pEdit->GetWindowText(szCurrent, 128);
+ if (strcmp(szNew, szCurrent))
+ {
+ pEdit->SetWindowText(szNew);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : nValue -
+// pSpin -
+// Output : static void
+//-----------------------------------------------------------------------------
+void IntegerToSpin(int nValue, CSpinButtonCtrl *pSpin)
+{
+ char szNew[128];
+
+ CWnd *pEdit = pSpin->GetBuddy();
+
+ if (nValue == NOT_INIT)
+ {
+ szNew[0] = 0;
+ }
+ else
+ {
+ sprintf(szNew, "%d", abs(nValue));
+ }
+
+ pSpin->SetPos(atoi(szNew));
+
+ char szCurrent[128];
+ pEdit->GetWindowText(szCurrent, 128);
+ if (strcmp(szNew, szCurrent) != 0)
+ {
+ pEdit->SetWindowText(szNew);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : fValue -
+// *pWnd -
+// Output : static void
+//-----------------------------------------------------------------------------
+void FloatToWnd(float fValue, CWnd *pWnd)
+{
+ char szCurrent[128];
+ char szNew[128];
+
+ if(fValue == NOT_INIT)
+ {
+ szNew[0] = 0;
+ }
+ else
+ {
+ sprintf(szNew, "%g", fValue);
+ }
+
+ pWnd->GetWindowText(szCurrent, 128);
+ if(strcmp(szNew, szCurrent))
+ pWnd->SetWindowText(szNew);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fetches a string value from a window and places it in a float. The
+// float is only modified if there is text in the window.
+// Input : pWnd - Window from which to get text.
+// fValue - Float in which to place value.
+//-----------------------------------------------------------------------------
+void TransferToFloat( CWnd *pWnd, float &fValue )
+{
+ CString str;
+ pWnd->GetWindowText( str );
+
+ if( !str.IsEmpty() )
+ {
+ fValue = ( float )atof( str );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fetches a string value from a window and places it in an integer. The
+// integer is only modified if there is text in the window.
+// Input : pWnd - Window from which to get text.
+// nValue - Float in which to place value.
+//-----------------------------------------------------------------------------
+void TransferToInteger( CWnd *pWnd, int &nValue )
+{
+ CString str;
+ pWnd->GetWindowText( str );
+
+ if( !str.IsEmpty() )
+ {
+ nValue = abs( atoi( str ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::ClickFace( CMapSolid *pSolid, int faceIndex, int cmd, int clickMode )
+{
+ // get the face
+ CMapFace *pFace = pSolid->GetFace( faceIndex );
+ bool bIsEditable = pSolid->IsEditable();
+
+ //
+ // are updates enabled?
+ //
+ CFaceEditSheet *pSheet = ( CFaceEditSheet* )GetParent();
+ bool bEnableUpdate = pSheet->HasUpdateEnabled();
+
+
+ SetReadOnly( !bIsEditable );
+
+ //
+ // find the behavior of the page based on the "click mode"
+ //
+ switch( clickMode )
+ {
+ case CFaceEditSheet::ModeAlignToView:
+ {
+ if ( bIsEditable )
+ {
+ AlignToView( pFace );
+ }
+ break;
+ }
+
+ case CFaceEditSheet::ModeLift:
+ {
+ if( bEnableUpdate )
+ {
+ UpdateDialogData( pFace );
+ }
+ break;
+ }
+
+ case CFaceEditSheet::ModeLiftSelect:
+ {
+ if ( bEnableUpdate )
+ {
+ UpdateDialogData();
+ }
+ break;
+ }
+
+ case CFaceEditSheet::ModeApplyLightmapScale:
+ {
+ // Apply the lightmap scale only. Leave everything else alone.
+ if ( bIsEditable )
+ {
+ Apply(pFace, FACE_APPLY_LIGHTMAP_SCALE);
+ }
+ break;
+ }
+
+ case CFaceEditSheet::ModeApply:
+ case CFaceEditSheet::ModeApplyAll:
+ {
+ if ( bIsEditable )
+ {
+ int flags = 0;
+
+ if (cmd & CFaceEditSheet::cfEdgeAlign)
+ {
+ // Adust the mapping to align with a reference face.
+ flags |= FACE_APPLY_ALIGN_EDGE;
+ }
+
+ if (clickMode == CFaceEditSheet::ModeApplyAll)
+ {
+ // Apply the material, mapping, lightmap scale, etc.
+ flags |= FACE_APPLY_ALL;
+ }
+ else
+ {
+ // Apply the material only. Leave everything else alone.
+ flags |= FACE_APPLY_MATERIAL;
+ }
+
+ Apply(pFace, flags);
+ }
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Maps the texture onto the face using the 3D view's up and right vectors.
+// This can be useful for mapping curvy things like hills because if you don't
+// move the 3D view, the texture will line up on any polies you map this way.
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::AlignToView( CMapFace *pFace )
+{
+ CView *pActiveView;
+ CMapView3D *pView3D;
+ CFrameWnd *pFrame;
+ Vector vView;
+
+ if((pFrame = GetMainWnd()->GetActiveFrame()) != NULL)
+ {
+ if((pActiveView = pFrame->GetActiveView()) != NULL)
+ {
+ if(pActiveView->IsKindOf(RUNTIME_CLASS(CMapView3D)))
+ {
+ pView3D = dynamic_cast<CMapView3D*>(pActiveView);
+ if(pView3D)
+ {
+ const CCamera *pCamera = pView3D->GetCamera();
+ if(pCamera)
+ {
+ Vector right, up;
+ pCamera->GetViewRight(right);
+ pCamera->GetViewUp(up);
+ pCamera->GetViewPoint(vView);
+
+ pFace->texture.UAxis.AsVector3D() = right;
+ pFace->texture.VAxis.AsVector3D() = up;
+ pFace->texture.UAxis[3] = DotProduct( right, vView);
+ pFace->texture.VAxis[3] = DotProduct( up, vView);
+ pFace->NormalizeTextureShifts();
+
+ pFace->texture.rotate = 0.0f;
+ pFace->texture.scale[0] = g_pGameConfig->GetDefaultTextureScale();
+ pFace->texture.scale[1] = g_pGameConfig->GetDefaultTextureScale();
+
+ pFace->CalcTextureCoords();
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Copies the texture coordinate system from pFrom into pTo. Then it rotates
+// the texture around the edge until it's as close to pTo's normal as possible.
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::CopyTCoordSystem( const CMapFace *pFrom, CMapFace *pTo )
+{
+ Vector axis[2], vEdge, vEdgePt, vOrigin;
+ Vector vFromPt, vNextFromPt;
+ Vector vToPt, vPrevToPt;
+ Vector vTestTextureNormal, vTextureNormal;
+ VMatrix mEdgeRotation, mOriginRotation, mTranslation;
+ float fAngle, fDot;
+ bool bRotate;
+ float fShift[2];
+ Vector vProjTexNormal;
+ Vector vProjPolyNormal;
+
+ // The edge vector lies on both planes.
+ vEdge = pFrom->plane.normal.Cross(pTo->plane.normal);
+ VectorNormalize( vEdge );
+
+ // To find a point on the plane, we make a plane from the edge vector and find the intersection
+ // between the three planes (without the third plane, there are an infinite number of solutions).
+ if( PlaneIntersection( VPlane(pFrom->plane.normal, pFrom->plane.dist),
+ VPlane(pTo->plane.normal, pTo->plane.dist),
+ VPlane(vEdge, 0.0f), vEdgePt ) )
+ {
+ bRotate = true;
+ }
+ else
+ {
+ // Ok, in this case, the planes are parallel so we don't need to rotate around the edge anyway!
+ bRotate = false;
+ }
+
+ // Copy the texture coordinate system.
+ axis[0] = pFrom->texture.UAxis.AsVector3D();
+ axis[1] = pFrom->texture.VAxis.AsVector3D();
+ fShift[0] = pFrom->texture.UAxis[3];
+ fShift[1] = pFrom->texture.VAxis[3];
+ vOrigin = axis[0]*fShift[0]*pFrom->texture.scale[0] + axis[1]*fShift[1]*pFrom->texture.scale[1];
+
+ vTextureNormal = axis[0].Cross(axis[1]);
+ VectorNormalize(vTextureNormal);
+ if(bRotate)
+ {
+ // Project texture normal and poly normal into the plane of rotation
+ // to get the angle between them.
+ vProjTexNormal = vTextureNormal - vEdge * vEdge.Dot(vTextureNormal);
+ vProjPolyNormal = pTo->plane.normal - vEdge * vEdge.Dot(pTo->plane.normal);
+
+ VectorNormalize( vProjTexNormal );
+ VectorNormalize( vProjPolyNormal );
+
+ fDot = vProjTexNormal.Dot(vProjPolyNormal);
+ fAngle = (float)(acos(fDot) * (180.0f / M_PI));
+ if(fDot < 0.0f)
+ fAngle = 180.0f - fAngle;
+
+ // Ok, rotate them for the final values.
+ mEdgeRotation = SetupMatrixAxisRot(vEdge, fAngle);
+ axis[0] = mEdgeRotation.ApplyRotation(axis[0]);
+ axis[1] = mEdgeRotation.ApplyRotation(axis[1]);
+
+ // Origin needs translation and rotation to rotate around the edge.
+ mTranslation = SetupMatrixTranslation(vEdgePt);
+ mOriginRotation = ~mTranslation * mEdgeRotation * mTranslation;
+ vOrigin = mOriginRotation * vOrigin;
+ }
+
+ pTo->texture.UAxis.AsVector3D() = axis[0];
+ pTo->texture.VAxis.AsVector3D() = axis[1];
+
+ pTo->texture.UAxis[3] = axis[0].Dot(vOrigin) / pFrom->texture.scale[0];
+ pTo->texture.VAxis[3] = axis[1].Dot(vOrigin) / pFrom->texture.scale[1];
+ pTo->NormalizeTextureShifts();
+
+ pTo->texture.scale[0] = pFrom->texture.scale[0];
+ pTo->texture.scale[1] = pFrom->texture.scale[1];
+
+ // rotate is only for UI purposes, it doesn't actually do anything.
+ pTo->texture.rotate = 0.0f;
+
+ pTo->CalcTextureCoords();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies dialog data to the list of selected faces.
+// Input : *pOnlyFace -
+// bAll -
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::Apply( CMapFace *pOnlyFace, int flags )
+{
+ int i;
+ CString str;
+ float fshiftX = NOT_INIT;
+ float fshiftY = NOT_INIT;
+ float fscaleX = NOT_INIT;
+ float fscaleY = NOT_INIT;
+ float frotate = NOT_INIT;
+ int material = NOT_INIT;
+ int nLightmapScale = NOT_INIT;
+ IEditorTexture *pTex = m_TexturePic.GetTexture();
+ CMapDoc *pMapDoc = CMapDoc::GetActiveMapDoc();
+
+ //
+ // Get numeric data.
+ //
+ if (flags & FACE_APPLY_MAPPING)
+ {
+ TransferToFloat( &m_shiftX, fshiftX );
+ TransferToFloat( &m_shiftY, fshiftY );
+ TransferToFloat( &m_scaleX, fscaleX );
+ TransferToFloat( &m_scaleY, fscaleY );
+ TransferToFloat( &m_rotate, frotate );
+ }
+
+ if (flags & FACE_APPLY_LIGHTMAP_SCALE)
+ {
+ TransferToInteger( &m_cLightmapScale, nLightmapScale );
+ }
+
+ if ( !pOnlyFace )
+ {
+ GetHistory()->MarkUndoPosition( NULL, "Apply Face Attributes" );
+
+ // make sure we apply everything in this case.
+ flags |= FACE_APPLY_ALL;
+
+ // Keep the solids that we are about to change.
+ // In the pOnlyFace case we do the Keep before calling ClickFace. Why?
+ CUtlVector<CMapSolid *> kept;
+ CFaceEditSheet *pSheet = ( CFaceEditSheet* )GetParent();
+ for( i = 0; i < pSheet->GetFaceListCount(); i++ )
+ {
+ CMapSolid *pSolid = pSheet->GetFaceListDataSolid( i );
+ if ( kept.Find( pSolid ) == -1 )
+ {
+ GetHistory()->Keep( pSolid );
+ kept.AddToTail( pSolid );
+ }
+ }
+ }
+
+ //
+ // Run thru stored faces & apply.
+ //
+ CFaceEditSheet *pSheet = ( CFaceEditSheet* )GetParent();
+ int faceCount = pSheet->GetFaceListCount();
+ for( i = 0; i < faceCount || pOnlyFace; i++ )
+ {
+ CMapFace *pFace;
+ if( pOnlyFace )
+ {
+ pFace = pOnlyFace;
+ }
+ else
+ {
+ pFace = pSheet->GetFaceListDataFace( i );
+ }
+
+ //
+ // Get values for texture shift, scale, rotate, and material.
+ //
+ if ((flags & FACE_APPLY_MAPPING) && (!(flags & FACE_APPLY_ALIGN_EDGE)))
+ {
+ if ( fshiftX != NOT_INIT )
+ {
+ pFace->texture.UAxis[3] = fshiftX;
+ }
+
+ if ( fshiftY != NOT_INIT )
+ {
+ pFace->texture.VAxis[3] = fshiftY;
+ }
+
+ if ( fscaleX != NOT_INIT )
+ {
+ pFace->texture.scale[0] = fscaleX;
+ }
+
+ if ( fscaleY != NOT_INIT )
+ {
+ pFace->texture.scale[1] = fscaleY;
+ }
+
+ if ( frotate != NOT_INIT )
+ {
+ pFace->RotateTextureAxes( frotate - pFace->texture.rotate );
+ pFace->texture.rotate = frotate;
+ }
+ }
+
+ if (flags & FACE_APPLY_CONTENTS_DATA)
+ {
+ if ( material != NOT_INIT )
+ {
+ pFace->texture.material = material;
+ }
+ }
+
+ if (flags & FACE_APPLY_LIGHTMAP_SCALE)
+ {
+ if (nLightmapScale != NOT_INIT)
+ {
+ pFace->texture.nLightmapScale = max( nLightmapScale, 1 );
+ }
+ }
+
+ //
+ // Update the texture and recalculate texture coordinates.
+ //
+ if ((flags & FACE_APPLY_MATERIAL) && (pTex != NULL))
+ {
+ char szCurrentTexName[MAX_PATH];
+ char szNewTexName[MAX_PATH];
+
+ pFace->GetTextureName( szCurrentTexName );
+ pTex->GetShortName( szNewTexName );
+
+ if( stricmp( szCurrentTexName, szNewTexName ) != 0 )
+ {
+ pFace->SetTexture( szNewTexName );
+
+ CMapClass *pParent = dynamic_cast< CMapClass * >( pFace->GetParent() );
+ if ( pParent )
+ {
+ pMapDoc->RemoveFromAutoVisGroups( pParent );
+ pMapDoc->AddToAutoVisGroup( pParent );
+ }
+ }
+ }
+
+ //
+ // Copy texture coordinate system.
+ //
+ if ((flags & FACE_APPLY_ALIGN_EDGE) && (faceCount >= 1))
+ {
+ CopyTCoordSystem( pSheet->GetFaceListDataFace( faceCount - 1 ), pFace );
+ }
+
+ //
+ // Recalculate texture coordinates.
+ //
+ pFace->CalcTextureCoords();
+
+ //
+ // Update the face flags.
+ //
+ if (flags & FACE_APPLY_CONTENTS_DATA)
+ {
+ //
+ // Copy the bits from this face into our variables.
+ //
+ m_FaceContents = pFace->texture.q2contents;
+ m_FaceSurface = pFace->texture.q2surface;
+
+ //
+ // Update our variables based on the state of the checkboxes.
+ //
+ for( int nItem = 0; nItem < sizeof( FaceAttributes ) / sizeof( FaceAttributes[0] ); nItem++ )
+ {
+ CButton *pButton = ( CButton* )GetDlgItem( FaceAttributes[nItem].uControlID );
+ if( pButton != NULL )
+ {
+ int nSet = pButton->GetCheck();
+
+ if (nSet == 0)
+ {
+ *FaceAttributes[nItem].puAttribute &= ~FaceAttributes[nItem].uFlag;
+ }
+ else if (nSet == 1)
+ {
+ *FaceAttributes[nItem].puAttribute |= FaceAttributes[nItem].uFlag;
+ }
+ }
+ }
+
+ //
+ // Copy our variables back into this face.
+ //
+ pFace->texture.q2contents = m_FaceContents;
+ pFace->texture.q2surface = m_FaceSurface;
+ }
+
+ if( pOnlyFace )
+ {
+ break;
+ }
+ }
+
+ pMapDoc->SetModifiedFlag();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOnlyFace -
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::UpdateDialogData( CMapFace *pOnlyFace )
+{
+ BOOL bFirst;
+ int nFaceAlignCount;
+ int nWorldAlignCount;
+ float fshiftX = NOT_INIT;
+ float fshiftY = NOT_INIT;
+ float fscaleX = NOT_INIT;
+ float fscaleY = NOT_INIT;
+ float frotate = NOT_INIT;
+ //float fsmooth = NOT_INIT;
+ int material = NOT_INIT;
+ int nLightmapScale = NOT_INIT;
+ CString strTexture;
+
+ bFirst = TRUE;
+ nFaceAlignCount = 0;
+ nWorldAlignCount = 0;
+
+ CFaceEditSheet *pSheet = ( CFaceEditSheet* )GetParent();
+ int faceCount = pSheet->GetFaceListCount();
+
+ for( int i = 0; i < faceCount || pOnlyFace; i++ )
+ {
+ CMapFace *pFace;
+
+ if( pOnlyFace )
+ {
+ pFace = pOnlyFace;
+ }
+ else
+ {
+ pFace = pSheet->GetFaceListDataFace( i );
+ }
+
+ TEXTURE &t = pFace->texture;
+
+ //
+ // Gather statistics about the texture alignment of all the selected faces.
+ // This is used later to set the state of the alignment checkboxes.
+ //
+ int nAlignment = pFace->GetTextureAlignment();
+ if (nAlignment & TEXTURE_ALIGN_FACE)
+ {
+ nFaceAlignCount++;
+ }
+
+ if (nAlignment & TEXTURE_ALIGN_WORLD)
+ {
+ nWorldAlignCount++;
+ }
+
+ //
+ // First update - copy first face's stuff into edit fields.
+ //
+ if (bFirst)
+ {
+ fshiftX = t.UAxis[3];
+ fshiftY = t.VAxis[3];
+ fscaleX = t.scale[0];
+ fscaleY = t.scale[1];
+ frotate = t.rotate;
+ material = t.material;
+ strTexture = t.texture;
+ nLightmapScale = t.nLightmapScale;
+
+ //
+ // Get the face's orientation. This is used by Apply to make intelligent decisions.
+ //
+ m_eOrientation = pFace->GetOrientation();
+ Assert(m_eOrientation != FACE_ORIENTATION_INVALID);
+
+ //
+ // Set the appropriate checkbox state for the face attributes.
+ //
+ m_FaceContents = t.q2contents;
+ m_FaceSurface = t.q2surface;
+
+ for (int nItem = 0; nItem < sizeof(FaceAttributes) / sizeof(FaceAttributes[0]); nItem++)
+ {
+ int nSet = ((*FaceAttributes[nItem].puAttribute & FaceAttributes[nItem].uFlag) != 0);
+ CButton *pButton = (CButton *)GetDlgItem(FaceAttributes[nItem].uControlID);
+ if (pButton != NULL)
+ {
+ pButton->SetCheck(nSet);
+ }
+ }
+
+ bFirst = FALSE;
+
+ if (pOnlyFace) // use one face - now break
+ {
+ break;
+ }
+ }
+ else
+ {
+ // update fields with face's data
+ if (t.UAxis[3] != fshiftX)
+ {
+ fshiftX = NOT_INIT;
+ }
+
+ if (t.VAxis[3] != fshiftY)
+ {
+ fshiftY = NOT_INIT;
+ }
+
+ if (t.scale[0] != fscaleX)
+ {
+ fscaleX = NOT_INIT;
+ }
+
+ if (t.scale[1] != fscaleY)
+ {
+ fscaleY = NOT_INIT;
+ }
+
+ if (t.rotate != frotate)
+ {
+ frotate = NOT_INIT;
+ }
+
+ if (t.material != material)
+ {
+ material = NOT_INIT;
+ }
+
+ if (t.nLightmapScale != nLightmapScale)
+ {
+ nLightmapScale = NOT_INIT;
+ }
+
+ if (!strTexture.IsEmpty() && strTexture != t.texture)
+ {
+ strTexture = "";
+ }
+
+ //
+ // Update the checkbox state for the face attributes. If any of this face's
+ // attributes are different from the current checkbox state, set the checkbox
+ // to the undefined state.
+ //
+ m_FaceContents = t.q2contents;
+ m_FaceSurface = t.q2surface;
+
+ for (int nItem = 0; nItem < sizeof(FaceAttributes) / sizeof(FaceAttributes[0]); nItem++)
+ {
+ int nSet = ((*FaceAttributes[nItem].puAttribute & FaceAttributes[nItem].uFlag) != 0);
+ CButton *pButton = (CButton *)GetDlgItem(FaceAttributes[nItem].uControlID);
+ if (pButton != NULL)
+ {
+ if (pButton->GetCheck() != nSet)
+ {
+ pButton->SetButtonStyle(BS_AUTO3STATE);
+ pButton->SetCheck(2);
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Set the state of the face alignment checkbox.
+ //
+ CButton *pFaceAlign = (CButton *)GetDlgItem(IDC_ALIGN_FACE);
+
+ if (nFaceAlignCount == 0)
+ {
+ pFaceAlign->SetCheck(0);
+ }
+ else if (nFaceAlignCount == faceCount)
+ {
+ pFaceAlign->SetCheck(1);
+ }
+ else
+ {
+ pFaceAlign->SetCheck(2);
+ }
+
+ //
+ // Set the state of the world alignment checkbox.
+ //
+ CButton *pWorldAlign = (CButton *)GetDlgItem(IDC_ALIGN_WORLD);
+
+ if (nWorldAlignCount == 0)
+ {
+ pWorldAlign->SetCheck(0);
+ }
+ else if (nWorldAlignCount == faceCount)
+ {
+ pWorldAlign->SetCheck(1);
+ }
+ else
+ {
+ pWorldAlign->SetCheck(2);
+ }
+
+ //
+ // Set up fields.
+ //
+ FloatToSpin(fshiftX, (CSpinButtonCtrl*)GetDlgItem(IDC_SPINSHIFTX), FALSE);
+ FloatToSpin(fshiftY, (CSpinButtonCtrl*)GetDlgItem(IDC_SPINSHIFTY), FALSE);
+ IntegerToSpin(nLightmapScale, (CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_LIGHTMAP_SCALE));
+
+ FloatToWnd(fscaleX, &m_scaleX);
+ FloatToWnd(fscaleY, &m_scaleY);
+
+ FloatToSpin(frotate, (CSpinButtonCtrl*)GetDlgItem(IDC_SPINROTATE), TRUE);
+
+ if (!strTexture.IsEmpty())
+ {
+ SelectTexture( strTexture );
+ }
+ else
+ {
+ // make empty
+ m_TextureList.SetCurSel( -1 );
+ }
+
+ //
+ // if no faces selected -- get selection from texture bar
+ //
+ if( faceCount == 0 )
+ {
+ CString strTexName = GetDefaultTextureName();
+ SelectTexture( strTexName );
+ }
+
+ //
+ // Call ctexturebar implementation because OUR implementation sets the
+ // q2 checkboxes, which flashes the screen a bit (cuz we change them
+ // again three lines down.)
+ //
+ UpdateTexture();
+
+ // Update the smoothing group data.
+ if ( GetMaterialPageTool() == MATERIALPAGETOOL_SMOOTHING_GROUP )
+ {
+ m_FaceSmoothDlg.UpdateControls();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : uCmd -
+// Output : Returns TRUE on success, FALSE on failure.
+//-----------------------------------------------------------------------------
+BOOL CFaceEditMaterialPage::OnAlign( UINT uCmd )
+{
+ // Set the material tool current.
+ SetMaterialPageTool( MATERIALPAGETOOL_MATERIAL );
+
+ // mark position in undo stack
+ GetHistory()->MarkUndoPosition(NULL, "Align texture");
+
+ CFaceEditSheet *pSheet = ( CFaceEditSheet* )GetParent();
+ int faceCount = pSheet->GetFaceListCount();
+
+ for( int i = 0; i < faceCount; i++ )
+ {
+ CMapFace *pFace = pSheet->GetFaceListDataFace( i );
+
+ CMapSolid *pSolid = pSheet->GetFaceListDataSolid( i );
+ GetHistory()->Keep( pSolid );
+
+ switch( uCmd )
+ {
+ case IDC_ALIGN_WORLD:
+ {
+ pFace->InitializeTextureAxes( TEXTURE_ALIGN_WORLD, INIT_TEXTURE_AXES | INIT_TEXTURE_FORCE );
+ break;
+ }
+
+ case IDC_ALIGN_FACE:
+ {
+ pFace->InitializeTextureAxes( TEXTURE_ALIGN_FACE, INIT_TEXTURE_AXES | INIT_TEXTURE_FORCE );
+ break;
+ }
+ }
+ }
+
+ CMapDoc::GetActiveMapDoc()->SetModifiedFlag();
+
+ UpdateDialogData();
+
+ return ( TRUE );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnHideMask(void)
+{
+ m_bHideMask = m_cHideMask.GetCheck();
+
+ CMapFace::SetShowSelection( m_bHideMask == FALSE );
+
+ CMapDoc::GetActiveMapDoc()->UpdateAllViews( MAPVIEW_UPDATE_ONLY_3D | MAPVIEW_UPDATE_OBJECTS | MAPVIEW_UPDATE_COLOR );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : Extents -
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::GetAllFaceExtents( Extents_t Extents )
+{
+ BOOL bFirst = TRUE;
+ Extents_t FaceExtents;
+
+ CFaceEditSheet *pSheet = ( CFaceEditSheet* )GetParent();
+ int faceCount = pSheet->GetFaceListCount();
+
+ for( int nFace = 0; nFace < faceCount; nFace++ )
+ {
+ CMapFace *pFace = pSheet->GetFaceListDataFace( nFace );
+ pFace->GetFaceExtents(FaceExtents);
+
+ if ((FaceExtents[EXTENTS_XMIN][0] < Extents[EXTENTS_XMIN][0]) || (bFirst))
+ {
+ Extents[EXTENTS_XMIN] = FaceExtents[EXTENTS_XMIN];
+ }
+
+ if ((FaceExtents[EXTENTS_XMAX][0] > Extents[EXTENTS_XMAX][0]) || (bFirst))
+ {
+ Extents[EXTENTS_XMAX] = FaceExtents[EXTENTS_XMAX];
+ }
+
+ if ((FaceExtents[EXTENTS_YMIN][1] < Extents[EXTENTS_YMIN][1]) || (bFirst))
+ {
+ Extents[EXTENTS_YMIN] = FaceExtents[EXTENTS_YMIN];
+ }
+
+ if ((FaceExtents[EXTENTS_YMAX][1] > Extents[EXTENTS_YMAX][1]) || (bFirst))
+ {
+ Extents[EXTENTS_YMAX] = FaceExtents[EXTENTS_YMAX];
+ }
+
+ if ((FaceExtents[EXTENTS_ZMIN][2] < Extents[EXTENTS_ZMIN][2]) || (bFirst))
+ {
+ Extents[EXTENTS_ZMIN] = FaceExtents[EXTENTS_ZMIN];
+ }
+
+ if ((FaceExtents[EXTENTS_ZMAX][2] > Extents[EXTENTS_ZMAX][2]) || (bFirst))
+ {
+ Extents[EXTENTS_ZMAX] = FaceExtents[EXTENTS_ZMAX];
+ }
+
+ bFirst = FALSE;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : uCmd -
+// Output : Returns TRUE on success, FALSE on failure.
+//-----------------------------------------------------------------------------
+BOOL CFaceEditMaterialPage::OnJustify( UINT uCmd )
+{
+ // Set the material tool current.
+ SetMaterialPageTool( MATERIALPAGETOOL_MATERIAL );
+
+ BOOL bTreatManyAsOneFace;
+ Extents_t Extents;
+
+ // mark undo position
+ GetHistory()->MarkUndoPosition( NULL, "Justify texture" );
+
+ CFaceEditSheet *pSheet = ( CFaceEditSheet* )GetParent();
+ int faceCount = pSheet->GetFaceListCount();
+
+ // If multiple faces are selected, use the m_bTreatManyAsOneFace variable to determine
+ // how to perform the justification.
+ if( faceCount > 1 )
+ {
+ bTreatManyAsOneFace = m_bTreatAsOneFace;
+ if( bTreatManyAsOneFace )
+ {
+ GetAllFaceExtents( Extents );
+ }
+ }
+ // If only one face is selected, treat it singly.
+ else
+ {
+ bTreatManyAsOneFace = FALSE;
+ }
+
+ for( int i = 0; i < faceCount; i++ )
+ {
+ CMapFace *pFace = pSheet->GetFaceListDataFace( i );
+
+ CMapSolid *pSolid = pSheet->GetFaceListDataSolid( i );
+ GetHistory()->Keep( pSolid );
+
+ if( !bTreatManyAsOneFace )
+ {
+ pFace->GetFaceExtents( Extents );
+ }
+
+ switch (uCmd)
+ {
+ case IDC_JUSTIFY_TOP:
+ {
+ pFace->JustifyTextureUsingExtents(TEXTURE_JUSTIFY_TOP, Extents);
+ break;
+ }
+
+ case IDC_JUSTIFY_BOTTOM:
+ {
+ pFace->JustifyTextureUsingExtents(TEXTURE_JUSTIFY_BOTTOM, Extents);
+ break;
+ }
+
+ case IDC_JUSTIFY_LEFT:
+ {
+ pFace->JustifyTextureUsingExtents(TEXTURE_JUSTIFY_LEFT, Extents);
+ break;
+ }
+
+ case IDC_JUSTIFY_RIGHT:
+ {
+ pFace->JustifyTextureUsingExtents(TEXTURE_JUSTIFY_RIGHT, Extents);
+ break;
+ }
+
+ case IDC_JUSTIFY_CENTER:
+ {
+ pFace->JustifyTextureUsingExtents(TEXTURE_JUSTIFY_CENTER, Extents);
+ break;
+ }
+
+ case IDC_JUSTIFY_FITTOFACE:
+ {
+ pFace->JustifyTextureUsingExtents(TEXTURE_JUSTIFY_FIT, Extents);
+ break;
+ }
+ }
+ }
+
+ CMapDoc::GetActiveMapDoc()->SetModifiedFlag();
+
+ UpdateDialogData();
+
+ return(TRUE);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : id -
+// Output : Returns TRUE on success, FALSE on failure.
+//-----------------------------------------------------------------------------
+BOOL CFaceEditMaterialPage::OnSwitchMode( UINT id )
+{
+ CWnd *pButton = GetDlgItem( IDC_MODE );
+
+ CFaceEditSheet *pSheet = ( CFaceEditSheet* )GetParent();
+ pSheet->SetClickMode( id );
+
+ switch( id )
+ {
+ case CFaceEditSheet::ModeLiftSelect: // set
+ pButton->SetWindowText( "Mode: Lift+Select" );
+ break;
+ case CFaceEditSheet::ModeLift:
+ pButton->SetWindowText( "Mode: Lift" );
+ break;
+ case CFaceEditSheet::ModeSelect:
+ pButton->SetWindowText( "Mode: Select" );
+ break;
+ case CFaceEditSheet::ModeApply:
+ pButton->SetWindowText( "Mode: Apply (texture)" );
+ break;
+ case CFaceEditSheet::ModeApplyAll:
+ pButton->SetWindowText( "Mode: Apply (all)" );
+ break;
+ case CFaceEditSheet::ModeAlignToView:
+ pButton->SetWindowText( "Align To View" );
+ break;
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnMode()
+{
+ // Set the material tool current.
+ SetMaterialPageTool( MATERIALPAGETOOL_MATERIAL );
+
+ // switch mode -
+ // LIFT - lift texture from clicked face
+ // APPLY - apply selected texture to clicked face
+ // SELECT - mark each face
+ // LIFT/SELECT - mark clicked faces & lift textures
+
+ CMenu menu;
+ menu.CreatePopupMenu();
+ menu.AppendMenu( MF_STRING, CFaceEditSheet::ModeLiftSelect, "Lift+Select" );
+ menu.AppendMenu( MF_STRING, CFaceEditSheet::ModeLift, "Lift" );
+ menu.AppendMenu( MF_STRING, CFaceEditSheet::ModeSelect, "Select" );
+ menu.AppendMenu( MF_STRING, CFaceEditSheet::ModeApply, "Apply (texture only)" );
+ menu.AppendMenu( MF_STRING, CFaceEditSheet::ModeApplyAll, "Apply (texture + values)" );
+ menu.AppendMenu( MF_STRING, CFaceEditSheet::ModeAlignToView, "Align To View" );
+
+ // track menu
+ CWnd *pButton = GetDlgItem( IDC_MODE );
+ CRect r;
+ pButton->GetWindowRect( r );
+ menu.TrackPopupMenu( TPM_LEFTALIGN | TPM_LEFTBUTTON, r.left, r.bottom, this, NULL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : nSBCode -
+// nPos -
+// *pScrollBar -
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnVScroll( UINT nSBCode, UINT nPos, CScrollBar *pScrollBar )
+{
+ Apply(NULL, FACE_APPLY_MAPPING);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pNMHDR -
+// pResult -
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnDeltaPosFloatSpin( NMHDR *pNMHDR, LRESULT *pResult )
+{
+ NM_UPDOWN *pNMUpDown = ( NM_UPDOWN* )pNMHDR;
+
+ CEdit *pEdit = NULL;
+ switch( pNMUpDown->hdr.idFrom )
+ {
+ case IDC_SPINSCALEY:
+ {
+ pEdit = &m_scaleY;
+ break;
+ }
+
+ case IDC_SPINSCALEX:
+ {
+ pEdit = &m_scaleX;
+ break;
+ }
+ }
+
+ if( pEdit != NULL )
+ {
+ CString str;
+ pEdit->GetWindowText(str);
+ float fTmp = atof(str);
+ fTmp += 0.1f * float( pNMUpDown->iDelta );
+ str.Format( "%.2f", fTmp );
+ pEdit->SetWindowText( str );
+
+ *pResult = 0;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : nType -
+// cx -
+// cy -
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnSize( UINT nType, int cx, int cy )
+{
+ return;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnSelChangeTexture( void )
+{
+ // Set the material tool current.
+ SetMaterialPageTool( MATERIALPAGETOOL_MATERIAL );
+
+ if( !m_bInitialized )
+ {
+ return;
+ }
+
+ UpdateTexture();
+
+ if( m_pCurTex != NULL )
+ {
+ m_TextureList.AddMRU( m_pCurTex );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnCheckUnCheck( void )
+{
+ Apply(NULL, FACE_APPLY_MAPPING);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnTreatAsOne( void )
+{
+ // Set the material tool current.
+ SetMaterialPageTool( MATERIALPAGETOOL_MATERIAL );
+
+ CButton *pCheck = ( CButton* )GetDlgItem( IDC_TREAT_AS_ONE );
+ Assert( pCheck != NULL );
+ m_bTreatAsOneFace = pCheck->GetCheck();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Invokes the texture replace dialog.
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnReplace( void )
+{
+ // Set the material tool current.
+ SetMaterialPageTool( MATERIALPAGETOOL_MATERIAL );
+
+ //
+ // get active map doc
+ //
+ CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
+ if( !pDoc )
+ return;
+
+ // ready the replace dialog
+ CReplaceTexDlg dlg( pDoc->GetSelection()->GetCount() );
+
+ // get the texture to replace -- the default texture?!
+ dlg.m_strFind = GetDefaultTextureName();
+
+ //
+ // open replace dialog -- modal
+ //
+ if( dlg.DoModal() != IDOK )
+ return;
+
+ // mark undo position
+ GetHistory()->MarkUndoPosition( pDoc->GetSelection()->GetList(), "Replace Textures" );
+
+ if( dlg.m_bMarkOnly )
+ {
+ pDoc->SelectObject( NULL, scClear ); // clear selection first
+ }
+
+ dlg.DoReplaceTextures();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the m_pTexture data member based on the current selection.
+// Also updates the window text and the texture picture.
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::UpdateTexture( void )
+{
+ int iSel = m_TextureList.GetCurSel();
+
+ if( iSel == LB_ERR )
+ {
+ m_TexturePic.SetTexture( NULL );
+ m_pCurTex = NULL;
+ return;
+ }
+
+ m_pCurTex = ( IEditorTexture* )m_TextureList.GetItemDataPtr( iSel );
+ m_TexturePic.SetTexture( m_pCurTex );
+
+ if( m_pCurTex )
+ {
+ char szBuf[128];
+ sprintf( szBuf, "%dx%d", m_pCurTex->GetWidth(), m_pCurTex->GetHeight() );
+ GetDlgItem( IDC_TEXTURESIZE )->SetWindowText( szBuf );
+
+ char szTexName[128];
+ m_pCurTex->GetShortName( szTexName );
+ SetDefaultTextureName( szTexName );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Selects a texture by name.
+// Input : pszTextureName - Texture name to select.
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::SelectTexture( LPCSTR pszTextureName )
+{
+ int nIndex = m_TextureList.SelectString( -1, pszTextureName );
+
+ //
+ // If the texture is not in the list, add it to the list.
+ //
+ if( nIndex == LB_ERR )
+ {
+ IEditorTexture *pTex = g_Textures.FindActiveTexture( pszTextureName );
+ if( pTex != NULL )
+ {
+ nIndex = m_TextureList.AddString( pszTextureName );
+ m_TextureList.SetItemDataPtr( nIndex, pTex );
+ m_TextureList.SetCurSel( nIndex );
+ }
+ }
+
+ UpdateTexture();
+
+ if( nIndex != LB_ERR )
+ {
+ IEditorTexture *pTex = ( IEditorTexture* )m_TextureList.GetItemDataPtr( nIndex );
+ m_TextureList.AddMRU( pTex );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::NotifyGraphicsChanged( void )
+{
+ if( !IsWindow( m_hWnd ) )
+ {
+ return;
+ }
+
+ // load groups into group list
+ CString str;
+ int iCurSel = m_TextureGroupList.GetCurSel();
+ if (iCurSel != LB_ERR)
+ {
+ m_TextureGroupList.GetLBText(iCurSel, str);
+ }
+
+ m_TextureGroupList.SetRedraw(FALSE);
+ m_TextureGroupList.ResetContent();
+ m_TextureGroupList.AddString("All Textures");
+
+ int nCount = g_Textures.GroupsGetCount();
+ if (nCount > 1)
+ {
+ //
+ // Skip first group ("All Textures").
+ //
+ for (int i = 1; i < nCount; i++)
+ {
+ CTextureGroup *pGroup = g_Textures.GroupsGet(i);
+ if (pGroup->GetTextureFormat() == g_pGameConfig->GetTextureFormat())
+ {
+ const char *p = strstr(pGroup->GetName(), "textures\\");
+ if (p)
+ {
+ p += strlen("textures\\");
+ }
+ else
+ {
+ p = pGroup->GetName();
+ }
+
+ m_TextureGroupList.AddString(p);
+ }
+ }
+ }
+ m_TextureGroupList.SetRedraw(TRUE);
+
+ if (iCurSel == LB_ERR || m_TextureGroupList.SelectString(-1, str) == LB_ERR)
+ {
+ m_TextureGroupList.SetCurSel(0);
+ }
+
+ m_TextureGroupList.Invalidate();
+
+ char szName[MAX_PATH];
+ m_TextureGroupList.GetLBText(m_TextureGroupList.GetCurSel(), szName);
+ g_Textures.SetActiveGroup(szName);
+
+ //
+ // This is called when the loaded graphics list is changed,
+ // or on first init by this->Create().
+ //
+ m_TextureList.LoadGraphicList();
+ UpdateTexture();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnBrowse( void )
+{
+ // Set the material tool current.
+ SetMaterialPageTool( MATERIALPAGETOOL_MATERIAL );
+
+ CTextureBrowser *pBrowser = GetMainWnd()->pTextureBrowser;
+
+ int iSel = m_TextureList.GetCurSel();
+
+ if (iSel != LB_ERR)
+ {
+ IEditorTexture *pTex = (IEditorTexture *)m_TextureList.GetItemDataPtr(iSel);
+ if (pTex != NULL)
+ {
+ char sz[128];
+
+ pTex->GetShortName(sz);
+ pBrowser->SetInitialTexture(sz);
+ }
+ }
+
+ if (pBrowser->DoModal() == IDOK)
+ {
+ IEditorTexture *pTex = g_Textures.FindActiveTexture(pBrowser->m_cTextureWindow.szCurTexture);
+ if (pTex != NULL)
+ {
+ int iCount = m_TextureList.GetCount();
+ for (int i = 0; i < iCount; i++)
+ {
+ if (pTex == (IEditorTexture *)m_TextureList.GetItemDataPtr(i))
+ {
+ m_TextureList.SetCurSel(i);
+ UpdateTexture();
+ m_TextureList.AddMRU(pTex);
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnChangeTextureGroup( void )
+{
+ // Set the material tool current.
+ SetMaterialPageTool( MATERIALPAGETOOL_MATERIAL );
+
+ int iGroup = m_TextureGroupList.GetCurSel();
+
+ //
+ // Set the active texture group by name.
+ //
+ char szName[MAX_PATH];
+ m_TextureGroupList.GetLBText(iGroup, szName);
+ g_Textures.SetActiveGroup(szName);
+
+ //
+ // Refresh the texture list contents.
+ //
+ m_TextureList.LoadGraphicList();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnButtonApply( void )
+{
+ // Set the material tool current.
+ SetMaterialPageTool( MATERIALPAGETOOL_MATERIAL );
+
+ Apply(NULL, FACE_APPLY_ALL);
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+BOOL CFaceEditMaterialPage::OnSetActive( void )
+{
+ CMainFrame *pMainFrm = GetMainWnd();
+ if( !pMainFrm )
+ return FALSE;
+
+ ToolManager()->SetTool( TOOL_FACEEDIT_MATERIAL );
+
+ // Set the initial face edit tool state.
+ SetMaterialPageTool( MATERIALPAGETOOL_MATERIAL );
+
+ return CPropertyPage::OnSetActive();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Brings up the smoothing group dialog.
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::OnButtonSmoothingGroups( void )
+{
+ if( !m_FaceSmoothDlg.Create( IDD_SMOOTHING_GROUPS, this ) )
+ return;
+
+ m_FaceSmoothDlg.ShowWindow( SW_SHOW );
+
+ // Set the initial face edit tool state.
+ SetMaterialPageTool( MATERIALPAGETOOL_SMOOTHING_GROUP );
+
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::SetMaterialPageTool( unsigned short iMaterialTool )
+{
+ if ( m_iMaterialTool == MATERIALPAGETOOL_SMOOTHING_GROUP )
+ {
+ // Close the window.
+ m_FaceSmoothDlg.DestroyWindow();
+ }
+
+ // Set the new material tool.
+ m_iMaterialTool = iMaterialTool;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a new material (.vmt file) is detected.
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::NotifyNewMaterial( IEditorTexture *pTex )
+{
+ m_TextureList.LoadGraphicList();
+ UpdateTexture();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called to set the enabled state of the dialog controls
+//-----------------------------------------------------------------------------
+void CFaceEditMaterialPage::SetReadOnly( bool bIsReadOnly )
+{
+ BOOL State = ( bIsReadOnly ? FALSE : TRUE );
+
+ m_shiftX.EnableWindow( State );
+ m_shiftY.EnableWindow( State );
+ m_scaleX.EnableWindow( State );
+ m_scaleY.EnableWindow( State );
+ m_rotate.EnableWindow( State );
+ m_cLightmapScale.EnableWindow( State );
+ m_cHideMask.EnableWindow( State );
+ m_cExpand.EnableWindow( State );
+ m_TextureList.EnableWindow( State );
+ m_TextureGroupList.EnableWindow( State );
+
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_JUSTIFY_LEFT ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_JUSTIFY_RIGHT ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_JUSTIFY_FITTOFACE ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_JUSTIFY_TOP ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_JUSTIFY_BOTTOM ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_JUSTIFY_CENTER ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_TREAT_AS_ONE ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_ALIGN_WORLD ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_ALIGN_FACE ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_BROWSE ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_REPLACE ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, ID_FACEEDIT_APPLY ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, IDC_MODE ), State );
+ ::EnableWindow( ::GetDlgItem( m_hWnd, ID_BUTTON_SMOOTHING_GROUPS ), State );
+}