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.
1785 lines
47 KiB
1785 lines
47 KiB
//========= 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 ); |
|
}
|
|
|